Python преобразует строку в локальное время в метку времени эпохи UTC

У меня есть строки в формате YMD hms, у которых был удален часовой пояс. Но я знаю, что они в восточном времени с переходом на летнее время.

Я пытаюсь преобразовать их в метки времени эпохи UTC.

Я написал следующую функцию:

def ymdhms_timezone_dst_to_epoch(input_str,  tz="US/Eastern"):
    print(input_str)
    dt = datetime.datetime.fromtimestamp(time.mktime(time.strptime(input_str,'%Y-%m-%d %H:%M:%S')))
    local_dt = pytz.timezone(tz).localize(dt)
    print(local_dt.strftime('%Y-%m-%d %H:%M:%S %Z%z'))
    utc_dt = local_dt.astimezone(pytz.utc)
    print(utc_dt.strftime('%Y-%m-%d %H:%M:%S %Z%z'))    
    e = int(utc_dt.strftime("%s"))
    print(e)
    return e

Given string `2015-04-20 21:12:07` this prints:

    2015-04-20 21:12:07
    2015-04-20 21:12:07 EDT-0400 #<- so far so good?
    2015-04-21 01:12:07 UTC+0000 #<- so far so good?
    1429596727

который выглядит хорошо до отметки времени эпохи. Но http://www.epochconverter.com/epoch/timezones.php?epoch=1429596727 говорит, что оно должно соответствовать среднему времени по Гринвичу 21 апреля 2015 года 06:12:07 UTC.

Что случилось?

3 ответа

Решение

У меня есть строки в формате YMD hms, у которых был удален часовой пояс. Но я знаю, что они в восточном времени с переходом на летнее время.

Портативный способ заключается в использовании pytz:

#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz

naive_dt = datetime.strptime('2015-04-20 21:12:07', '%Y-%m-%d %H:%M:%S')
tz = pytz.timezone('US/Eastern')
eastern_dt = tz.normalize(tz.localize(naive_dt))
print(eastern_dt)
# -> 2015-04-20 21:12:07-04:00

Я пытаюсь преобразовать их в метки времени эпохи UTC.

timestamp = (eastern_dt - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()
# -> 1429578727.0

См. Преобразование datetime.date в метку времени UTC в Python.


В вашем коде есть несколько проблем:

  • time.mktime() может дать неверный результат в течение неоднозначного времени ввода (50% вероятности), например, при переходе DST "откат" в падении

  • time.mktime() а также datetime.fromtimestamp() может потерпеть неудачу для прошлых / будущих дат, если у них нет доступа к базе данных исторических часовых поясов в системе (особенно Windows)

  • localize(dt) может возвращать неверный результат в течение неоднозначного или несуществующего времени, т. е. во время переходов DST. Если вы знаете, что время соответствует летнему времени, используйте is_dst=True, tz.normalize() Здесь необходимо настроить возможное несуществующее время на входе

  • utc_dt.strftime("%s") не является переносимым и не относится к объекту tzinfo. Он интерпретирует ввод как местное время, т. Е. Возвращает неверный результат, если только ваш местный часовой пояс не является UTC.


Могу ли я просто установить is_dst = True?

Вы можете, если вы не против получить неточные результаты для неоднозначных или несуществующих времен, например, есть переход на летнее время в часовом поясе Fall in America/New_York:

>>> from datetime import datetime
>>> import pytz # $ pip install pytz
>>> tz = pytz.timezone('America/New_York')
>>> ambiguous_time = datetime(2015, 11, 1, 1, 30)
>>> time_fmt = '%Y-%m-%d %H:%M:%S%z (%Z)'
>>> tz.localize(ambiguous_time).strftime(time_fmt)
'2015-11-01 01:30:00-0500 (EST)'
>>> tz.localize(ambiguous_time, is_dst=False).strftime(time_fmt) # same
'2015-11-01 01:30:00-0500 (EST)'
>>> tz.localize(ambiguous_time, is_dst=True).strftime(time_fmt) # different
'2015-11-01 01:30:00-0400 (EDT)'
>>> tz.localize(ambiguous_time, is_dst=None).strftime(time_fmt) 
Traceback (most recent call last):
...
pytz.exceptions.AmbiguousTimeError: 2015-11-01 01:30:00

Часы возвращаются в 2 часа ночи. в первое воскресенье ноября:

часы повернуты назад

is_dst Флаг устранения неоднозначности может иметь три значения:

  • False - по умолчанию, допустим зимнее время
  • True - предположим, летнее время
  • None - поднять исключение для неоднозначных / несуществующих времен.

is_dst значение игнорируется для существующих уникальных локальных времен.

Вот график из PEP 0495 - устранение неоднозначности по местному времени, который иллюстрирует переход по летнему времени: UTC против местного времени в сгибе

Местное время повторяется дважды в сгибе (летнее время - до сгиба, зимнее время - после).

Чтобы иметь возможность автоматически устранять неоднозначность местного времени, вам нужна дополнительная информация, например, если вы читаете серию местного времени, то может помочь, если вы знаете, что они отсортированы: анализ упорядоченных временных меток по местному времени (в UTC) при наблюдении Летнее время.

Прежде всего '%s' не поддерживается на всех платформах, на самом деле это работает для вас, потому что ваша библиотека C платформы strftime() функция (которая вызывается Python) поддерживает это. Скорее всего, именно эта функция и вызывает проблему. Я предполагаю, что она не знает о часовом поясе, поэтому, принимая разницу с временем эпохи, она использует местный часовой пояс, который, скорее всего, EST(?)

Вместо того чтобы полагаться на '%s', который работает только на нескольких платформах (я полагаю, linux), вы должны вручную вычесть дату и время из эпохи (1970/1/1 00:00:00), чтобы получить фактические секунды с эпохи. Пример -

e = (utc_dt - datetime.datetime(1970,1,1,0,0,0,tzinfo=pytz.utc)).total_seconds()

Демо -

>>> (utc_dt - datetime.datetime(1970,1,1,0,0,0,tzinfo=pytz.utc)).total_seconds()
1429578727.0

Это правильно соответствует дате и времени, которое вы получите.

Я точно не знаю, почему, но вы должны удалить информацию о часовом поясе из вашего utc_dt Перед использованием %s распечатать это.

e = int(utc_dt.replace(tzinfo=None).strftime("%s"))
print(e)
return e
Другие вопросы по тегам