2009-09-04 2 views
33

У меня возникли какие-то странные проблемы с функцией .localize() pytz. Иногда это не будет вносить изменения в локализованной DateTime:pytz localize vs datetime replace

.localize поведение:

>>> tz 
<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD> 
>>> d 
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421) 

>>> tz.localize(d) 
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421, 
        tzinfo=<DstTzInfo 'Africa/Abidjan' GMT0:00:00 STD>) 
>>> tz.normalize(tz.localize(d)) 
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421, 
        tzinfo=<DstTzInfo 'Africa/Abidjan' GMT0:00:00 STD>) 

Как вы можете видеть, время не было изменено в результате локализовать/нормализовать операции. Однако, если используется .Надеть:

>>> d.replace(tzinfo=tz) 
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421, 
        tzinfo=<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD>) 
>>> tz.normalize(d.replace(tzinfo=tz)) 
datetime.datetime(2009, 9, 2, 15, 1, 42, 91421, 
        tzinfo=<DstTzInfo 'Africa/Abidjan' GMT0:00:00 STD>) 

Который, кажется, делает корректив в DateTime.

Вопрос - это правильно и почему другие ошибаются?

+0

относящийся: [Datetime Timezone conversion using pytz] (http://stackoverflow.com/q/27531718/4279) – jfs

ответ

27

localize только предполагает, что наивное datetime, которое вы передаете, является «правильным» (за исключением того, что вы не знаете о часовом поясе!), И поэтому просто устанавливает часовой пояс, никаких других настроек.

Вы можете (и это желательно ...) внутренне работать в UTC (а не с наивными DateTimes) и использовать replace, когда нужно выполнить ввод/вывод DateTimes в локализованном пути (normalize будет обрабатывать DST и т.п.).

+1

цитата Alex за предложение использовать UTC и локализовать/delocalize во время операций ввода-вывода. Могу ли я предложить, что это не рекомендуется, но настоятельно рекомендуется (читать обязательно)! – DrFalk3n

+2

ОП задал вопрос о различии между 'localize' и' replace'. Не «заменяет» также просто устанавливает часовой пояс, не делая никаких других настроек? Если да, то почему существуют различия между этими двумя результатами? –

+5

@MichaelWaterfall: 'pytz.timezone()' может соответствовать нескольким объектам tzinfo (одно и то же место, разные смещения UTC, сокращения по часовой стрелке). 'tz.localize (d)' пытается найти правильный tzinfo для данного локального времени 'd' (некоторое местное время неоднозначно или не существует). 'replace()' просто устанавливает любую (случайную) информацию о часовом поясе pytz по умолчанию, не учитывая данную дату (LMT в последних версиях). 'tz.normalize()' может корректировать время, если 'd' является несуществующим местным временем, например, временем перехода DST весной (северное полушарие), иначе оно ничего не делает в этом случае. – jfs

4

Этот класс DstTzInfo используется для часовых поясов, где смещение от UTC изменяется в определенные моменты времени. Например, как вы, вероятно, знаете, во многих местах переход к «летнему времени» в начале лета, а затем к «стандартному времени» в конце лета. Каждый экземпляр DstTzInfo представляет только один из этих часовых поясов, но методы «локализовать» и «нормализовать» помогают получить правильный экземпляр.

Для Абиджане, есть только когда-либо был один переход (по pytz), и это было в 1912 году:

>>> tz = pytz.timezone('Africa/Abidjan') 
>>> tz._utc_transition_times 
[datetime.datetime(1, 1, 1, 0, 0), datetime.datetime(1912, 1, 1, 0, 16, 8)] 

Объект TZ мы получаем из pytz представляет собой предварительно 1912 часовой пояс:

>>> tz 
<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD> 

Теперь, глядя на ваших два примеров видно, что при вызове tz.localize (г) вы НЕ получить это до 1912 часового пояса добавляется к вашему наивному объекту DateTime. Он предполагает, что объект datetime, который вы указываете ему, представляет собой локальное время в подходящий временной отрегь на это местное время, что является часовым поясом после 1912 года.

Однако в вашем втором примере, использующем d.replace (tzinfo = tz), требуется, чтобы ваш объект datetime отображал время в часовом поясе до 1912 года. Вероятно, это не то, что вы имели в виду. Затем, когда вы вызываете dt.normalize, он преобразует это в часовой пояс, который является правильным для этого значения datetime, то есть часовой пояс после 1912 года.

6

Я понимаю, что я немного опаздываю на это ... , но вот что я нашел, чтобы работать хорошо. Работа в UTC как Алекс заявил:

tz = pytz.timezone('Africa/Abidjan') 
now = datetime.datetime.utcnow() 

Тогда для локализации:

tzoffset = tz.utcoffset(now) 
mynow = now+tzoffset 

И этот метод делает обрабатывать DST совершенно

+1

неверно, если '.utcoffset()' метод ожидает datetime в локальном часовом поясе, который отличается от utc. Чтобы получить текущее время в локальном часовом поясе: 'now = datetime.now (tz)'. Чтобы преобразовать время utc в локальный часовой пояс: 'dt = utc_dt.replace (tzinfo = pytz.utc) .astimezone (tz)' (вам не нужно '.localize()', '.normalize() здесь). – jfs

+0

Это также может быть неверно, если смещение пересекает границу DST – paolov

1

localize является правильной функцией, чтобы использовать для создания даты и времени, знающих объекты с начальное фиксированное значение даты и времени. Результирующий объект, поддерживающий дату и время, будет иметь исходное значение datetime. На мой взгляд, очень распространенный шаблон использования, и тот, который, возможно, pytz может лучше документировать.

replace(tzinfo = ...), к сожалению, назван. Это функция, которая является случайной по своему поведению. Я бы посоветовал не использовать эту функцию для установки часовых поясов, если вам не нравятся боли, вызванные самим собой. Я уже достаточно пострадал от использования этой функции.