为什么我对萨摩亚的 pytz 和 dateutil 得到不同的结果?

Why do I get different results for pytz and dateutil for Samoa?

我原以为下面两个会给出相同的结果,但他们没有。为什么会这样?

版本:

pytz==2018.5
python-dateutil==2.7.3

示例 1:pytz

import datetime
import pytz

tz = pytz.timezone('Pacific/Apia')
today_utc = datetime.datetime(2011, 12, 30, 9, 59,
                              tzinfo=datetime.timezone.utc)
today_tz = today_utc.astimezone(tz)
print(today_tz.isoformat())

打印 2011-12-29T23:59:00-10:00(正确)

示例 2:dateutil

import datetime
import dateutil.tz

tz = dateutil.tz.gettz('Pacific/Apia')
today_utc = datetime.datetime(2011, 12, 30, 9, 59,
                              tzinfo=datetime.timezone.utc)
today_tz = today_utc.astimezone(tz)
print(today_tz.isoformat())

打印 2011-12-29T23:59:00+14:00(这是错误的)

您发现了 bug in dateutil, which I have now reported and fixed

该错误是由于 "wall time" 中的转换在 dateutil 中的计算方式问题引起的,当时时区的基准偏移量发生变化 [=40] 时做出了一些不成立的假设=]夏令时期间。稍微扩展一下您的示例:

from datetime import datetime, timedelta
from dateutil import tz
import pytz

APIA = tz.gettz('Pacific/Apia')
APIA_p = pytz.timezone('Pacific/Apia')
dt0 = datetime.fromisoformat('2011-12-29T20:00-10:00')

for i in range(5):
    dt = (dt0 + timedelta(hours=i))
    dt_d = dt.astimezone(APIA)
    dt_p = dt.astimezone(APIA_p)
    print(f'{dt_d.isoformat()}, {dt_p.isoformat()}')

## Result:
# 2011-12-29T20:00:00-10:00, 2011-12-29T20:00:00-10:00
# 2011-12-29T21:00:00-10:00, 2011-12-29T21:00:00-10:00
# 2011-12-29T22:00:00-10:00, 2011-12-29T22:00:00-10:00
# 2011-12-29T23:00:00+14:00, 2011-12-29T23:00:00-10:00
# 2011-12-31T00:00:00+14:00, 2011-12-31T00:00:00+14:00

您可以看到 dateutil 总是正确计算日期和时间,但是当 isoformat 调用 utcoffset 时,偏移量会提前 1 小时发生变化。这是因为 astimezone 调用了 tzinfo.fromutc under the hood, while isoformat calls utcoffsetdateutil 以 UTC 和本地时间存储转换时间,UTC 时间用于 fromutc,本地时间用于 utcoffsetdsttzname.此错误涉及在 DST->DST 转换期间计算转换的 "wall time" 时对 DST 的过度补偿(这种情况极为罕见),这就是它不影响 astimezone.[=30= 的原因]

底线 - 您正确使用了 pytzdateutil,此错误将在下一个版本中修复。

注意:这个答案是在我找到错误的原因并修复后编辑的。