Получить название Olson TZ для местного часового пояса? - PullRequest
48 голосов
/ 06 октября 2011

Как мне получить имя часового пояса Олсона (например, Australia/Sydney), соответствующее значению, данному вызовом C's localtime?

Этозначение переопределяется через TZ, с помощью символической ссылки /etc/localtime или путем установки переменной TIMEZONE в файлах конфигурации системы, связанных со временем.

Ответы [ 11 ]

17 голосов
/ 30 ноября 2011

Я думаю, что лучше всего пройти через все часовые пояса 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

output:

['America / Atikokan', 'America / Bahia_Banderas',' Америка / Bahia_Banderas ',' Америка / Белиз ',' Америка / Cambridge_Bay ',' Америка / Канкун ',' Америка / Чикаго ',' Америка / Чихуахуа ',' Америка / Coral_Harbour ',' Америка / Costa_Rica ',«Америка / Эль-Сальвадор», «Америка / Форт-Уэйн», «Америка / Гватемала», «Америка / Индиана / Индианаполис», «Америка / Индиана / Нокс», «Америка / Индиана / Маренго», «Америка / Индиана / Маренго»,«Америка / Индиана / Петербург», «Америка / Индиана / Tell_City», «Америка / Индиана / Вевай», «Америка / Индиана / Винсеннес», «Америка / Индиана / Винамак», «Америка / Индианаполис», «Америка / Икалуит»',' Америка / Кентукки / Луисвилл ',' Америка / КентUcky / Луисвилл "," Америка / Кентукки / Монтиселло "," Америка / Knox_IN "," Америка / Луисвилл "," Америка / Луисвилл "," Америка / Манагуа "," Америка / Матаморос "," Америка / Меномини ","Америка / Мерида ',' Америка / Мексика_Город ',' Америка / Монтеррей ',' Америка / Северная_Дакота / Беула ',' Америка / Северная_Дакота / Центр ',' Америка / Северная_Дакота / Новый_Салем ',' Америка / Оджинага ',' Америка /Pangnirtung ',' America / Rainy_River ',' America / Rankin_Inlet ',' America / Resolute ',' America / Resolute ',' America / Тегусигальпа ',' America / Winnipeg ',' CST6CDT ',' Canada / Central ','Мексика / Генерал »,« США / Центральная »,« США / Восточная Индиана »,« США / Индиана-Старке »]

В производстве вы можете заранее создать такое отображение и сохранить его вместоитерация всегда.

Проверка сценария после изменения часового пояса:

$ export TZ = 'Австралия / Сидней'
$ python get_tz_names.py
['Антарктида / Macquarie', «Австралия / ACT», «Австралия / Брисбен», «Австралия / Канберра», «Австралия / Керри», «Австралия / Хобарт», «Австралия / Линде»человек »,« Австралия / Мельбурн »,« Австралия / Новый Южный Уэльс »,« Австралия / Квинсленд »,« Австралия / Сидней »,« Австралия / Тасмания »,« Австралия / Виктория »]

16 голосов
/ 20 октября 2011

Я знаю, что это обман, но получение от '/etc/localtime' не работает для вас? Нравится следующее:

>>>  import os
>>> '/'.join(os.readlink('/etc/localtime').split('/')[-2:])
'Australia/Sydney'

Надеюсь, это поможет.

Редактировать : мне понравилась идея @ A.H. на случай, если '/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()
13 голосов
/ 26 октября 2011

Одна проблема заключается в том, что существует несколько «красивых имен», таких как «Австралия / Сидней», которые указывают на один и тот же часовой пояс (например, 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://pytz.sourceforge.net/

и

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кажется довольно ограниченным, например, он не имеет вызова, подобного all_linked_zone_names Руби - который может найти все имена синонимов для данного часового пояса.

8 голосов
/ 26 октября 2011

Если оценка /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
...

Дубликаты могут быть отфильтрованы с использованием только официальных имен регионов "Европа", "Америка "... Если есть еще дубликаты, вы можете взять самое короткое имя: -)

7 голосов
/ 25 октября 2011

Установить pytz

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

В этом коде будет применена наиболее вероятная ошибка в вашем часовом поясе Олсона, основанная как на имени часового пояса (в соответствии с модулем time Python), так и на вашем коде страны (в соответствии с locale модулем Python *1008*). 1009 * проект hostip.info , который ссылается на ваш IP-адрес и соответственно определяет ваше местоположение).

Например, простое сопоставление Имен Timzone может привести к America/Moncton, America/Montreal или America/New_York для EST (GMT-5). Однако, если ваша страна - США, ответ будет ограничен America/New_York.

Однако, если вашей страной является Канада, сценарий просто по умолчанию выдаст самый верхний результат в Канаде (America/Moncton). Если есть способ уточнить это, пожалуйста, не стесняйтесь оставлять предложения в комментариях.

4 голосов
/ 24 апреля 2013

Модуль tzlocal для Python нацелен именно на эту проблему.Он дает согласованные результаты как в Linux, так и в Windows, правильно преобразовывая идентификаторы часовых поясов Windows в Olson, используя сопоставления CLDR.

2 голосов
/ 06 октября 2011

Это даст вам имя часового пояса, в соответствии с тем, что находится в переменной TZ, или файл местного времени, если не установлено:

#! /usr/bin/env python

import time

time.tzset
print time.tzname
1 голос
/ 02 июня 2012

Вот еще одна возможность, используя вместо нее 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 (как было бы возвращено запросом базы данных) в местном часовом поясе.

0 голосов
/ 30 июля 2013

Этот проект JavaScript пытается решить ту же проблему в клиентской части браузера.Он работает, воспроизводя «двадцать вопросов» с языковым стандартом, запрашивая смещение UTC для определенных прошедших времен (для проверки границ летнего времени и т. Д.) И используя эти результаты для определения того, каким должен быть местный часовой пояс.К сожалению, я не знаю ни одного эквивалентного пакета Python, поэтому, если кто-то захочет использовать это решение, его придется перенести на Python.

Хотя эта формула потребует обновления каждый раз (в худшем случае) база данных TZОбновленный, комбинация этого алгоритма и решения, предложенного Anurag Uniyal (сохраняя только возможности, возвращаемые обоими методами), звучит для меня как самый верный способ вычисления эффективного местного часового пояса.Пока существует некоторая разница между смещением UTC по крайней мере одного местного времени в любых двух часовых поясах, такая система может правильно выбирать между ними.

0 голосов
/ 30 июля 2013

Я изменил скрипт 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()
...