为啥 pytz 在跨越 TZ 和 DST 边界而不是 TZ 名称时正确调整时间和偏移量?

Posted

技术标签:

【中文标题】为啥 pytz 在跨越 TZ 和 DST 边界而不是 TZ 名称时正确调整时间和偏移量?【英文标题】:Why does pytz correctly adjust time & offset when crossing TZ & DST boundaries but not the TZ name?为什么 pytz 在跨越 TZ 和 DST 边界而不是 TZ 名称时正确调整时间和偏移量? 【发布时间】:2014-04-17 05:46:06 【问题描述】:

我在这里查看了几个与pytz 相关的问题,但似乎没有一个能够解决我所看到的确切问题。

在pytz documentation之后,这是一个循环打印多个时区的当前时间,包括时区偏移量、时区名称,以及datetime对象是否认为它是DST。

nowDT = datetime.datetime.now()
chicagoTz = pytz.timezone('America/Chicago')
chicagoDT = chicagoTz.normalize(chicagoTz.localize(nowDT))
sys.stdout.write( "%-10s %-35s %s\n" % ('Chicago',
                                        chicagoDT.strftime("%Y/%m/%d %H:%M:%S %Z %z"),
                                        chicagoDT.dst()) )

tzTups = [('New York', 'America/New_York'),
          ('London',   'Europe/London'),
          ('Sydney',   'Australia/Sydney')]

for tzTup in tzTups:
    tz = pytz.timezone(tzTup[1])
    locDT = tz.normalize(chicagoDT.astimezone(tz))
    sys.stdout.write( "%-10s %-35s %s\n" % (tzTup[0],
                                            locDT.strftime("%Y/%m/%d %H:%M:%S %Z %z"),
                                            locDT.dst()) )

这是输出:

Chicago    2014/03/12 14:34:53 CDT -0500       1:00:00
New York   2014/03/12 15:34:53 EDT -0400       1:00:00
London     2014/03/12 19:34:53 GMT +0000       0:00:00
Sydney     2014/03/13 06:34:53 EST +1100       1:00:00

检查timeanddate.com,我们看到所有这些信息都是正确的,包括悉尼时间、偏移量和1:00:00,表明datetime对象认为DST当前在悉尼生效.

唯一的问题是悉尼时间标记为EST 而不是EDT。事实上,我无法让 Python 在 EDT 中声明 Sydney,即使它知道 DST 偏移量:

tz = pytz.timezone('Australia/Sydney')
for i in range(1,13):
    locDT = tz.normalize(tz.localize(datetime.datetime(2013, i, 15)))
    sys.stdout.write("%02d %s %s\n" % (i, locDT.dst(), locDT.tzname()))

输出:

01 1:00:00 EST
02 1:00:00 EST
03 1:00:00 EST
04 0:00:00 EST
05 0:00:00 EST
06 0:00:00 EST
07 0:00:00 EST
08 0:00:00 EST
09 0:00:00 EST
10 1:00:00 EST
11 1:00:00 EST
12 1:00:00 EST

我做错了吗? /usr/share/zoneinfo 在我的系统上是否已过期?这是在pytz 的最新版本或我可能没有的 Olson DB 中更正的已知问题吗? (我说它正在使用OLSON_VERSION = '2010b'。)

【问题讨论】:

使用tz.localize(naive_dt, is_dst=None),除非您希望默认为is_dst=False 用于不明确或不存在的当地时间。 谢谢。这就是 normalize 周围的含义 - 为相关日期/时间适当设置 DST。 如果时间不明确;它是模棱两可的:tz.normalize() 无法修复它(如果可以,那么 .localize() 会自己做正确的事情)。 @J.F.Sebastian 哦,我明白你在说什么。感谢您的提示。 【参考方案1】:

IANA 维护 Olson 数据库。 IANA 的 tz mailing list here 讨论了澳大利亚应该使用什么时区缩写的问题(讨论持续了两个月:March 2013、April 2013)。

对于缩写应该是什么,各方似乎都有强烈的意见,而这些强烈的意见导致了僵局。

Some say the abbreviations are a relic of the past 并且不应使用,并且不应修复歧义以帮助阻止其使用。

显然,澳大利亚没有公认的权威机构定义缩写词。 Some say conflicting organizations use different timezone abbreviations,为了不选择政治立场,IANA 选择了 EST 作为标准时间和夏令时。

目前,Olson DB 对澳大利亚/悉尼所有日期的所有时区使用 EST:

In [60]: import pytz
In [61]: sydney = pytz.timezone('Australia/Sydney')

In [68]: [(date, tzabbrev) for date, (utcoffset, dstoffset, tzabbrev) in zip(sydney._utc_transition_times, sydney._transition_info)]
Out[68]: 
[(datetime.datetime(1, 1, 1, 0, 0), 'EST'),
 (datetime.datetime(1916, 12, 31, 14, 1), 'EST'),
 (datetime.datetime(1917, 3, 24, 15, 0), 'EST'),
 (datetime.datetime(1941, 12, 31, 16, 0), 'EST'),
 (datetime.datetime(1942, 3, 28, 15, 0), 'EST'),
 (datetime.datetime(1942, 9, 26, 16, 0), 'EST'),
 (datetime.datetime(1943, 3, 27, 15, 0), 'EST'),
 ...]

In [69]: set([tzabbrev for utcoffset, dstoffset, tzabbrev in sydney._transition_info])
Out[69]: 'EST'

这表明在澳大利亚/悉尼时区,EST 用于跨越每个转换边界。

【讨论】:

祝一群数据库维护者好运,他们试图改革公共实践。假设这种做法在澳大利亚和美国一样普遍。 +1。并且为了清楚起见(如果你可以这样称呼它),在夏季 - 澳大利亚东部时区使用“东部夏令时间”而不是“东部标准时间”,因此有两个“EST”缩写。持续的争论是是否将其称为 EST、EDT、AEST 或 AEDT。

以上是关于为啥 pytz 在跨越 TZ 和 DST 边界而不是 TZ 名称时正确调整时间和偏移量?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 pytz localize() 不生成一个 tzinfo 与本地化它的 tz 对象匹配的 datetime 对象?

为啥只有一个日期受 DST 影响?

使用pytz查找时区时差

使用 moment.js 将跨越秋季 DST 边界的一系列非 UTC 时间戳转换为 UTC 时间戳的正确方法?

pytz UTC 转换

Python & pytz:确定时区是不是支持 DST 以及 DST 和非 DST UTC 偏移量是多少