Получение правильного смещения часового пояса в Python с использованием локального часового пояса

Хорошо, позвольте мне сначала начать с того, что мой часовой пояс - CET/CEST. Точный момент, когда он изменяется от CEST до CET (от DST, который равен GMT + 2, до нормального, который GMT + 1, таким образом) всегда является последним воскресеньем октября в 3 часа ночи. В 2010 году это было 31 октября 3AM.

Теперь обратите внимание на следующее:

>>> import datetime
>>> import pytz.reference
>>> local_tnz = pytz.reference.LocalTimezone()
>>> local_tnz.utcoffset(datetime.datetime(2010, 10, 31, 2, 12, 30))
datetime.timedelta(0, 3600)

Это неправильно, как описано выше.

>>> local_tnz.utcoffset(datetime.datetime(2010, 10, 30, 2, 12, 30))
datetime.timedelta(0, 7200)
>>> local_tnz.utcoffset(datetime.datetime(2010, 10, 31, 2, 12, 30))
datetime.timedelta(0, 7200)

Теперь это внезапно правильно:/

Я знаю, что есть несколько вопросов об этом уже, но данное решение всегда "использует локализацию", но моя проблема здесь в том, что LocalTimezone не предоставляет этот метод.

Фактически, у меня есть несколько временных меток в миллисекундах, из которых мне нужен utcoffset локального часового пояса (не только мой, но и любой, кто использует программу). Один из них - 1288483950000 или Sun Oct 31 2010 02:12:30 GMT + 0200 (CEST) в моем часовом поясе.

В настоящее время я делаю следующее, чтобы получить объект datetime:

datetime.datetime.fromtimestamp(int(int(millis)/1E3)) 

и для получения utcoffset в минутах:

-int(local_tnz.utcoffset(date).total_seconds()/60)

который, к сожалению, во многих случаях ошибочен: (.

Любые идеи?

Примечание. Я использую python3.2.4, а не то, что в этом случае это имеет значение.

EDIT:

Нашел решение благодаря @JamesHolderness:

def datetimeFromMillis(millis):
    return pytz.utc.localize(datetime.datetime.utcfromtimestamp(int(int(millis)/1E3)))

def getTimezoneOffset(date):
    return -int(date.astimezone(local_tz).utcoffset().total_seconds()/60)

С local_tz, равным tzlocal.get_localzone() из модуля tzlocal.

Ответ 1

В соответствии с Wikipedia переход к летнему времени и из него происходит в 01:00 UTC.

  • В 00:12 UTC вы все еще находитесь в центральноевропейском летнем времени (т.е. UTC + 02: 00), поэтому местное время 02:12.

  • В 01:12 UTC вы вернулись в стандартное центральноевропейское время (т.е. UTC + 01: 00), поэтому местное время снова 02:12.

При переходе с летнего времени на стандартное время местное время идет с 02:59 до 02:00, и час повторяется. Поэтому, когда вы спрашиваете о смещении UTC в 02:12 (местное время), ответ может быть правдивым либо +01: 00, либо +02: 00 - это зависит от версии 02:12, о которой вы говорите.

При дальнейшем изучении библиотеки pytz, я думаю, ваша проблема может заключаться в том, что вы не должны использовать реализацию pytz.reference, которая может не справиться с этими двусмысленностями очень хорошо. Цитата из комментариев в исходном коде:

Справочные реализации tzinfo из документов Python. Используется для тестирования, поскольку они верны только для лет С 1987 по 2006. Не используйте их для реального кода.

Работа с неоднозначными временами в pytz

Что вы должны делать, так это создание объекта часового пояса для соответствующего часового пояса:

import pytz
cet = pytz.timezone('CET')

Затем вы можете использовать метод utcoffset для вычисления смещения UTC даты/времени в этом часовом поясе.

dt = datetime.datetime(2010, 10, 31, 2, 12, 30)
offset = cet.utcoffset(dt)

Обратите внимание, что приведенный выше пример будет вызывать исключение AmbiguousTimeError, потому что он не может определить, какую из двух версий 02:12:30 вы имели в виду. К счастью, pytz позволит вам указать, хотите ли вы версию dst или стандартную версию, установив параметр is_dst. Например:

offset = cet.utcoffset(dt, is_dst = True)

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

Как работать со отметками времени

Что касается обработки временных меток, лучше всего хранить их как значения UTC как можно дольше, иначе вы можете в конечном итоге выбросить ценную информацию. Поэтому сначала конвертируйте в datetime UTC с помощью метода datetime.utcfromtimestamp.

dt = datetime.datetime.utcfromtimestamp(1288483950)

Затем используйте pytz для локализации времени как UTC, поэтому часовой пояс привязан к объекту datetime.

dt = pytz.utc.localize(dt)

Наконец, вы можете конвертировать этот UTC datetime в свой часовой пояс и получить смещение временной зоны следующим образом:

offset = dt.astimezone(cet).utcoffset()

Обратите внимание, что этот набор вычислений приведет к правильным смещениям для 1288483950 и 1288487550, хотя обе отметки времени представлены в 02:12:30 в часовом поясе CET.

Определение локального часового пояса

Если вам нужно использовать локальный часовой пояс своего компьютера, а не фиксированный часовой пояс, вы не можете сделать это напрямую из pytz. Вы также не можете просто создать объект pytz.timezone, используя имя часового пояса от time.tzname, потому что имена не всегда будут распознаны pytz.

Решение заключается в использовании tzlocal module - его единственная цель - предоставить эту недостающую функциональность в pytz. Вы используете его следующим образом:

import tzlocal
local_tz = tzlocal.get_localzone()

Функция get_localzone() возвращает объект pytz.timezone, поэтому вы должны иметь возможность использовать это значение во всех местах, где я использовал переменную cet в приведенных выше примерах.

Ответ 2

Учитывая временную метку в миллисекундах, вы можете получить смещение utc для локального часового пояса, используя только stdlib:

#!/usr/bin/env python
from datetime import datetime

millis = 1288483950000
ts = millis * 1e-3
# local time == (utc time + utc offset)
utc_offset = datetime.fromtimestamp(ts) - datetime.utcfromtimestamp(ts)

Если мы игнорируем время вокруг секунд прыжка, тогда нет никакой двусмысленности или несуществующих времен.

Он поддерживает DST и изменения смещения utc по другим причинам, если ОС поддерживает исторический часовой пояс db, например, он должен работать на Ubuntu для любой прошлой/текущей даты, но может быть поврежден в Windows для прошлых дат, в которых используется другое смещение utc.

Здесь же, используя tzlocal модуль, который должен работать в системах * nix и Win32:

#!/usr/bin/env python
from datetime import datetime
from tzlocal import get_localzone # pip install tzlocal

millis = 1288483950000
ts = millis * 1e-3
local_dt = datetime.fromtimestamp(ts, get_localzone())
utc_offset = local_dt.utcoffset()

См. Как преобразовать datetime utc python в локальное datetime, используя только стандартную библиотеку python?

Чтобы получить смещение utc в минутах (Python 3.2 +):

from datetime import timedelta

minutes = utc_offset / timedelta(minutes=1)

Не используйте pytz.reference.LocalTimezone(), он предназначен только для тестов.