Как мне узнать время UTC “полночь” для данного часового пояса?
Вопрос
Лучшее, что я могу придумать на данный момент, - это это чудовище:
>>> datetime.utcnow() \
... .replace(tzinfo=pytz.UTC) \
... .astimezone(pytz.timezone("Australia/Melbourne")) \
... .replace(hour=0,minute=0,second=0,microsecond=0) \
... .astimezone(pytz.UTC) \
... .replace(tzinfo=None)
datetime.datetime(2008, 12, 16, 13, 0)
Т.е. на английском языке получите текущее время (в UTC), преобразуйте его в какой-нибудь другой часовой пояс, установите время на полночь, затем преобразуйте обратно в UTC.
Я не просто использую now() или localtime(), поскольку это использовало бы часовой пояс сервера, а не пользователя.
Я не могу избавиться от чувства, что я чего-то не понимаю, есть идеи?
Решение
Я думаю, вы можете сократить несколько вызовов методов, если сделаете это следующим образом:
>>> from datetime import datetime
>>> datetime.now(pytz.timezone("Australia/Melbourne")) \
.replace(hour=0, minute=0, second=0, microsecond=0) \
.astimezone(pytz.utc)
НО… в вашем коде есть проблема посерьезнее эстетики:это даст неверный результат в день перехода на летнее время или с него.
Причина этого заключается в том, что ни конструкторы datetime, ни replace()
примите во внимание изменения летнего времени.
Например:
>>> now = datetime(2012, 4, 1, 5, 0, 0, 0, tzinfo=pytz.timezone("Australia/Melbourne"))
>>> print now
2012-04-01 05:00:00+10:00
>>> print now.replace(hour=0)
2012-04-01 00:00:00+10:00 # wrong! midnight was at 2012-04-01 00:00:00+11:00
>>> print datetime(2012, 3, 1, 0, 0, 0, 0, tzinfo=tz)
2012-03-01 00:00:00+10:00 # wrong again!
Однако документация для tz.localize()
состояния:
Этот метод следует использовать для построения localtimes, а не вместо передачи аргумента tzinfo конструктору datetime.
Таким образом, ваша проблема решается следующим образом:
>>> import pytz
>>> from datetime import datetime, date, time
>>> tz = pytz.timezone("Australia/Melbourne")
>>> the_date = date(2012, 4, 1) # use date.today() here
>>> midnight_without_tzinfo = datetime.combine(the_date, time())
>>> print midnight_without_tzinfo
2012-04-01 00:00:00
>>> midnight_with_tzinfo = tz.localize(midnight_without_tzinfo)
>>> print midnight_with_tzinfo
2012-04-01 00:00:00+11:00
>>> print midnight_with_tzinfo.astimezone(pytz.utc)
2012-03-31 13:00:00+00:00
Однако никаких гарантий для дат до 1582 года нет.
Другие советы
ответ @hop's является неправильным в день перехода с летнего времени (DST), например, 1 апреля 2012 года.Чтобы это исправить tz.localize()
может быть использован:
tz = pytz.timezone("Australia/Melbourne")
today = datetime.now(tz).date()
midnight = tz.localize(datetime.combine(today, time(0, 0)), is_dst=None)
utc_dt = midnight.astimezone(pytz.utc)
То же самое с комментариями:
#!/usr/bin/env python
from datetime import datetime, time
import pytz # pip instal pytz
tz = pytz.timezone("Australia/Melbourne") # choose timezone
# 1. get correct date for the midnight using given timezone.
today = datetime.now(tz).date()
# 2. get midnight in the correct timezone (taking into account DST)
#NOTE: tzinfo=None and tz.localize()
# assert that there is no dst transition at midnight (`is_dst=None`)
midnight = tz.localize(datetime.combine(today, time(0, 0)), is_dst=None)
# 3. convert to UTC (no need to call `utc.normalize()` due to UTC has no
# DST transitions)
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
print midnight.astimezone(pytz.utc).strftime(fmt)
Установка переменной среды TZ изменяет, с каким часовым поясом работают функции даты и времени Python.
>>> time.gmtime()
(2008, 12, 17, 1, 16, 46, 2, 352, 0)
>>> time.localtime()
(2008, 12, 16, 20, 16, 47, 1, 351, 0)
>>> os.environ['TZ']='Australia/Melbourne'
>>> time.localtime()
(2008, 12, 17, 12, 16, 53, 2, 352, 1)
Каждый часовой пояс имеет свой номер, например, US / Central = -6.Это определяется как смещение в часах от UTC.Поскольку 0000 - это полночь, вы можете просто использовать это смещение для определения времени в любом часовом поясе, когда наступает полночь по UTC.Чтобы получить доступ к этому, я полагаю, вы можете использовать
time.timezone
Согласно Документы по Python, time.часовой пояс фактически дает отрицательное значение этого числа:
время.часовой пояс
Смещение местного часового пояса (не DST) в секундах к западу от UTC (отрицательное в большинстве стран Западной Европы, положительное в США, нулевое в Великобритании).
Таким образом, вы бы просто использовали это число для времени в часах, если оно положительное (т. Е. если в Чикаго полночь (значение часового пояса + 6), то это 6000 = 6 утра по Гринвичу).
Если число отрицательное, вычтите из 24.Например, Берлин дал бы значение -1, так что 24: 1 => 23:00 = 11 вечера.
Это более просто с dateutil.tz , чем с pytz:
>>>import datetime
>>>import dateutil.tz
>>>midnight=(datetime.datetime
.now(dateutil.tz.gettz('Australia/Melbourne'))
.replace(hour=0, minute=0, second=0, microsecond=0)
.astimezone(dateutil.tz.tzutc()))
>>>print(midnight)
2019-04-26 14:00:00+00:00
В документация tzinfo рекомендует dateutil.tz начиная с Python 3.6.Объекты tzinfo из dateutil.tz не имеют проблем с аномалиями, такими как летнее время, не требуя функциональности локализации pytz.Используя пример из user3850:
>>> now = (datetime.datetime(2012, 4, 1, 5,
... tzinfo = dateutil.tz.gettz('Australia/Melbourne')))
>>> print(now.replace(hour = 0).astimezone(dateutil.tz.tzutc()))
2012-03-31 13:00:00+00:00