получить временную метку UTC в Python с datetime

83

Есть ли способ получить метку времени в формате UTC, указав дату? Чего я ожидал:

datetime(2008, 1, 1, 0, 0, 0, 0)

должен привести к

 1199145600

Создание наивного объекта datetime означает отсутствие информации о часовом поясе. Если я посмотрю документацию для datetime.utcfromtimestamp, создание метки времени в формате UTC означает отказ от информации о часовом поясе. Поэтому я предполагаю, что создание наивного объекта datetime (как и я) приведет к отметке времени в формате UTC. Тем не мение:

then = datetime(2008, 1, 1, 0, 0, 0, 0)
datetime.utcfromtimestamp(float(then.strftime('%s')))

приводит к

2007-12-31 23:00:00

Есть ли еще какая-либо информация о скрытых часовых поясах в объекте datetime? Что я делаю неправильно?

погладить
источник
проблема в том, then.strftime('%s')что ожидается местное время, но метка времени указывает, что datetime(2008, 1, 1)это время в формате UTC.
jfs

Ответы:

91

Наивный datetimeпротив осведомленногоdatetime

datetimeОбъекты по умолчанию называются «наивными»: они хранят информацию о времени без информации о часовом поясе. Думайте о наивности datetimeкак об относительном числе (например:) +4без четкого происхождения (на самом деле ваше происхождение будет общим для всей границы вашей системы).

Напротив, думайте о знании datetimeкак об абсолютных числах (например:) 8с общим происхождением для всего мира.

Без информации о часовом поясе вы не можете преобразовать "наивное" datetime в какое-либо ненативное представление времени (где же +4цели, если мы не знаем, с чего начать?). Вот почему у вас не может быть datetime.datetime.toutctimestamp()метода. (см. http://bugs.python.org/issue1457227 )

Чтобы проверить datetime dt, наивен ли ты , проверь dt.tzinfo, если None, то наивно:

datetime.now()        ## DANGER: returns naïve datetime pointing on local time
datetime(1970, 1, 1)  ## returns naïve datetime pointing on user given time

У меня наивное время свидания, что мне делать?

Вы должны сделать предположение в зависимости от вашего конкретного контекста: вопрос, который вы должны задать себе: был ли вы datetimeв UTC? или это было местное время?

  • Если вы использовали UTC (у вас нет проблем):

    import calendar
    
    def dt2ts(dt):
        """Converts a datetime object to UTC timestamp
    
        naive datetime will be considered UTC.
    
        """
    
        return calendar.timegm(dt.utctimetuple())
    
  • Если вы НЕ использовали UTC , добро пожаловать в ад.

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

    Вам понадобится название часового пояса и информация о том, действовало ли летнее время при создании целевого наивного datetime (последняя информация о летнем времени требуется для угловых случаев):

    import pytz     ## pip install pytz
    
    mytz = pytz.timezone('Europe/Amsterdam')             ## Set your timezone
    
    dt = mytz.normalize(mytz.localize(dt, is_dst=True))  ## Set is_dst accordingly
    

    Последствия непредоставленияis_dst :

    Если не использовать, is_dstбудет сгенерировано неправильное время (и временная метка UTC), если целевая дата-время была создана, когда было установлено обратное летнее время (например, изменение времени летнего времени путем удаления одного часа).

    Предоставление неверного is_dstзначения, конечно же, будет генерировать неправильное время (и временную метку UTC) только при перекрытии летнего времени или дырах. И, когда также указывается неправильное время, возникающее в «дырах» (время, которое никогда не существовало из-за перехода на летнее время вперед), is_dstдаст интерпретацию того, как рассматривать это фиктивное время, и это единственный случай, когда здесь .normalize(..)действительно что-то будет делать, так как затем он переведет его как действительное время (при необходимости изменив дату и время И объект DST). Обратите внимание, что .normalize()это не требуется для наличия правильной отметки времени в формате UTC в конце, но, вероятно, рекомендуется, если вам не нравится идея фиктивного времени в ваших переменных, особенно если вы повторно используете эту переменную в другом месте.

    и ИЗБЕГАЙТЕ СЛЕДУЮЩЕГО : (см. Преобразование часового пояса Datetime с использованием pytz )

    dt = dt.replace(tzinfo=timezone('Europe/Amsterdam'))  ## BAD !!
    

    Почему? потому что .replace()заменяет вслепую tzinfoбез учета целевого времени и выберет плохой объект DST. В то время как .localize()использует целевое время и вашу is_dstподсказку, чтобы выбрать правильный объект летнего времени.

СТАРЫЙ неправильный ответ (спасибо @JFSebastien за то, что поднял этот вопрос):

Надеюсь, что довольно легко угадать часовой пояс (ваше местное происхождение), когда вы создаете свой наивный datetimeобъект, поскольку он связан с конфигурацией системы, которую вы, надеюсь, НЕ измените между созданием наивного объекта datetime и моментом, когда вы хотите получить Отметка времени в формате UTC. Этот трюк можно использовать, чтобы задать неточный вопрос.

Используя, time.mktimeмы можем создать utc_mktime:

def utc_mktime(utc_tuple):
    """Returns number of seconds elapsed since epoch

    Note that no timezone are taken into consideration.

    utc tuple must be: (year, month, day, hour, minute, second)

    """

    if len(utc_tuple) == 6:
        utc_tuple += (0, 0, 0)
    return time.mktime(utc_tuple) - time.mktime((1970, 1, 1, 0, 0, 0, 0, 0, 0))

def datetime_to_timestamp(dt):
    """Converts a datetime object to UTC timestamp"""

    return int(utc_mktime(dt.timetuple()))

Вы должны убедиться, что ваш datetimeобъект создан в том же часовом поясе, что и тот, который создал ваш datetime.

Это последнее решение неверно, поскольку в нем предполагается, что смещение UTC от настоящего момента совпадает с смещением UTC от EPOCH. Что не так для многих часовых поясов (в конкретный момент года для смещений летнего времени (DST)).

вааб
источник
4
Наивный объект datetime всегда должен представлять время в формате UTC. Другие часовые пояса следует использовать только для ввода / вывода (отображения). В datetime.timestamp()Python 3.3 есть метод.
jfs
2
time.mktime()следует использовать только для местного времени. calendar.timegm()может использоваться для преобразования кортежа времени utc в метку времени posix. Или еще лучше использовать только методы datetime. Смотрите мой ответ
jfs
4
-1. В вашем коде предполагается, что utc_offset (сейчас) и utc_offset (эпоха) одинаковы в местном часовом поясе. Это не так в 116 часовых поясах (из 430 обычных часовых поясов).
jfs
1
по-прежнему -1: не используйте .replace()с часовым поясом с нефиксированным смещением utc, например 'Europe/Amsterdam'. См. Раздел «Преобразование часового пояса в дату и время с помощью pytz» .
jfs
1
1- Вы понимаете, почему нельзя использовать .replace(tzinfo=get_localzone())? 2- уже timegm()возвращается int. Не нужно оборачивать int. Также .timetuple()падает за доли секунды.
jfs
30

Другая возможность:

d = datetime.datetime.utcnow()
epoch = datetime.datetime(1970,1,1)
t = (d - epoch).total_seconds()

Это работает, поскольку и «d», и «epoch» являются наивными датами, делая оператор «-» действительным и возвращая интервал. total_seconds()превращает интервал в секунды. Обратите внимание, что total_seconds()возвращает число с плавающей запятой, дажеd.microsecond == 0

Крис Когдон
источник
12
Ну не совсем, идея та же, но эту легче понять :)
Натим
3
Можно подумать , что было бы один метод в библиотеке времени или что - то ... Sheesh
wordsforthewise
Чтобы получить метку времени, выполните datetime.datetime.utcnow (). Timestamp ()
Евгений
21

Также обратите внимание на функцию calendar.timegm (), описанную в этой записи блога:

import calendar
calendar.timegm(utc_timetuple)

Вывод должен соответствовать решению vaab.

Мистер мистер
источник
13

Если входной объект datetime находится в UTC:

>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> timestamp = (dt - datetime(1970, 1, 1)).total_seconds()
1199145600.0

Примечание: он возвращает число с плавающей запятой, т.е. микросекунды представлены как доли секунды.

Если объект даты ввода находится в формате UTC:

>>> from datetime import date
>>> utc_date = date(2008, 1, 1)
>>> timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * 24*60*60
1199145600

Дополнительные сведения см. В разделе « Преобразование datetime.date в метку времени в формате UTC в Python» .

jfs
источник
8

Я чувствую, что основной ответ все еще не так ясен, и стоит потратить время, чтобы понять время и часовые пояса .

Самое важное, что нужно понимать, имея дело со временем, - это то, что время относительно !

  • 2017-08-30 13:23:00: (наивное datetime), представляет местное время где-то в мире, но учтите, что 2017-08-30 13:23:00в Лондоне НЕ ТО ЖЕ ВРЕМЯ, как 2017-08-30 13:23:00в Сан-Франциско.

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

UTC метка времени представляет собой число в секундах (или миллисекундах) от эпохи (определяется как 1 January 1970 00:00:00при GMTвременной зоны +00: 00 смещение).

Эпоха привязана к часовому поясу GMT и, следовательно, является абсолютной точкой времени. UTC метка времени будучи смещение от абсолютного времени , следовательно , определяет абсолютную точку во времени .

Это дает возможность упорядочивать события по времени.

Без информации о часовом поясе время является относительным и не может быть преобразовано в абсолютное понятие времени без указания некоторого указания на то, к какому часовому поясу следует привязать наивное datetime.

Какие типы времени используются в компьютерной системе?

  • наивное datetime : обычно для отображения в местном времени (то есть в браузере), где ОС может предоставить программе информацию о часовом поясе.

  • Метки времени в формате UTC : метка времени в формате UTC - это абсолютная точка времени, как упоминалось выше, но она привязана к заданному часовому поясу, поэтому метка времени в формате UTC может быть преобразована в дату и время в любом часовом поясе, однако она не содержит информации о часовом поясе. Что это значит? Это означает, что 1504119325 соответствует 2017-08-30T18:55:24Zили 2017-08-30T17:55:24-0100также 2017-08-30T10:55:24-0800. Он не сообщает вам, откуда записана дата и время. Обычно он используется на стороне сервера для записи событий (журналов и т. Д.) Или используется для преобразования даты и времени с учетом часового пояса в абсолютный момент времени и вычисления разницы во времени .

  • Строка даты и времени ISO-8601 : ISO-8601 - это стандартизированный формат для записи даты и времени с часовым поясом. (На самом деле это несколько форматов, читайте здесь: https://en.wikipedia.org/wiki/ISO_8601 ) Он используется для передачи информации о дате и времени с учетом часовых поясов сериализуемым образом между системами.

Когда какой использовать? или, скорее, когда вам нужно заботиться о часовых поясах?

  • Если вам нужно каким-либо образом заботиться о времени суток , вам нужна информация о часовом поясе. Для календаря или будильника необходимо время дня, чтобы назначить встречу в правильное время дня для любого пользователя в мире. Если эти данные сохраняются на сервере, серверу необходимо знать, какому часовому поясу соответствует datetime.

  • Для вычисления разницы во времени между событиями, происходящими из разных мест в мире, метки времени в формате UTC достаточно, но вы теряете возможность анализировать, в какое время суток происходили события (например, для веб-аналитики вы можете захотеть узнать, когда пользователи приходят к вам сайт в их местное время : вы видите больше пользователей утром или вечером делать вы не можете понять , что без информации о времени суток.

Смещение часового пояса в строке даты :

Еще один важный момент: смещение часового пояса в строке даты не фиксировано . Это означает, что если 2017-08-30T10:55:24-0800указано смещение -0800или 8 часов назад, это не означает, что так будет всегда!

Летом вполне может быть летнее время, и было бы -0700

Это означает, что смещение часового пояса (+0100) не совпадает с названием часового пояса (Европа / Франция) или даже с обозначением часового пояса (CET).

America/Los_Angelestimezone - это место в мире , но оно превращается в PST(Тихоокеанское стандартное время) смещение часового пояса зимой и в PDT(Тихоокеанское летнее время) летом.

Таким образом, помимо получения смещения часового пояса из строки даты, вы также должны получить точное имя часового пояса.

Большинство пакетов смогут самостоятельно конвертировать числовые смещения из летнего времени в стандартное, но это не обязательно тривиально с простым смещением. Например, WATобозначение часового пояса в Западной Африке - UTC + 0100, как и CETчасовой пояс во Франции, но во Франции используется летнее время, а в Западной Африке - нет (потому что они близки к экватору).

Короче говоря, это сложно. ОЧЕНЬ сложно, и поэтому вам не следует делать это самостоятельно, а доверять пакету, который сделает это за вас, и ПОДДЕРЖИВАЙТЕ ЭТО ВОВРЕМЕННО!

MrE
источник
см мой пост в блоге о дате и времени в Python , чтобы понять ловушки различных пакетов medium.com/@eleroy/...
MRE
3

Простое решение без использования внешних модулей:

from datetime import datetime, timezone

dt = datetime(2008, 1, 1, 0, 0, 0, 0)
int(dt.replace(tzinfo=timezone.utc).timestamp())
Майк Сёмкин
источник
1

Я думаю, что правильный способ сформулировать ваш вопрос Is there a way to get the timestamp by specifying the date in UTC?, потому что временная метка - это просто число, которое является абсолютным, а не относительным. Относительный (или с учетом часового пояса) фрагмент - это дата.

Я считаю панд очень удобными для временных меток, поэтому:

import pandas as pd
dt1 = datetime(2008, 1, 1, 0, 0, 0, 0)
ts1 = pd.Timestamp(dt1, tz='utc').timestamp()
# make sure you get back dt1
datetime.utcfromtimestamp(ts1)  
Итамар Кац
источник
Использование pandas - это ИМХО правильный подход, для текущего времени также есть t = Timestamp.utcnow (), чтобы напрямую узнать правильное время :)
ntg
0

Принятый ответ, похоже, не работает для меня. Мое решение:

import time
utc_0 = int(time.mktime(datetime(1970, 01, 01).timetuple()))
def datetime2ts(dt):
    """Converts a datetime object to UTC timestamp"""
    return int(time.mktime(dt.utctimetuple())) - utc_0
мечтательное облако
источник
он не выполняется, если dtсмещение текущего ( ) UTC местного часового пояса и 1970 отличается. mktime()ожидает местное время.
jfs
0

Самый простой способ:

>>> from datetime import datetime
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> dt.strftime("%s")
'1199163600'

Изменить: @Daniel верен, это преобразует его в часовой пояс машины. Вот исправленный ответ:

>>> from datetime import datetime, timezone
>>> epoch = datetime(1970, 1, 1, 0, 0, 0, 0, timezone.utc)
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0, timezone.utc)
>>> int((dt-epoch).total_seconds())
'1199145600'

Фактически, это даже не обязательно указывать timezone.utc, потому что разница во времени одинакова, если оба datetimeимеют один и тот же часовой пояс (или без часового пояса).

>>> from datetime import datetime
>>> epoch = datetime(1970, 1, 1, 0, 0, 0, 0)
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> int((dt-epoch).total_seconds())
1199145600
Майк Ферлендер
источник
этот метод не возвращает utc, а скорее настраивает дату и время на текущий часовой пояс. т.е. если сейчас 12 утра в tz +3, он вернется в эпоху 9 утра.
Даниэль Дубовски
Ах ты прав. Мой часовой пояс был UTC - вот почему он работал.
Майк Ферлендер,
Если вы собираетесь использовать (Python 3.2 и новее) timezone.utcобъект, а затем просто использовать это с .timestamp(): datetime(2008, 1, 1, tzinfo=timezone.utc).timestamp(). Не нужно создавать объект эпохи и вычитать ..
Мартейн Питерс