为啥 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 名称时正确调整时间和偏移量?的主要内容,如果未能解决你的问题,请参考以下文章