2013-07-18 11 views
12

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

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

>>> 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, не то, что в этом случае это имеет значение.

РЕДАКТИРОВАТЬ:

нашел решение благодаря @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.

+0

Используя Python 2.7.5 и pytz 2013b, я получаю разные и последовательные результаты: 'datetime.timedelta (0, 3600)'. 'datetime.timedelta (0, 7200)' и 'datetime.timedelta (0, 3600)' соответственно. Я поставил свой часовой пояс на CET перед началом теста. – martineau

+1

Попробуйте вручную перейти через строки в utcoffset и _isdst на cli, см. Код здесь: http://bazaar.launchpad.net/~stub/pytz/devel/view/head:/src/pytz/reference .py Посмотрите, видите ли вы что-то не так. – ojs

+0

Связанный: [Получение UTC UTC в Python] (http://stackoverflow.com/q/3168096/4279) – jfs

ответ

18

Согласно Wikipedia, переход на летнее время и обратно происходит в 01:00 UTC.

  • В 00:12 UTC вы все еще в Центральной Европе летнего времени (т.е. UTC + 02: 00), поэтому местное время 2: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, пока это возможно, в противном случае вы потенциально в конечном итоге выбрасывая ценную информацию. Поэтому сначала конвертируйте в UTC datetime с методом 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, так что вы должны быть в состоянии использовать это значение во всех местах я использовал СЕТ переменная в приведенных выше примерах.

+0

Я использовал последнюю версию, и результат был также неправильным для 01:12 CE (S) T на в тот же день, поэтому он не имеет ничего общего с двусмысленностью даты. Также обычно принято считать DST один в эти переходные периоды, JavaScript делает это так. –

+1

Мне любопытно, что заставляет вас думать, что есть какое-либо соглашение в отношении этих переходных периодов. На моем компьютере реализация javascript отличается от одного браузера к другому. Попробуйте ввести 'new Date (2010, 9, 31, 2, 12, 30, 0) .getTime()' в консоль вашего браузера. В Chrome я получаю 1288487550000 (т. Е. 01:12 UTC). В Firefox я получаю 1288483950000 (т. Е. 00:12 UTC). –

+0

Попробуйте с новой датой (1288483950000), дает мне «Sun Oct 31 2010 02:12:30 GMT + 0200» для Firefox и Chrome. –

6

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

См How to convert a python utc datetime to a local datetime using only python standard library?

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

from datetime import timedelta 

minutes = utc_offset/timedelta(minutes=1) 

Не использовать pytz.reference.LocalTimezone(), it is only for tests.

Смежные вопросы