Как настроить системный часовой пояс и передать его в pytz.timezone?

Мы можем использовать time.tzname имя локального часового пояса, но это имя несовместимо с pytz.timezone.

Фактически, имя, возвращаемое time.tzname, неоднозначно. Этот метод возвращает ('CST', 'CST') в моей системе, но "CST" может указывать четыре часовых пояса:

  • Центральный часовой пояс (Северная Америка) - наблюдается в Центральном часовом поясе Северной Америки.
  • Стандартное время в Китае
  • Стандартное время Chungyuan - термин "стандартное время Chungyuan" теперь редко используется на Тайване.
  • Центральное стандартное время Австралии (ACST)

Ответ 1

Очень простой способ решить этот вопрос:

import time

def localTzname():
    offsetHour = time.timezone / 3600
    return 'Etc/GMT%+d' % offsetHour

Обновить: @MartijnPieters сказал: "Это не будет работать с DST/летнее время". Итак, как насчет этой версии?

import time

def localTzname():
    if time.daylight:
        offsetHour = time.altzone / 3600
    else:
        offsetHour = time.timezone / 3600
    return 'Etc/GMT%+d' % offsetHour

Ответ 2

tzlocal module возвращает объект pytz tzinfo, соответствующий локальному часовому поясу:

import time
from datetime import datetime

import pytz # $ pip install pytz
from tzlocal import get_localzone # $ pip install tzlocal

# get local timezone    
local_tz = get_localzone() 

# test it
# utc_now, now = datetime.utcnow(), datetime.now()
ts = time.time()
utc_now, now = datetime.utcfromtimestamp(ts), datetime.fromtimestamp(ts)

local_now = utc_now.replace(tzinfo=pytz.utc).astimezone(local_tz) # utc -> local
assert local_now.replace(tzinfo=None) == now

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

local_tz также работает для прошлых дат, даже если utc offset для локального часового пояса в то время было другим. Решение на основе dateutil.tz.tzlocal() терпит неудачу в этом случае, например, в европейском/московском часовом поясе (пример с 2013 года):

>>> import os, time
>>> os.environ['TZ'] = 'Europe/Moscow'
>>> time.tzset()
>>> from datetime import datetime
>>> from dateutil.tz import tzlocal
>>> from tzlocal import get_localzone
>>> dateutil_tz = tzlocal()
>>> tzlocal_tz = get_localzone()
>>> datetime.fromtimestamp(0, dateutil_tz)                              
datetime.datetime(1970, 1, 1, 4, 0, tzinfo=tzlocal())
>>> datetime.fromtimestamp(0, tzlocal_tz)
datetime.datetime(1970, 1, 1, 3, 0, tzinfo=<DstTzInfo 'Europe/Moscow' MSK+3:00:00 STD>)

dateutil возвращает неправильный UTC + 4 смещение вместо правильного UTC + 3 в 1970-01-01.

Для тех, кто сталкивается с этим в 2017 году, dateutil.tz.tzlocal() все еще сломан. Вышеприведенный пример работает сейчас, потому что текущее смещение utf - это UTC + 3 в Москве (что случайно соответствует смещению utc с 1970 года). Чтобы продемонстрировать ошибку, мы можем выбрать дату, когда utc offset - UTC + 4:

>>> import os, time
>>> os.environ['TZ'] = 'Europe/Moscow'
>>> time.tzset()
>>> from datetime import datetime
>>> from dateutil.tz import tzlocal
>>> from tzlocal import get_localzone
>>> dateutil_tz = tzlocal()
>>> tzlocal_tz = get_localzone()
>>> ts = datetime(2014, 6,1).timestamp() # get date in 2014 when gmtoff=14400 in Moscow
>>> datetime.fromtimestamp(ts, dateutil_tz)
datetime.datetime(2014, 5, 31, 23, 0, tzinfo=tzlocal())
>>> datetime.fromtimestamp(ts, tzlocal_tz)
datetime.datetime(2014, 6, 1, 0, 0, tzinfo=<DstTzInfo 'Europe/Moscow' MSK+4:00:00 STD>)

dateutil возвращает неправильный UTC + 3 вместо правильного UTC + 4 на 2014-06-01.

Ответ 3

Используйте tzlocal функцию из пакета python-dateutil:

from dateutil.tz import tzlocal

localtimezone = tzlocal()

Внутри это класс, который использует time.timezone и time.altzone (переключение на основе time.daylight), но создает из него подходящий объект часового пояса.

Вы используете это вместо часового пояса pytz.

Альтернативой является чтение текущего настроенного часового пояса из операционной системы, но это сильно отличается от ОС к ОС. В Mac OS X вам нужно прочитать вывод systemsetup -gettimezone:

$ systemsetup -gettimezone
Time Zone: Europe/Copenhagen

В системах Debian и Ubuntu вы можете прочитать /etc/timezone:

$ cat /etc/timezone
Europe/Oslo

В RedHat и нарисованных системах вам нужно прочитать его из /etc/sysconfig/clock:

$ grep ZONE /etc/sysconfig/clock
ZONE="Europe/Oslo"

Ответ 4

Я не знаю, полезно ли это для вас или нет, но я думаю, что он отвечает на вашу более общую проблему:

если у вас есть дата, которая находится в неоднозначном часовом поясе, например CST, simple-date (только python 3.2+, извините) может автоматизировать поиск и позволяет делать такие вещи, как предпочитают определенные страны.

например:

>>> SimpleDate('2013-07-04 18:53 CST')
Traceback [...
simpledate.AmbiguousTimezone: 3 distinct timezones found: <DstTzInfo 'Australia/Broken_Hill' CST+9:30:00 STD>; <DstTzInfo 'America/Regina' LMT-1 day, 17:01:00 STD>; <DstTzInfo 'Asia/Harbin' LMT+8:27:00 STD> (timezones=('CST',), datetime=datetime.datetime(2013, 7, 4, 18, 53), is_dst=False, country=None, unsafe=False)
>>> SimpleDate('2013-07-04 18:53 CST', country='CN')
SimpleDate('2013-07-04 18:53 CST')
>>> SimpleDate('2013-07-04 18:53 CST', country='CN').utc
SimpleDate('2013-07-04 10:53 UTC', tz='UTC')

обратите внимание, как, указав страну, вы достаточно уменьшите диапазон возможных значений, чтобы разрешить преобразование в UTC.

он реализован путем выполнения поиска по тайм-зонам в PyTZ:

>>> SimpleDate('2013-07-04 18:53 CST', country='CN', debug=True)
...
PyTzFactory: Have country code CN
PyTzFactory: Country code CN has 5 timezones
PyTzFactory: Expanded country codes to 5 timezones
PyTzFactory: Expanding ('CST',)
PyTzFactory: Name lookup failed for CST
PyTzFactory: Found CST using Asia/Shanghai
PyTzFactory: Found CST using Asia/Harbin
PyTzFactory: Found CST using Asia/Chongqing
PyTzFactory: Found CST using Asia/Urumqi
PyTzFactory: Found CST using Asia/Kashgar
PyTzFactory: Expanded timezone to 5 timezones
PyTzFactory: New offset 8:00:00 for Asia/Shanghai
PyTzFactory: Known offset 8:00:00 for Asia/Harbin
PyTzFactory: Known offset 8:00:00 for Asia/Chongqing
PyTzFactory: Known offset 8:00:00 for Asia/Urumqi
PyTzFactory: Known offset 8:00:00 for Asia/Kashgar
PyTzFactory: Have 1 distinct timezone(s)
PyTzFactory: Found Asia/Shanghai
...
SimpleDate('2013-07-04 18:53 CST')

наконец, чтобы ответить на заданный вопрос напрямую, он также обертывает tzlocal, как упоминается в другом ответе здесь, поэтому автоматически сделает то, что вы ожидаете, если вы не дадите часовой пояс. например, я живу в чили, поэтому

>>> SimpleDate()
SimpleDate('2013-07-04 19:21:25.757222 CLT', tz='America/Santiago')
>>> SimpleDate().tzinfo
<DstTzInfo 'America/Santiago' CLT-1 day, 20:00:00 STD>

дает мой часовой пояс локали (неоднозначный или нет).