Konwersja DateTime ze strefą czasową na czas lokalny w Pythonie

Jak przekonwertować obiekt datetime ze strefą czasową na równoważny datetime bez strefy czasowej dla lokalnej strefy czasowej?

Moja konkretna aplikacja używa Django (chociaż w rzeczywistości jest to ogólne Pytanie Pythona):

import iso8601

....

date_str="2010-10-30T17:21:12Z"

....

d = iso8601.parse_date(date_str)

foo = app.models.FooModel(the_date=d)
foo.save()

To powoduje, że Django wyrzuca błąd:

raise ValueError("MySQL backend does not support timezone-aware datetimes.")

Potrzebuję:

d = iso8601.parse_date(date_str)
local_d = SOME_FUNCTION(d)
foo = app.models.FooModel(the_date=local_d)

Co by było SOME_FUNCTION?

Author: kes, 2011-03-28

4 answers

Ogólnie rzecz biorąc, aby przekonwertować dowolny DateTime świadomy strefy czasowej na naiwny (lokalny) datetime, użyłbym modułu pytz i astimezone Aby przekonwertować na czas lokalny, i replace aby uczynić DateTime naiwnym:

In [76]: import pytz

In [77]: est=pytz.timezone('US/Eastern')

In [78]: d.astimezone(est)
Out[78]: datetime.datetime(2010, 10, 30, 13, 21, 12, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)

In [79]: d.astimezone(est).replace(tzinfo=None)
Out[79]: datetime.datetime(2010, 10, 30, 13, 21, 12)

Ale ponieważ twoja konkretna data wydaje się być w strefie czasowej UTC, możesz to zrobić zamiast tego:

In [65]: d
Out[65]: datetime.datetime(2010, 10, 30, 17, 21, 12, tzinfo=tzutc())

In [66]: import datetime

In [67]: import calendar

In [68]: datetime.datetime.fromtimestamp(calendar.timegm(d.timetuple()))
Out[68]: datetime.datetime(2010, 10, 30, 13, 21, 12)

Przy okazji, może lepiej będzie przechowywać dane jako naiwne daty UTC zamiast naiwnych lokalnych dat. W ten sposób Twoje dane są niezależne od czasu lokalnego i konwertujesz na czas lokalny lub inną strefę czasową tylko wtedy, gdy jest to konieczne. Jak najbardziej analogiczne do pracy w unicode, a kodowanie tylko wtedy, gdy jest to konieczne.

Więc jeśli zgadzasz się, że przechowywanie danych w UTC jest najlepszym sposobem, to wszystko, co musisz zrobić, to zdefiniować:

local_d = d.replace(tzinfo=None)
 53
Author: unutbu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-27 23:25:18

W najnowszych wersjach Django (co najmniej 1.4.1):

from django.utils.timezone import localtime

result = localtime(some_time_object)
 56
Author: user9876,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-17 16:09:04

Przenośne solidne rozwiązanie powinno korzystać z bazy danych tz. Aby uzyskać lokalną strefę czasową jako pytz tzinfo obiekt, use tzlocal module :

#!/usr/bin/env python
import iso8601
import tzlocal # $ pip install tzlocal

local_timezone = tzlocal.get_localzone()
aware_dt = iso8601.parse_date("2010-10-30T17:21:12Z") # some aware datetime object
naive_local_dt = aware_dt.astimezone(local_timezone).replace(tzinfo=None)

Uwaga: Może być kuszące użycie czegoś takiego jak:

#!/usr/bin/env python3
# ...
naive_local_dt = aware_dt.astimezone().replace(tzinfo=None)

Ale może się nie udać, jeśli lokalna Strefa czasowa ma zmienną UTC offset, ale python nie używa historycznej bazy danych strefy czasowej na danej platformie.

 2
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-23 07:55:38

Za pomocą python-dateutil możesz przetworzyć datę w formacie iso-8561 za pomocą dateutil.parsrser.parse(), która da ci aware datetime w strefie czasowej UTC/Zulu.

Za pomocą .astimezone() można przekonwertować go do świadomej datetime w innej strefie czasowej.

Użycie .replace(tzinfo=None) przekształci świadomy datetime w naiwny datetime.

from datetime import datetime
from dateutil import parser as datetime_parser
from dateutil.tz import tzutc,gettz

aware = datetime_parser.parse('2015-05-20T19:51:35.998931Z').astimezone(gettz("CET"))
naive = aware.replace(tzinfo=None)

Ogólnie najlepszym pomysłem jest konwersja wszystkich dat NA UTC i zapisywanie ich w ten sposób, a następnie konwersja z powrotem na lokalne w razie potrzeby. Używam aware.astimezone(tzutc()).replace(tzinfo=None), aby upewnić się, że jest w UTC i przekonwertować na naiwny.

 0
Author: ecerulm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-29 10:04:34