python 将时间增量添加到本地日期时间,DST转换期间出现意外行为

jaql4c8m  于 2022-12-28  发布在  Python
关注(0)|答案(1)|浏览(121)

我只是在创建跨越DST转变的日期时间时偶然发现了Python日期时间的这种令人惊讶的行为。
向本地datetime添加一个timedelta可能不会添加我们期望的时间量。

import datetime as dt
from zoneinfo import ZoneInfo

# Midnight
d0 = dt.datetime(2020, 3, 29, 0, 0, tzinfo=ZoneInfo("Europe/Paris"))
# datetime.datetime(2020, 3, 29, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Paris'))
d0.isoformat()
# '2020-03-29T00:00:00+01:00'

# Before DST shift
d1 = d0 + dt.timedelta(hours=2)
# datetime.datetime(2020, 3, 29, 2, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Paris'))
d1.isoformat()
# '2020-03-29T02:00:00+01:00'

# After DST shift
d2 = d0 + dt.timedelta(hours=3)
# datetime.datetime(2020, 3, 29, 3, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Paris'))
d2.isoformat()
# '2020-03-29T03:00:00+02:00'

# Convert to UCT
d1u = d1.astimezone(dt.timezone.utc)
# datetime.datetime(2020, 3, 29, 1, 0, tzinfo=datetime.timezone.utc)
d2u = d2.astimezone(dt.timezone.utc)
# datetime.datetime(2020, 3, 29, 1, 0, tzinfo=datetime.timezone.utc)

# Compute timedeltas
d2 - d1
# datetime.timedelta(seconds=3600)
d2u - d1u
# datetime.timedelta(0)

我同意d1和d2是相同的,但d2不应该是'2020-03- 29 T04:00:00+02:00'吗?

d3 = d0 + dt.timedelta(hours=4)
# datetime.datetime(2020, 3, 29, 4, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Paris'))

显然,当添加一个timedelta(例如3小时)到一个本地日期时间时,它的添加与时区无关,并且两个日期时间之间的delta(真实的/ UTC)不保证是那个timedelta(即,由于DST,它可能是2小时)。
理由是什么?有文件证明吗?

gywdnpxw

gywdnpxw1#

理由是:timedelta算法是墙时间算法。也就是说,它包括DST过渡小时数(或排除,取决于更改)。另请参阅P. Ganssle's blog post主题。
举例说明:

import datetime as dt
from zoneinfo import ZoneInfo

# Midnight
d0 = dt.datetime(2020, 3, 29, 0, 0, tzinfo=ZoneInfo("Europe/Paris"))

for h in range(1, 4):
    print(h)
    print(d0 + dt.timedelta(hours=h))
    print((d0 + dt.timedelta(hours=h)).astimezone(ZoneInfo("UTC")), end="\n\n")
1
2020-03-29 01:00:00+01:00
2020-03-29 00:00:00+00:00 # as expected, 1 hour added

2
2020-03-29 02:00:00+01:00 # that's a non-existing datetime...
2020-03-29 01:00:00+00:00 # looks normal

3
2020-03-29 03:00:00+02:00
2020-03-29 01:00:00+00:00 # oops, 3 hours timedelta is only 2 hours actually!

需要更多的混淆吗?使用朴素的日期时间。假设我的机器(Europe/Berlin)的tz与上面使用的tz具有相同的DST转换:
一个二个一个一个

相关问题