Python: astimezone(None) дает осведомленность о дате и времени, не зная о летнем времени

astimezone(None)это удобный способ локализовать объект datetime на местное время, то есть настройки вашей ОС ( документы, пример). Однако я заметил, что есть нюанс. Поскольку я нахожусь на CET/CEST, и у нас только что было изменение DST, я играл с осведомленными datetime и timedelta.1

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

t_DSTactive = datetime(2020,10,23, tzinfo=ZoneInfo('Europe/Berlin'))
t_DSTinactive = datetime(2020,10,26, tzinfo=ZoneInfo('Europe/Berlin'))
print(t_DSTinactive - t_DSTactive)
# 3 days, 0:00:00

Как и ожидалось, timedeltaпоказывает разницу во времени2 на стене, которая составляет 3 дня между этими датами. Из-за перехода с активного летнего времени на неактивное летнее время продолжительность по UTC составляет 3 дня и 1 час:

t_DSTactive = t_DSTactive.astimezone(timezone.utc)
t_DSTinactive = t_DSTinactive.astimezone(timezone.utc)
print(t_DSTinactive - t_DSTactive)
# 3 days, 1:00:00

С помощью asttimezone(None) для локализации вроде все нормально (UTC+2 → UTC+1):

DSTinactive = datetime(2020,10,26).astimezone(None)
print(DSTactive, DSTinactive)
# 2020-10-23 00:00:00+02:00 2020-10-26 00:00:00+01:00

...но timedelta включает +1 час после перехода на летнее время:

print(DSTinactive - DSTactive)
# 3 days, 1:00:00

Что здесь происходит?


1 ответ

Решение

Глядя на repr() объектов datetime, полученных с помощью .astimezone(None), мы видим, что tzinfo атрибут "только" timedelta в обоих случаях:

       print(repr(DSTactive))
print(repr(DSTinactive))
# datetime.datetime(2020, 10, 23, 0, 0, tzinfo=datetime.timezone(datetime.timedelta(seconds=7200), 'Mitteleuropäische Sommerzeit'))
# datetime.datetime(2020, 10, 26, 0, 0, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'Mitteleuropäische Zeit'))

Другими словами, фиксированное смещение UTC, а не часовой пояс в географическом смысле (включая предписанные изменения летнего времени). Фиксированное смещение, конечно, не учитывает изменения летнего времени. С учетом смещений расчет правильно дает 3 дня с разницей в 1 час.

Предыстория: в src datetime мы видим, что astimezone(None) звонки _local_timezone()чтобы получить tzinfo, который возвращает это как фиксированное смещение.

Хотя удобно, astimezone(None)здесь может привести к неожиданным результатам. Способ обойти эту проблему, конечно, заключается в локализации datetime в местном часовом поясе. Здесь tzlocal может оказать большую помощь, особенно в Windows, которая особенно неохотно сообщает имя часового пояса.

Другие вопросы по тегам