Преобразование времени и времени с учетом часовых поясов в локальное время в Python
Как вы преобразовываете объект datetime, соответствующий времени часового пояса, в эквивалентное время и время, не соответствующее часовому поясу, для локального часового пояса?
В моем конкретном приложении используется Django (хотя на самом деле это общий вопрос Python):
import iso8601
....
date_str="2010-10-30T17:21:12Z"
....
d = iso8601.parse_date(date_str)
foo = app.models.FooModel(the_date=d)
foo.save()
Это приводит к ошибке Django:
raise ValueError("MySQL backend does not support timezone-aware datetimes.")
Что мне нужно:
d = iso8601.parse_date(date_str)
local_d = SOME_FUNCTION(d)
foo = app.models.FooModel(the_date=local_d)
Что будет SOME_FUNCTION?
Ответы
Ответ 1
В общем случае, чтобы преобразовать произвольное время и время, зависящие от часового пояса, к наивному (локальному) времени и времени, я бы использовал модуль pytz
и astimezone
для преобразования в локальное время и replace
, чтобы сделать datetime наивным
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)
Но так как ваше конкретное время, похоже, находится в часовом поясе UTC, вы можете сделать это вместо этого:
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)
Кстати, вам может быть лучше хранить данные как наивные UTC datetimes вместо наивных локальных дат. Таким образом, ваши данные являются агностиками локального времени, и при необходимости вы только конвертируете их в локальный или любой другой часовой пояс. Похоже на то, чтобы работать в уникоде как можно больше и кодировать только при необходимости.
Итак, если вы согласны с тем, что сохранение времени в наивном UTC - лучший способ, тогда вам нужно всего лишь определить:
local_d = d.replace(tzinfo=None)
Ответ 2
В последних версиях Django (не менее 1.4.1):
from django.utils.timezone import localtime
result = localtime(some_time_object)
Ответ 3
Портативное надежное решение должно использовать базу данных tz. Чтобы получить локальный часовой пояс как pytz
tzinfo
объект, использовать tzlocal
модуль:
#!/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)
Примечание: может возникнуть соблазн использовать что-то вроде:
#!/usr/bin/env python3
# ...
naive_local_dt = aware_dt.astimezone().replace(tzinfo=None)
но он может выйти из строя, если локальный часовой пояс имеет переменную utc offset, но python не использует базу данных хронологического времени на данной платформе.
Ответ 4
Используя python-dateutil
, вы можете проанализировать дату в формате iso-8561 с помощью dateutil.parsrser.parse()
, которая даст вам знать datetime
в часовом поясе UTC/Zulu.
Используя .astimezone()
, вы можете преобразовать его в известное время в другом часовом поясе.
Использование .replace(tzinfo=None)
преобразует значащее время datetime в наивное 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)
В общем, лучшая идея - конвертировать все даты в UTC и хранить их таким образом и преобразовывать их обратно в локальные, если это необходимо. Я использую aware.astimezone(tzutc()).replace(tzinfo=None)
, чтобы убедиться, что он находится в UTC и конвертируется в наивный.