Python 3.9:使用标准库构造 DST 有效时间戳

Fab*_*Fab 5 timezone timedelta zoneinfo dst python-3.x


我想仅使用 Python 3.9 中的标准库构建 DST 有效时间戳,并希望在此版本中可以实现这一点。

在我的时区“欧洲/柏林”中,2020 年的 DST 交叉点为:
2020-03-29 02:00 时钟切换到 03:00(没有 2 点!)
2020-10-25 03:00时钟切换回 02:00(小时 2 存在两次!)

我的脚本产生以下输出:
MARCH
2020-03-29 01:59:00+01:00 CET plus 1 h: 2020-03-29 02: 59:00+01:00 CET
(应该是 03:59:00 CEST,因为没有小时 2!) 2020 年

10 月 25 日 10 月
02:00:00+02:00 CEST 加 1 小时:2020-10-25 03:00:00+01:00 CET
(看起来不错-编辑:应该是 02:00 CET!!!)

下面提供了示例代码。Windows 用户可能需要“pip install tzdata”才能使其工作。

任何建议将不胜感激!

'''
Should work out of the box with Python 3.9

Got a fallback import statement.

BACKPORT (3.6+)
pip install backports.zoneinfo

WINDOWS (TM) needs:
pip install tzdata
'''

from datetime import datetime, timedelta
from time import tzname
try:
    from zoneinfo import ZoneInfo
except ImportError:
    from backports import zoneinfo
    ZoneInfo = zoneinfo.ZoneInfo


tz = ZoneInfo("Europe/Berlin")
hour = timedelta(hours=1)

print("MARCH")
dt_01 = datetime(2020, 3, 29, 1, 59, tzinfo=tz)

dt_02 = dt_01 + hour
print(f"{dt_01} {dt_01.tzname()} plus 1 h: {dt_02} {dt_02.tzname()}")


print("\nOCTOBER")
dt_01 = datetime(2020, 10, 25, 2, 0, tzinfo=tz)
dt_02 = dt_01 + hour
print(f"{dt_01} {dt_01.tzname()} plus 1 h: {dt_02} {dt_02.tzname()}")
Run Code Online (Sandbox Code Playgroud)

Pau*_*aul 6

虽然有悖常理,但这也在意料之中。有关日期时间算术工作原理的更多详细信息,请参阅此博客文章。这样做的原因是,将 a 添加timedelta到 adatetime应该被视为“将日历/时钟提前 X 量”,而不是“在经过这段时间后日历/时钟会说什么”。请注意,第一个问题可能会导致出现在本地时区中甚至不存在的时间!

\n

如果您想“什么表示经过datetime此表示的时间后将是什么时间?” timedelta(看起来你确实这样做了),你应该做一些相当于转换为 UTC 并返回的事情,如下所示:

\n
from datetime import datetime, timedelta, timezone\n\ndef absolute_add(dt: datetime, td: timedelta) -> datetime:\n    utc_in = dt.astimezone(timezone.utc)  # Convert input to UTC\n    utc_out = utc_in + td  # Do addition in UTC\n    civil_out = utc_out.astimezone(dt.tzinfo)  # Back to original tzinfo\n    return civil_out\n
Run Code Online (Sandbox Code Playgroud)\n

我相信您可以创建一个timedelta重写的子类__add__来为您执行此操作(如果可以的话,我想将类似的内容引入标准库)。

\n

请注意,如果dt.tzinfoNone,这将使用您的系统本地时区来确定如何进行绝对加法,并且它将返回一个感知时区。运行这个America/New_York

\n
>>> absolute_add(datetime(2020, 11, 1, 1), timedelta(hours=1))\ndatetime.datetime(2020, 11, 1, 1, 0, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=68400), \'EST\'))\n
Run Code Online (Sandbox Code Playgroud)\n

如果您希望对 na\xc3\xafve 日期时间进行民用加法,并对感知日期时间进行绝对加法,您可以在函数中检查它是否是 na\xc3\xafve:

\n
def absolute_add_nolocal(dt: datetime, td: timedelta) -> datetime:\n    if dt.tzinfo is None:\n        return dt + td\n    return absolute_add(dt, td)\n
Run Code Online (Sandbox Code Playgroud)\n

另外,要明确的是,这与 无关zoneinfo。这一直是 Python 中日期时间的语义,我们无法以向后兼容的方式更改它。pytz工作方式确实有点不同,因为添加pytz-aware datetimes 会做错误的事情,并且需要normalize在算术发生后执行一个步骤,并且pytz作者决定normalize应该使用绝对时间语义。

\n

absolute_add也适用于pytzdateutil,因为它使用适用于所有时区库的操作。

\n