Получить имя Olson TZ для локального часового пояса?

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

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

Ответ 1

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

выход:

['Америка/Атикокан', 'Америка/Bahia_Banderas', "Америка/Bahia_Banderas", "Америка/Белиз", "Америка/Кембридж_Бей", "Америка/Канкун", "Америка/Чикаго", "Америка/Чиуауа", "Америка/Корал-Харбор", "Америка/Коста-Рика", "Америка/Эль-Салвадор", "Америка/Форт-Вайне", "Америка/Гватемала", "Америка/Индиана/Индианаполис", "Америка/Индиана/Нокс", 'Америка/Индиана/Маренго', 'Америка/Индиана/Маренго', "Америка/Индиана/Петербург", "Америка/Индиана/Телль_Чита", 'Америка/Индиана/Vevay', 'Америка/Индиана/Винсеннес', ' "Америка/Индиана/Винамак", "Америка/Индианаполис", "Америка/Икалуит", "Америка/Кентукки/Луисвилл", "Америка/Кентукки/Луисвилл", "Америка/Кентукки/Монтичелло", "Америка/Нокс_Ин", "Америка/Луисвилл", "Америка/Луисвилл", "Америка/Манагуа", "Америка/Матаморос", "Америка/Меномине", "Америка/Мерида", 'America/Mexico_City', 'Америка/Монтеррей', "Америка/Северная_Дакота/Беулах", "Америка/Северная_Дакота/Центр", 'America/North_Dakota/New_Salem', 'Америка/Ojinaga', 'America/Pangnirtung', 'Америка/Rainy_River', 'Америка/Rankin_Inlet', "Америка/Резолюция", "Америка/Резолюция", "Америка/Тегусигальпа", "Америка/Виннипег", "CST6CDT", "Канада/Центральная", "Мексика/генерал", 'US/Central', 'US/East-Indiana', 'US/Indiana-Starke']

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

Тестирование script после изменения часового пояса:

$export TZ = 'Австралия/Сидней'
$ python get_tz_names.py
[ "Антарктика/Маккуори", "Австралия/АКТ", "Австралия/Брисбен", "Австралия/Канберра", "Австралия/Керри", "Австралия/Хобарт", "Австралия/Линдеман", "Австралия/Мельбурн", "Австралия/NSW", "Австралия/Квинсленд", "Австралия/Сидней", "Австралия/Тасмания", 'Австралия/Виктория']

Ответ 2

Это своего рода обман, я знаю, но получение от '/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()

Ответ 3

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

Ответ 4

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

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

Ответ 5

Установить 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

Этот код будет взламываться на ваш часовой пояс Olson, основанный как на вашем имени часового пояса (в соответствии с модулем Python time), так и на вашем коде страны (в соответствии с Python locale module hostip.info проект, который ссылается на ваш IP-адрес и соответствующим образом привязывает вас к геолокации).

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

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

Ответ 6

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

Ответ 7

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

#! /usr/bin/env python

import time

time.tzset
print time.tzname

Ответ 8

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

Ответ 9

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

Ответ 10

Я изменил tcurvelo script, чтобы найти правильную форму часового пояса (Continent/..../City), в большинстве случаев, но вернуть все из них, если сбой

#!/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()

Ответ 11

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

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