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

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

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


person Matt Joiner    schedule 06.10.2011    source источник
comment
Итак, чтобы быть ясным, вам нужен способ определения имени часового пояса Олсона (т.е. America/New_York) текущего пользовательского языкового стандарта в Linux?   -  person wberry    schedule 06.10.2011
comment
@wberry: Да, именно так.   -  person Matt Joiner    schedule 07.10.2011
comment
В таком случае я играю главную роль, потому что я тоже хочу знать :-)   -  person wberry    schedule 08.10.2011
comment
Достаточно похоже на stackoverflow.com/questions/1723073/, чтобы получить некоторые идеи.   -  person A.H.    schedule 26.10.2011
comment
Думаю, в моем ответе правильно перечислены возможные совпадения? как вы думаете, в некоторых случаях он может давать неверные результаты?   -  person Anurag Uniyal    schedule 30.11.2011
comment
@MattJoiner Я изменил фрагмент, чтобы он стал более надежным. Надеюсь, это то, что вы искали.   -  person Patrick Perini    schedule 03.12.2011


Ответы (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

вывод:

['America / Atikokan', 'America / Bahia_Banderas', 'America / Bahia_Banderas', 'America / Belize', 'America / Cambridge_Bay', 'America / Cancun', 'America / Chicago', 'America / Chihuahua', ' America / Coral_Harbour ',' America / Costa_Rica ',' America / El_Salvador ',' America / Fort_Wayne ',' Америка / Гватемала ',' America / Indiana / Indianapolis ',' America / Indiana / Knox ',' America / Indiana / Маренго ',' Америка / Индиана / Маренго ',' Америка / Индиана / Петербург ',' Америка / Индиана / Телл_Сити ',' Америка / Индиана / Вевей ',' Америка / Индиана / Винсеннес ',' Америка / Индиана / Винамак ' , 'Америка / Индианаполис', 'Америка / Икалуит', 'Америка / Кентукки / Луисвилл', 'Америка / Кентукки / Луисвилл', 'Америка / Кентукки / Монтичелло', 'Америка / Knox_IN', 'Америка / Луисвилл', ' Америка / Луисвилл, Америка / Манагуа, Америка / Матаморос, Америка / Меномини, Америка / Мерида, Америка / Мексика_Город, Америка / Монтеррей, Америка / Северная_Дакота / Беула, » America / North_Dakota / Center ',' America / North_Dakota / New_Salem ',' Америка / Оджин aga ',' America / Pangnirtung ',' America / Rainy_River ',' America / Rankin_Inlet ',' America / Resolute ',' America / Resolute ',' America / Tegucigalpa ',' America / Winnipeg ',' CST6CDT ',' Канада / Центральная »,« Мексика / Общие »,« США / Центральная »,« США / Восточная Индиана »,« США / Индиана-Старк »]

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

Скрипт тестирования после смены часового пояса:

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

person Anurag Uniyal    schedule 30.11.2011
comment
Этот алгоритм пока дает наилучшие результаты, но он не кажется таким чистым, как ответ @ pcperini. - person Matt Joiner; 02.12.2011
comment
@Matt Joiner, я думаю, что это такой же чистый код, мудрый и более точный, поскольку вместо названия страны он сравнивает смещения - person Anurag Uniyal; 02.12.2011
comment
@MattJoiner, также я не думаю, что код страны, полученный из URL-адреса, изменится, если изменится настройка TZ системы? - person Anurag Uniyal; 02.12.2011
comment
Чтобы узнать, действует ли летнее время: if time.daylight and time.localtime().tm_isdst > 0. Примечание: time.daylight вместе просто сообщает, есть ли в местном часовом поясе летнее время, но ничего не говорит о текущем состоянии. - person jfs; 02.08.2013

Я знаю, что это своего рода обман, но получение от '/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()
person Thiago Curvelo    schedule 20.10.2011
comment
Он работает, когда / etc / localtime является символической ссылкой. В большинстве случаев это не так. - person Matt Joiner; 21.10.2011
comment
Это работает на удивление хорошо. Интересно, что бы произошло, если бы информация о зоне была обновлена, а /etc/localtime - нет. Также я рекомендую вам сразу перейти на отступ в 4 пробела вместо табуляции (или 8 пробелов). - person Matt Joiner; 02.12.2011
comment
Кстати, там было 8 пробелов ... Насчет zoneinfo, я думаю, ваша система будет без tz, без / etc / locatime, не так ли? - person Thiago Curvelo; 02.12.2011
comment
Нет, я имел в виду, если файлы zoneinfo обновлены и / etc / localtime не был символической ссылкой. У вас может быть местное время, которое больше не соответствует ни одному файлу zoneinfo. - person Matt Joiner; 03.12.2011
comment
В моей системе Ubuntu это, к сожалению, возвращает zoneinfo / localtime. - person Matt Joiner; 03.12.2011
comment
@MattJoiner: в моей системе Ubuntu он дает тот же результат, что и ответ Anurag. Я использовал этот код - person jfs; 23.11.2012

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

person Tilo    schedule 25.10.2011
comment
Я согласен, я думаю, это было сделано для того, чтобы пользователи могли установить свой часовой пояс с меньшими хлопотами (например, не зная своего смещения по всемирному координированному времени). - person Mike; 06.11.2011
comment
@Tilo: Для награждения требуется алгоритм для создания этого списка имен часовых поясов. - person Matt Joiner; 02.12.2011
comment
легко в Ruby (я добавил ответ для Ruby ниже) ... проверьте pytz API, чтобы узнать, как сделать то же самое с Python. - person Tilo; 02.12.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
...

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

person A.H.    schedule 25.10.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 hostip.info, который ссылается на ваш IP-адрес и соответственно определяет ваше местоположение).

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

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

person Patrick Perini    schedule 25.10.2011
comment
Можете ли вы переделать определение кода страны, чтобы получить это значение из более надежного места? В Австралии нередко бывает en_US, несмотря на то, что en_AU существует. Кроме того, мое tzname - 'EST', поэтому ваш алгоритм дает мне America/New_York, но я нахожусь в Australia/Sydney. - person Matt Joiner; 02.12.2011
comment
Работаю над этим. Могу ли я предположить доступ в Интернет? - person Patrick Perini; 02.12.2011

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

person Matt Johnson-Pint    schedule 24.04.2013

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

#! /usr/bin/env python

import time

time.tzset
print time.tzname
person Diego Torres Milano    schedule 06.10.2011
comment
К сожалению, он возвращает кортеж из 3-х буквенных кодов часовых поясов, а не полное имя часового пояса. - person Matt Joiner; 06.10.2011
comment
Даже если вы построите для этого таблицу поиска, она будет неоднозначной. EST может быть America/New_York или Australia/Sydney. BST может быть Europe/London или Asia/Dhaka. - person wberry; 10.10.2011
comment
Он прав. Единственное, что система знает о своем часовом поясе, - это смещение по Гринвичу и настройки летнего времени. В Linux они устанавливаются либо переменной среды TZ (обычно не заданной), либо файлом / etc / localtime, который может быть либо ссылкой на одно из определений часового пояса, найденным в / usr / share / zoneinfo, либо копией один из них. Невозможно получить это значение в общем случае, если вы не знаете наверняка, что система, в которой вы запускаете сценарий, настроена с использованием символической ссылки или переменной среды. Тогда вы, вероятно, могли бы запустить системные команды из Python, чтобы получить его. - person Yanick Girouard; 19.10.2011
comment
@MattJoiner: Что ж, если вы хотите получить содержимое переменной среды, вы можете использовать os.getenv (TZ) или проанализировать возврат ls -l /etc/localtime и извлечь путь ссылки из результата. Здесь есть целая ветка о том, как это сделать: stackoverflow.com/questions/89228/ (то есть вызов ls, а не путь синтаксического анализа) - person Yanick Girouard; 03.12.2011

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

person SingleNegationElimination    schedule 01.06.2012
comment
Что-то не так с отделением интенсивной терапии, что людям не нравится ответ? - person saeedgnu; 30.07.2013

Я предпочитаю следовать немного лучше, чем ковыряться в значениях _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()
person korc    schedule 17.04.2012
comment
Использование переменной среды TZ работает только для Linux (или Unix), но не для Windows. - person saeedgnu; 30.07.2013
comment
Другое дело, что это вообще не работает на моей текущей Fedora (F20), там не определена такая переменная TZ. - person Vinzenz; 22.05.2014

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

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

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

person wberry    schedule 30.07.2013
comment
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится. - Из отзыва - person CJ Dennis; 18.05.2018
comment
Два абзаца объяснения - это ответ только по ссылке? - person wberry; 21.05.2018
comment
Я мог бы выбрать Это комментарий и не отвечает на вопрос. Когда у вас будет достаточно репутации, вы сможете прокомментировать вопрос. Однако у вас более 10 000 повторений, так что это было снисходительно. - person CJ Dennis; 21.05.2018
comment
Я думаю, что он отлично отвечает на вопрос, иначе я бы не тратил время зря. Рассмотрение смещения по всемирному координированному времени ключевых прошлых дат и времени в качестве подсказки к вашей истинной локали - это подход, который здесь не предлагается ни в одном другом ответе. Неужели вы не ожидаете, что я воспроизведу точный алгоритм в моем ответе? - person wberry; 21.05.2018
comment
Если этот проект JavaScript перестанет существовать, ваш ответ будет практически бесполезен. Совершенно не ясно, как сравнение любого количества дат в формате UTC может дать результат Australia/Sydney. - person CJ Dennis; 21.05.2018