“Canonical”使用pytz从UTC偏移?

时间:2014-12-15 19:51:59

标签: python utc timezone-offset pytz

我很好奇如何打电话给#34;规范"在某些时区选择器上提供的时区偏移(如果有甚至这样的事情,作为规范偏移,我甚至不确定)。

例如,在Windows XP上,您可以看到,对于Eastern Time (US & Canada),下拉列表始终显示GMT-05:00这实际上在全年都不正确,因为当夏令时生效时, UTC的偏移量仅为-4:00。另一方面,每个人都提到US/Eastern距离UTC 5小时。我想知道如何调用-5:00。电子邮件时间标识符?典型的时区偏移?

此外,如果我使用pytz创建US/Eastern时区,是否可以抓取-5:00,无论实际的现在时间是否在DST与否?我的意思是......我想知道是否有某种功能,或者某种东西可以获得-5:00,无论我今天或在中间运行8月(启用DST且实际偏移量仅为-4:00

Windows XP timezone selector

来自http://www.microsoft.com/library/media/1033/windowsxp/images/using/setup/tips/67446-change-time-zone.gif

的图片

提前谢谢。

1 个答案:

答案 0 :(得分:2)

如果我们将“规范”表示不在DST中的日期的utcoffset,则问题将减少为查找不是DST的日期(对于每个时区)。

我们可以先尝试当前日期。如果不是DST,那么我们很幸运。如果是,那么我们可以逐步查看utc过渡日期列表(存储在tzone._utc_transition_times中)直到我们找到一个不是DST的日期:

import pytz
import datetime as DT
utcnow = DT.datetime.utcnow()

canonical = dict()
for name in pytz.all_timezones:
    tzone = pytz.timezone(name)
    try:
        dstoffset = tzone.dst(utcnow, is_dst=False)
    except TypeError:
        # pytz.utc.dst does not have a is_dst keyword argument
        dstoffset = tzone.dst(utcnow)
    if dstoffset == DT.timedelta(0):
        # utcnow happens to be in a non-DST period
        canonical[name] = tzone.localize(utcnow, is_dst=False).strftime('%z') 
    else:
        # step through the transition times until we find a non-DST datetime
        for transition in tzone._utc_transition_times[::-1]:
            dstoffset = tzone.dst(transition, is_dst=False) 
            if dstoffset == DT.timedelta(0):
                canonical[name] = (tzone.localize(transition, is_dst=False)
                                   .strftime('%z'))
                break

for name, utcoffset in canonical.iteritems():
    print('{} --> {}'.format(name, utcoffset)) 

# All timezones have been accounted for
assert len(canonical) == len(pytz.all_timezones)

产量

...
Mexico/BajaNorte --> -0800
Africa/Kigali --> +0200
Brazil/West --> -0400
America/Grand_Turk --> -0400
Mexico/BajaSur --> -0700
Canada/Central --> -0600
Africa/Lagos --> +0100
GMT-0 --> +0000
Europe/Sofia --> +0200
Singapore --> +0800
Africa/Tripoli --> +0200
America/Anchorage --> -0900
Pacific/Nauru --> +1200

请注意,上面的代码访问私有属性tzone._utc_transition_times。这是pytz中的实现细节。由于它不是公共API的一部分,因此不保证在未来版本的pytz中存在。实际上,对于当前版本的pytz中的所有时区都不存在 - 特别是,对于没有DST转换时间的时区,例如'Africa/Bujumbura',它不存在。 (这就是我为什么要先检查utcnow是否恰好在非DST时间段内的原因。)

如果您想要一种不依赖私有属性的方法,我们可以简单地将utcnow游回一天,直到我们找到一个非DST时间段的日子。代码会比上面的代码慢一点,但是因为你真的只需要运行一次这段代码来收集所需的信息,所以它真的无关紧要。

以下是不使用_utc_transition_times代码的代码:

import pytz
import datetime as DT
utcnow = DT.datetime.utcnow()

canonical = dict()
for name in pytz.all_timezones:
    tzone = pytz.timezone(name)
    try:
        dstoffset = tzone.dst(utcnow, is_dst=False)
    except TypeError:
        # pytz.utc.dst does not have a is_dst keyword argument
        dstoffset = tzone.dst(utcnow)
    if dstoffset == DT.timedelta(0):
        # utcnow happens to be in a non-DST period
        canonical[name] = tzone.localize(utcnow, is_dst=False).strftime('%z') 
    else:
        # step through the transition times until we find a non-DST datetime
        date = utcnow
        while True:
            date = date - DT.timedelta(days=1)
            dstoffset = tzone.dst(date, is_dst=False) 
            if dstoffset == DT.timedelta(0):
                canonical[name] = (tzone.localize(date, is_dst=False)
                                   .strftime('%z'))
                break

for name, utcoffset in canonical.iteritems():
    print('{} --> {}'.format(name, utcoffset)) 

# All timezones have been accounted for
assert len(canonical) == len(pytz.all_timezones)