Получить название Olson TZ для местного часового пояса?
Как я могу получить название часового пояса Олсона (например, Australia/Sydney
) соответствует значению, данному C localtime
вызов?
Это значение переопределено через TZ
по символической ссылке /etc/localtime
или настройка TIMEZONE
переменная в файлах конфигурации системы, связанных со временем.
11 ответов
Я думаю, что лучше всего пройти через все часовые пояса pytz и проверить, какой из них соответствует местному часовому поясу, каждый объект часового пояса pytz содержит информацию о utcoffset и tzname, таких как CDT, EST, ту же информацию о местном времени можно получить из time.timezone/altzone
а также time.tzname
и я думаю, что этого достаточно, чтобы правильно сопоставить местный часовой пояс в базе данных pytz, например
import time
import pytz
import datetime
local_names = []
if time.daylight:
local_offset = time.altzone
localtz = time.tzname[1]
else:
local_offset = time.timezone
localtz = time.tzname[0]
local_offset = datetime.timedelta(seconds=-local_offset)
for name in pytz.all_timezones:
timezone = pytz.timezone(name)
if not hasattr(timezone, '_tzinfos'):
continue#skip, if some timezone doesn't have info
# go thru tzinfo and see if short name like EDT and offset matches
for (utcoffset, daylight, tzname), _ in timezone._tzinfos.iteritems():
if utcoffset == local_offset and tzname == localtz:
local_names.append(name)
print local_names
выход:
[Америка / Атикокан, Америка / Баия Бандерас, Америка / Баия Бандерас, Америка / Белиз, Америка / Кембридж Бэй, Америка / Канкун, Америка / Чикаго, Америка / Чихуахуа, ' Америка /Coral_Harbour', 'America/Costa_Rica', 'America/El_Salvador', 'America/Fort_Wayne', 'America/Guatemala', 'America/Indiana/Indianapolis', ' Америка / Индиана /Knox', 'America/Indiana/ Маренго "," Америка / Индиана / Маренго "," Америка / Индиана / Петербург "," Америка / Индиана /Tell_City "," Америка / Индиана / Вевай "," Америка / Индиана / Винсеннес "," Америка / Индиана / Винамак ", "Америка / Индианаполис", "Америка / Икалуит", "Америка / Кентукки / Луисвилл", "Америка / Кентукки / Луисвилл", "Америка / Кентукки / Монтиселло", "Америка / Нокс_IN", "Америка / Луисвилл", " Америка / Луисвилл "," Америка / Манагуа "," Америка / Матаморос "," Америка / Меномини "," Америка / Мерида "," Америка / Мексика_Город "," Америка / Монтеррей "," Америка / Северная Дакота / Бьюла "," Америка / Северная Дакота / Центр, Америка / Северная Дакота / Новый Салем, Америка / Оджинага, Америка / Пангниртунг, Ам erica/Rainy_River', 'America/Rankin_Inlet', 'America/Resolute', 'America/Resolute', 'America/Tegucigalpa', 'America/Winnipeg', 'CST6CDT', 'Canada/Central', 'Mexico/General', "США / Центральная", "США / Восточная Индиана", "США / Индиана-Старке"]
В производственной среде вы можете заранее создать такое отображение и сохранить его, вместо того, чтобы выполнять итерации всегда.
Скрипт тестирования после смены часового пояса:
$ export TZ = 'Австралия / Сидней'
$ python get_tz_names.py
['Антарктида / Маккуори', 'Австралия / АСТ', 'Австралия / Брисбен', 'Австралия / Канберра', 'Австралия / Керри', 'Австралия / Хобарт', 'Австралия / Линдеман', 'Австралия / Мельбурн', ' Австралия /NSW', ' Австралия / Квинсленд ',' Австралия / Сидней ',' Австралия / Тасмания ',' Австралия / Виктория ']
Это своего рода обман, я знаю, но получаю от '/etc/localtime'
у тебя не работает? Вроде следующего:
>>> import os
>>> '/'.join(os.readlink('/etc/localtime').split('/')[-2:])
'Australia/Sydney'
Надеюсь, поможет.
Редактировать: мне понравилась идея @AH, на случай, если '/etc/localtime'
не символическая ссылка Переводя это на Python:
#!/usr/bin/env python
from hashlib import sha224
import os
def get_current_olsonname():
tzfile = open('/etc/localtime')
tzfile_digest = sha224(tzfile.read()).hexdigest()
tzfile.close()
for root, dirs, filenames in os.walk("/usr/share/zoneinfo/"):
for filename in filenames:
fullname = os.path.join(root, filename)
f = open(fullname)
digest = sha224(f.read()).hexdigest()
if digest == tzfile_digest:
return '/'.join((fullname.split('/'))[-2:])
f.close()
return None
if __name__ == '__main__':
print get_current_olsonname()
Одна проблема заключается в том, что существует несколько "красивых имен", таких как "Австралия / Сидней", которые указывают на один и тот же часовой пояс (например, CST).
Таким образом, вам нужно будет получить все возможные имена для местного часового пояса, а затем выбрать имя, которое вам нравится.
Например: для Австралии существует 5 часовых поясов, но гораздо больше идентификаторов часовых поясов:
"Australia/Lord_Howe", "Australia/Hobart", "Australia/Currie",
"Australia/Melbourne", "Australia/Sydney", "Australia/Broken_Hill",
"Australia/Brisbane", "Australia/Lindeman", "Australia/Adelaide",
"Australia/Darwin", "Australia/Perth", "Australia/Eucla"
Вы должны проверить, есть ли библиотека, которая упаковывает TZinfo, для обработки API часового пояса.
Например: для Python, проверьте pytz
библиотека:
а также
http://pypi.python.org/pypi/pytz/
в Python вы можете сделать:
from pytz import timezone
import pytz
In [56]: pytz.country_timezones('AU')
Out[56]:
[u'Australia/Lord_Howe',
u'Australia/Hobart',
u'Australia/Currie',
u'Australia/Melbourne',
u'Australia/Sydney',
u'Australia/Broken_Hill',
u'Australia/Brisbane',
u'Australia/Lindeman',
u'Australia/Adelaide',
u'Australia/Darwin',
u'Australia/Perth',
u'Australia/Eucla']
но API для Python, кажется, довольно ограничен, например, он не имеет такого вызова, как Ruby all_linked_zone_names
- который может найти все имена синонимов для данного часового пояса.
Если оценивать /etc/localtime
хорошо для вас, следующий трюк может сработать - после перевода его на python:
> md5sum /etc/localtime
abcdefabcdefabcdefabcdefabcdefab /etc/localtime
> find /usr/share/zoneinfo -type f |xargs md5sum | grep abcdefabcdefabcdefabcdefabcdefab
abcdefabcdefabcdefabcdefabcdefab /usr/share/zoneinfo/Europe/London
abcdefabcdefabcdefabcdefabcdefab /usr/share/zoneinfo/posix/Europe/London
...
Дубликаты можно отфильтровать, используя только официальные названия регионов "Европа", "Америка" ... Если дубликаты все еще есть, вы можете взять самое короткое имя:-)
import pytz
import time
#import locale
import urllib2
yourOlsonTZ = None
#yourCountryCode = locale.getdefaultlocale()[0].split('_')[1]
yourCountryCode = urllib2.urlopen('http://api.hostip.info/country.php').read()
for olsonTZ in [pytz.timezone(olsonTZ) for olsonTZ in pytz.all_timezones]:
if (olsonTZ._tzname in time.tzname) and (str(olsonTZ) in pytz.country_timezones[yourCountryCode]):
yourOlsonTZ = olsonTZ
break
print yourOlsonTZ
Этот код будет делать предположение о вашем часовом поясе Олсона, основываясь как на названии вашего часового пояса (согласно Python time
модуль), и ваш код страны (в соответствии с locale
модуль проекта http://www.hostip.info/, который ссылается на ваш IP-адрес и соответственно определяет ваше местоположение).
Например, простое сопоставление имен Timzone может привести к America/Moncton
, America/Montreal
, или же America/New_York
для EST (GMT-5). Однако если ваша страна - США, ответ будет ограничен America/New_York
,
Однако, если вашей страной является Канада, сценарий просто по умолчанию будет иметь самый высокий результат в Канаде (America/Moncton
). Если есть способ уточнить это, пожалуйста, не стесняйтесь оставлять предложения в комментариях.
Модуль tzlocal для Python нацелен именно на эту проблему. Он дает согласованные результаты как в Linux, так и в Windows, правильно конвертируя идентификаторы часовых поясов Windows в Olson, используя сопоставления CLDR.
Это даст вам имя часового пояса, в соответствии с тем, что находится в переменной TZ, или файл местного времени, если не установлен:
#! /usr/bin/env python
import time
time.tzset
print time.tzname
Вот еще одна возможность, используя вместо этого PyICU; который работает для моих целей:
>>> from PyICU import ICUtzinfo
>>> from datetime import datetime
>>> datetime(2012, 1, 1, 12, 30, 18).replace(tzinfo=ICUtzinfo.getDefault()).isoformat()
'2012-01-01T12:30:18-05:00'
>>> datetime(2012, 6, 1, 12, 30, 18).replace(tzinfo=ICUtzinfo.getDefault()).isoformat()
'2012-06-01T12:30:18-04:00'
Здесь он интерпретирует niave datetime (как было бы возвращено запросом базы данных) в местном часовом поясе.
Я изменил скрипт tcurvelo, чтобы найти правильную форму часового пояса (Континент /..../ Город), в большинстве случаев, но вернуть все, если не получится
#!/usr/bin/env python
from hashlib import sha224
import os
from os import listdir
from os.path import join, isfile, isdir
infoDir = '/usr/share/zoneinfo/'
def get_current_olsonname():
result = []
tzfile_digest = sha224(open('/etc/localtime').read()).hexdigest()
test_match = lambda filepath: sha224(open(filepath).read()).hexdigest() == tzfile_digest
def walk_over(dirpath):
for root, dirs, filenames in os.walk(dirpath):
for fname in filenames:
fpath = join(root, fname)
if test_match(fpath):
result.append(tuple(root.split('/')[4:]+[fname]))
for dname in listdir(infoDir):
if dname in ('posix', 'right', 'SystemV', 'Etc'):
continue
dpath = join(infoDir, dname)
if not isdir(dpath):
continue
walk_over(dpath)
if not result:
walk_over(join(infoDir))
return result
if __name__ == '__main__':
print get_current_olsonname()
Я предпочитаю следовать немного лучше, чем копаться в значениях _xxx
import time, pytz, os
cur_name=time.tzname
cur_TZ=os.environ.get("TZ")
def is_current(name):
os.environ["TZ"]=name
time.tzset()
return time.tzname==cur_name
print "Possible choices:", filter(is_current, pytz.all_timezones)
# optional tz restore
if cur_TZ is None: del os.environ["TZ"]
else: os.environ["TZ"]=cur_TZ
time.tzset()
Этот проект JavaScript пытается решить ту же проблему в клиентской части браузера. Он работает, воспроизводя "двадцать вопросов" с языковым стандартом, запрашивая смещение UTC для определенных прошедших времен (для проверки границ летнего времени и т. Д.) И используя эти результаты для определения того, каким должен быть местный часовой пояс. К сожалению, я не знаю ни одного эквивалентного пакета Python, поэтому, если кто-то захочет использовать это решение, его придется перенести на Python.
Хотя эта формула потребует обновления каждый раз (в худшем случае) база данных TZ обновляется, комбинация этого алгоритма и решения, предложенного Anurag Uniyal (сохраняя только возможности, возвращаемые обоими методами), звучит для меня как самый верный способ вычисления эффективного местный часовой пояс. Пока существует некоторая разница между смещением UTC по меньшей мере одного местного времени в любых двух часовых поясах, такая система может правильно выбирать между ними.