Если мы возьмем «канонический», чтобы означать 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)