在创建datetimes accross DST shift时,我偶然发现Python datetimes出现了这种令人惊讶的行为.

向本地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-29T04:00:00+02:00’吗?

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

显然,当向本地日期时间添加时间增量(例如3小时)时,无论时区如何,都会添加时间增量,并且两个日期时间(实时/UTC)之间的增量不能保证为时间增量(即,由于DST,它可能为2小时).这有点trap .

理由是什么?这有文件记载吗?

推荐答案

其基本原理是: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
2020-03-29 01:00:00+00:00 # ok that looks normal too

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!

需要更多的困惑吗?使用原始日期时间.鉴于我的机器(欧洲/柏林)的tz与上面使用的tz具有相同的DST转换:

d0 = dt.datetime(2020, 3, 29, 0, 0)

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       # 1 hour as expected
2020-03-29 00:00:00+00:00 # we're on UTC+1

2
2020-03-29 02:00:00       # ok 2 hours...
2020-03-29 00:00:00+00:00 # wait, what?!

3
2020-03-29 03:00:00
2020-03-29 01:00:00+00:00

Python相关问答推荐

如何在Python中将returns.context. DeliverresContext与Deliverc函数一起使用?

Python中的嵌套Ruby哈希

通过Selenium从页面获取所有H2元素

为什么符号没有按顺序添加?

如何从在虚拟Python环境中运行的脚本中运行需要宿主Python环境的Shell脚本?

数据抓取失败:寻求帮助

在Mac上安装ipython

Asyncio:如何从子进程中读取stdout?

Pandas Loc Select 到NaN和值列表

在单个对象中解析多个Python数据帧

如何在Python中获取`Genericums`超级类型?

如何从需要点击/切换的网页中提取表格?

基于Scipy插值法的三次样条系数

GPT python SDK引入了大量开销/错误超时

numpy数组和数组标量之间的不同行为

在第一次调用时使用不同行为的re. sub的最佳方式

按条件计算将记录拆分成两条记录

根据过滤后的牛郎星图表中的数据计算新系列

具有不同坐标的tkinter canvs.cocords()和canvs.moveto()

在不降低分辨率的情况下绘制一组数据点的最外轮廓