2014-12-15 10 views
4

Мне любопытно, как назвать «каноническое» смещение часового пояса, которое предлагается по определенным селекторам часового пояса (если есть даже такая вещь, как каноническое смещение, о котором я даже не уверен)."Канонический" отсчет от UTC с помощью pytz?

Например, в Windows XP, вы можете увидеть, что для Eastern Time (US & Canada) выпадающий всегда показывает GMT-05:00 Это на самом деле не правильно в течение всего года, так как при переходе на летнее время в действительности, смещение с UTC только -4:00 , С другой стороны, все упоминают US/Eastern как 5 часов от UTC. Мне было интересно, как называется -5:00. Идентификатор времени электронной почты? Каноническое смещение часового пояса?

Кроме того, если я создаю US/Eastern часовой пояс с pytz, есть способ захвата, что -5:00 независимо от того, является ли текущая настоящего времени в DST или нет? Я имею в виду ... Я хотел бы знать, есть ли функция, или ... что-то получить -5:00, независимо от того, выполняю ли я это сегодня или в середине августа (когда включено DST и фактическое смещение просто -4:00)

Windows XP timezone selector

изображение из http://www.microsoft.com/library/media/1033/windowsxp/images/using/setup/tips/67446-change-time-zone.gif

Спасибо заранее.

ответ

2

Если мы возьмем «канонический», чтобы означать utcoffset дат, которые не находятся в DST, тогда проблема сводится к поиску дат (для каждого часового пояса), которые не являются 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. Код будет немного медленнее, чем предыдущий, но поскольку вам действительно нужно только один раз запустить этот код, чтобы получить нужную информацию, это действительно не имеет значения.

Вот что код будет выглядеть без использования _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)