如何强制zoneinfo使用Python tzdata库,而不是系统tzdata,或者,为什么在不同平台上得到的UTC偏移量不同?
我遇到了一个问题,可能是我这边的用户错误、也可能是Python的 zoneinfo与 tzdata模块在不同平台上的行为不一致,或者IANA数据库在各个平台之间存在差异。
我发现,是否在同一个IANA数据库下,取决于所处的平台,zoneinfo库进行utcoffset计算的结果会不同,即使使用的IANA数据库相当相似。这些日期都在1970年之前,包括应与UTC有偏移的日期,以及基于LMT的日期。我的测试输出的完整结果如下。
那么我的核心问题是:这是怎么回事?我如何强制zoneinfo使用已安装(且在通过uv管理的虚拟环境中锁定版本)的Python tzdata库,而不是系统tzdata?
以下是我部分测试的结果、所使用的平台,以及已安装的tzdata库版本。
Ubuntu noble,tzdata 2026a-0ubuntu0.24.04。
Python 3.12.3 (main, Mar 23 2026, 19:04:32) [GCC 13.3.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 9.5.0 -- An enhanced Interactive Python. Type '?' for help.
Tip: You can use `files = !ls *.png`
In [1]: import zoneinfo
In [2]: from datetime import datetime
In [3]: zoneinfo.ZoneInfo("Europe/Amsterdam").utcoffset(datetime(1923, 12, 1, 13
⋮ , 43, 12)).total_seconds()
Out[3]: 1172.0
In [4]: zoneinfo.ZoneInfo("Europe/Amsterdam").utcoffset(datetime(1023, 12, 1, 13
⋮ , 43, 12)).total_seconds()
Out[4]: 1172.0
In [5]: zoneinfo.TZPATH
Out[5]:
('/usr/share/zoneinfo',
'/usr/lib/zoneinfo',
'/usr/share/lib/zoneinfo',
'/etc/zoneinfo')
MacOS Sequoia 15.7.2,zoneinfo版本2025c
Python 3.12.12 (main, Mar 23 2026, 19:04:32) [GCC 13.3.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 9.5.0 -- An enhanced Interactive Python. Type '?' for help.
Tip: You can use `files = !ls *.png`
In [13]: import zoneinfo
In [14]: from datetime import datetime
In [15]: zoneinfo.ZoneInfo("Europe/Amsterdam").utcoffset(datetime(1923,12,1,13,4
⋮ 3,12)).total_seconds()
Out[15]: 0.0
In [16]: zoneinfo.ZoneInfo("Europe/Amsterdam").utcoffset(datetime(1023,12,1,13,4
⋮ 3,12)).total_seconds()
Out[16]: 1050.0
In [17]: zoneinfo.TZPATH
Out[17]:
('/usr/share/zoneinfo',
'/usr/lib/zoneinfo',
'/usr/share/lib/zoneinfo',
'/etc/zoneinfo')
raspbian bookworm,tzdata 2025b-0+deb12u2
Python 3.11.2 (main, Apr 28 2025, 14:11:48) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import zoneinfo
>>> from datetime import datetime
>>> zoneinfo.ZoneInfo("Europe/Amsterdam").utcoffset(datetime(1923, 12, 1,13,43,12)).total_seconds()
1172.0
>>> zoneinfo.ZoneInfo("Europe/Amsterdam").utcoffset(datetime(1023, 12, 1,13,43,12)).total_seconds()
1172.0
>>> zoneinfo.TZPATH
('/usr/share/zoneinfo', '/usr/lib/zoneinfo', '/usr/share/lib/zoneinfo', '/etc/zoneinfo')
>>
Windows 10,PowerShell,使用uv安装的Python,tzdata版本为2026b
Python 3.12.1 (tags/v3.12.1:2305ca5, Dec 7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import zoneinfo
>>> import tzdata
>>> from datetime import datetime
>>> zoneinfo.ZoneInfo("Europe/Amsterdam").utcoffset(datetime(1923, 12, 1,13,43,12)).total_seconds()
0.0
>>> zoneinfo.ZoneInfo("Europe/Amsterdam").utcoffset(datetime(1023, 12, 1,13,43,12)).total_seconds()
1050.0
>>> zoneinfo.TZPATH
()
>>> tzdata.IANA_VERSION
'2026b'
所以这里的异常点是MacOS,其tzdata为 2025c,以及Windows使用uv安装的tzdata版本为2026b。相反,raspbian的 tzdata为 2025b,Ubuntu的 tzdata为 2026a,二者都给出相同的结果。
我浏览了 IANA tz邮件列表的公告,并没有发现这些版本之间对Europe/Amsterdam文件的变更提及。
到目前为止,我完全被搞糊涂了,也不知道为什么在这些版本之间zoneinfo文件没有(我能找到的)变更的情况下,在不同平台上会得到不同的结果。我可能误解了zoneinfo和 tzdata在不同平台上的工作方式。
(注意,我也尝试通过将datetime调用显式设为 “Etc/UTC” 来让时区感知到这一点,但问题仍然存在。)
对此的任何帮助都将不胜感激!
解决方案
据chatty的说法
即使tzdata文件没有变化,zoneinfo仍可能在不同的操作系统上产生不同的结果,因为每个平台可能使用不同的时区数据源(系统tzdata与 PyPI tzdata包),并且对于像公元1023年这样的极早日期,由于IANA数据库中的历史规则不完整或未定义,处理方式可能不同;再加上Python的 zoneinfo在不同操作系统上的回退行为,即使底层数据看起来未变,也会导致偏移不一致。