提问人:Paul 提问时间:8/8/2023 最后编辑:FObersteinerPaul 更新时间:8/8/2023 访问量:165
Python pytz、zoneinfo 和夏令时
Python pytz, zoneinfo and daylight savings time
问:
我目前正在尝试将代码库从使用 Python 库迁移到使用 Python 库。与如何处理夏令时转换相比,我遇到了如何处理夏令时转换的问题。pytz
zoneinfo
zoneinfo
pytz
假设我有一个幼稚的对象:datetime
>>> import datetime
>>> start = datetime.datetime(2021, 10, 30, 23, 0)
用于使此对象具有时区感知的方法是使用 ,如下所示:pytz
localize
>>> local_timezone = pytz.timezone('Europe/Copenhagen')
>>> start_cet = local_timezone.localize(start)
>>> start_cet
datetime.datetime(2021, 10, 30, 23, 0, tzinfo=<DstTzInfo 'Europe/Copenhagen' CEST+2:00:00 DST>)
请注意,这非常接近 CEST 2021 的结束。如果我在这个时间上加上 5 个小时,我会得到以下结果:start_cet
>>> end = start_cet + datetime.timedelta(hours=5)
>>> end
datetime.datetime(2021, 10, 31, 4, 0, tzinfo=<DstTzInfo 'Europe/Copenhagen' CEST+2:00:00 DST>)
现在,实际上,增加 5 小时实际上应该让我们在 2021 年 10 月 31 日 3:00 降落,因为离开 DST 会让我们回到一个小时,从 CEST 转移到 CET。我们可以通过应用我们的时区来获得正确的值:start_cet
astimezone
pytz
>>> end.astimezone(local_timezone)
datetime.datetime(2021, 10, 31, 3, 0, tzinfo=<DstTzInfo 'Europe/Copenhagen' CET+1:00:00 STD>)
问:使用 zoneinfo
时,在 DST 中移动时查找实际时间的等效方法是什么?
这是我尝试过的。 没有方法,而是建议我们简单地使用:zoneinfo
localize
replace
>>> local_timezone = zoneinfo.ZoneInfo('Europe/Copenhagen')
>>> start_cet = start.replace(tzinfo=local_timezone)
>>> start_cet
datetime.datetime(2021, 10, 30, 23, 0, tzinfo=backports.zoneinfo.ZoneInfo(key='Europe/Copenhagen'))
>>> start_cet.utcoffset()
datetime.timedelta(seconds=7200)
zoneinfo
这样做的好处是,添加 a 将成功地将我们过渡到正确的时区(从结果中可以看出:pytz
timedelta
utcoffset
>>> end = start_cet + datetime.timedelta(hours=5)
>>> end
datetime.datetime(2021, 10, 31, 4, 0, tzinfo=backports.zoneinfo.ZoneInfo(key='Europe/Copenhagen'))
>>> end.utcoffset()
datetime.timedelta(seconds=3600)
尽管如此,对于时区,似乎无法获得将 5 小时添加到 上的实际时间。如上所示,使用 I 可以使用 ,但使用 ,这不会改变任何内容:zoneinfo
start_cet
pytz
astimezone
zoneinfo
>>> end.astimezone(tz=local_timezone)
datetime.datetime(2021, 10, 31, 4, 0, tzinfo=backports.zoneinfo.ZoneInfo(key='Europe/Copenhagen'))
答:
除了 @deceze 的评论(CET 指定 UTC 偏移量,而不是时区)之外,请注意 Python 中的时间增量算术是墙时间算术。
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
eu_berlin_tz = ZoneInfo("Europe/Berlin")
start = datetime(2021, 10, 30, 23, 0, tzinfo=eu_berlin_tz)
dur = timedelta(hours=5)
print(start)
# 2021-10-30 23:00:00+02:00 => CEST
print(start + dur)
# 2021-10-31 04:00:00+01:00 => CET
2021-10-30T23:00:00+02:00 至 2021-10-31T04:00:00+01:00 是挂钟上的 5 小时,尽管由于 DST 变为非活动状态,实际上已经过去了 6 小时。
如果以 UTC 为单位添加持续时间,然后转换回指定的时区,则结果更符合您的预期 IIUC:
def aware_add(dt, duration):
return (dt.astimezone(ZoneInfo("UTC")) + duration).astimezone(dt.tzinfo)
print(aware_add(start, dur))
# 2021-10-31 03:00:00+01:00
2021-10-30T23:00:00+02:00 至 2021-10-31T03:00:00+01:00 在挂钟上是 4 小时,但实际上是 5 小时。
评论
Europe/Berlin