Получение правильного смещения часового пояса в Python с использованием местного часового пояса
Хорошо, позвольте мне сначала сказать, что мой часовой пояс CET/CEST. Точный момент, когда он меняется с CEST на CET (обратно от DST, который равен GMT+2, к нормальному, который, таким образом, GMT+1), всегда в последнее воскресенье октября в 3:00. В 2010 году это было 31 октября 3 утра.
Теперь обратите внимание на следующее:
>>> 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 или вс 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, но это не так важно.
РЕДАКТИРОВАТЬ:
Нашел решение благодаря @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.
4 ответа
Согласно Википедии, переход в летнее время и обратно происходит в 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 не повредит, даже если время не будет неоднозначным. Согласно документации, он используется только во время неоднозначных периодов перехода на летнее время для устранения этой неоднозначности.
Как бороться с метками времени
Что касается работы с метками времени, лучше всего хранить их как значения UTC как можно дольше, иначе вы в конечном итоге выбрасываете ценную информацию. Поэтому сначала преобразуйте дату в формате UTC с помощью метода datetime.utcfromtimestamp.
dt = datetime.datetime.utcfromtimestamp(1288483950)
Затем используйте pytz, чтобы локализовать время как UTC, чтобы часовой пояс был привязан к объекту datetime.
dt = pytz.utc.localize(dt)
Наконец, вы можете преобразовать дату и время UTC в местный часовой пояс и получить смещение часового пояса, например:
offset = dt.astimezone(cet).utcoffset()
Обратите внимание, что этот набор вычислений даст правильные смещения для 1288483950 и 1288487550, даже если обе временные метки представлены 02:12:30 в часовом поясе CET.
Определение местного часового пояса
Если вам нужно использовать локальный часовой пояс вашего компьютера, а не фиксированный часовой пояс, вы не сможете сделать это напрямую из pytz. Вы также не можете просто создать объект pytz.timezone, используя имя часового пояса из time.tzname, потому что имена не всегда будут распознаваться pytz.
Решение состоит в том, чтобы использовать модуль tzlocal - его единственная цель - предоставить эту недостающую функциональность в pytz. Вы используете это так:
import tzlocal
local_tz = tzlocal.get_localzone()
Функция get_localzone() возвращает объект pytz.timezone, поэтому вы должны иметь возможность использовать это значение во всех местах, где я использовал переменную cet в приведенных выше примерах.
Учитывая временную метку в миллисекундах, вы можете получить смещение 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 по другим причинам, если ОС поддерживает исторический дБ часового пояса, например, он должен работать в 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()
Чтобы получить смещение utc в минутах (Python 3.2+):
from datetime import timedelta
minutes = utc_offset / timedelta(minutes=1)
Не использовать pytz.reference.LocalTimezone()
, это только для испытаний.
import pytz, datetime
tz = timezone('CET')
tz.utcoffset(datetime.datetime.now()).total_seconds()
7200,0
other_datetime = "YYYY-mm-ddTHH:MM:SSother_offset"
T может быть пробелом или самим символом «T».
Other_offset обычно выражается как «+ЧЧММ» или «-ЧЧММ».
Затем вы просто делаете это:
- возьми свое
timezone offset := my_offset
-
my_datetime = other_datetime + minutes( my_offset - other_offset)
Помните, что значения my_offset иother_offset могут быть положительными или отрицательными.
Таким образом, вы можете сделать что-то вроде этого, например:
my_datetime = other_datetime + minutes( (-7:00) - (-8:00) ) = other_datetime + minutes(+1:00) = other_datetime + 60 minutes
и т. д.
Обратите внимание на двойное отрицание.