Python pytz、zoneinfo 和夏令时

问题描述 投票:0回答:1

我目前正在尝试将代码库从使用

pytz
迁移到使用 Python 的
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>)

请注意,

start_cet
已经非常接近 2021 年中欧夏令时结束了。如果我在这个时间上加上 5 个小时,我会得到以下结果:

>>> end = start_cet + datetime.timedelta(hours=5)
>>> end
datetime.datetime(2021, 10, 31, 4, 0, tzinfo=<DstTzInfo 'Europe/Copenhagen' CEST+2:00:00 DST>)

现在,实际上,在

start_cet
上增加 5 个小时实际上应该会让我们到达 2021 年 10 月 31 日的 3:00,因为离开 DST 会让我们倒退一小时,从 CEST 转到 CET。我们可以通过将
astimezone
与我们的
pytz
时区一起应用来获得正确的值:

>>> end.astimezone(local_timezone)
datetime.datetime(2021, 10, 31, 3, 0, tzinfo=<DstTzInfo 'Europe/Copenhagen' CET+1:00:00 STD>)

问题:使用

zoneinfo
时查找夏令时实际时间的等效方法是什么?

这是我尝试过的。

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
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)

尽管如此,对于

zoneinfo
时区,似乎无法获得在 start_cet 上添加 5 小时后的
实际时间
。如上所示,使用
pytz
我可以使用
astimezone
,但是使用
zoneinfo
,这不会改变任何东西:

>>> end.astimezone(tz=local_timezone)
datetime.datetime(2021, 10, 31, 4, 0, tzinfo=backports.zoneinfo.ZoneInfo(key='Europe/Copenhagen'))

python datetime dst pytz zoneinfo
1个回答
0
投票

除了@deceze的评论(CET是UTC偏移量,而不是时区)之外,请注意Python中的timedelta算术是wall time算术

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 小时。

© www.soinside.com 2019 - 2024. All rights reserved.