Преобразование даты и времени в метку времени Unix и преобразование ее обратно в python

186

У меня есть dt = datetime(2013,9,1,11), и я хотел бы получить метку времени Unix этого объекта datetime.

Когда я это сделаю, (dt - datetime(1970,1,1)).total_seconds()я получу метку времени 1378033200.

При конвертации обратно с помощью datetime.fromtimestampя получил datetime.datetime(2013, 9, 1, 6, 0).

Час не совпадает. Что я здесь пропустил?

Бриджуотер
источник
1
В качестве примечания, если вы используете Python 3.3+, вы действительно хотите использовать этот timestampметод вместо того, чтобы пытаться сделать это самостоятельно. С одной стороны, это полностью исключает возможность вычитания наивных времен из разных часовых поясов.
abarnert
1
Откуда dtберутся? Это местное время или время в UTC?
JFS
2
@abarnert Я также хотел бы отправить запрос на очистку всех кодовых страниц и символов Unicode, то есть запретить все языки кодировки, не относящиеся к ascii и не по умолчанию. Картины символов все еще разрешены.
Даниэль Ф
6
@DanielF: Запрос отклонен. Я не заинтересован в создании здесь языка с одним миром, и даже если бы мы это сделали, я не хотел бы сделать жизнь невозможной для исторических лингвистов и ученых Толкина. Unicode уже решает проблему, за исключением того, что некоторые организации и продукты (консорциум TRON, японское программное обеспечение, использующее Shift-JIS поверх UTF-8, Microsoft по-прежнему поставляют ОС по умолчанию cp1252 для пользовательских текстовых файлов и различные SDK, которые претендуют на этот UTF -16 - кодировка фиксированной ширины и / или та же вещь, что и в Юникоде), должны быть наказаны, чтобы привести их в соответствие.
abarnert

Ответы:

106

То, что вы пропустили здесь, это часовые пояса.

Предположительно у вас пять часов свободного времени по UTC, поэтому 2013-09-01T11: 00: 00 по местному времени и 2013-09-01T06: 00: 00Z - это одно и то же время.

Вы должны прочитать верхнюю часть datetimeдокументации, которая объясняет часовые пояса и "наивные" и "осведомленные" объекты.

Если ваша первоначальная наивная дата-время была в формате UTC, utcfromtimestampвместо нее можно использовать способ восстановления fromtimestamp.

С другой стороны, если ваша первоначальная наивная дата-время была локальной, вы не должны были сначала вычесть из нее метку времени UTC; используйте datetime.fromtimestamp(0)вместо этого.

Или, если у вас был осведомленный объект datetime, вам нужно либо использовать локальную (осведомленную) эпоху с обеих сторон, либо явно конвертировать в и из UTC.

Если у вас есть Python 3.3 или более поздней версии или вы можете обновить его, вы можете избежать всех этих проблем, просто используя timestampметод, а не пытаясь понять, как это сделать самостоятельно. И даже если вы этого не сделаете, вы можете рассмотреть возможность заимствования его исходного кода .

(И если вы можете подождать Python 3.4, похоже, что PEP 341 , вероятно, войдет в финальную версию, а это означает, что все то, о чем мы с Дж. Ф. Себастьяном говорили в комментариях, должно быть выполнимо только с помощью stdlib, и работает одинаково на Unix и Windows.)

abarnert
источник
1
Если dtиспользуется местный часовой пояс, то datetime.fromtimestamp(0)вместо этого следует использовать формулу в вопросе (эпоха в текущем часовом поясе) datetime(1970, 1,1)(эпоха unix в UTC).
JFS
@JFSebastian: Я не хотел подробно раскрывать все три возможности, но, думаю, ты прав, я должен.
abarnert
Кстати, fromtimestamp(0)может произойти сбой, если система не хранит историческую информацию о часовом поясе, например, в Windows. pytzможет быть использован в этом случае.
Jfs
@JFSebastian: Но pytzэто не поможет, если вы уже не знаете, в каком часовом поясе вы находитесь; чтобы сделать это программно, вам нужна другая библиотека, которая получает текущий часовой пояс Windows и преобразует его в pytzчасовой пояс и / или ищет по имени.
abarnert
есть tzlocalмодуль, который также работает на Windows. Вот как вы можете конвертировать время UTC в местное время .
Jfs
141

решение

import time
import datetime
d = datetime.date(2015,1,5)

unixtime = time.mktime(d.timetuple())
DmitrySemenov
источник
12
Предполагается, что dэто наивный объект даты / даты и времени, представляющий местное время (он может дать сбой в течение неоднозначных или прошлых / будущих дат, если ОС не предоставляет исторический tz db (смещение UTC в прошлом могло быть другим в локальном часовой пояс)). Больше вариантов .
JFS
6
Похоже, никто не возражает против решения с плавающей точкой. Это очевидно для всех?
Иван Балашов
4
Это уменьшает микросекунды. Может быть интересно.
Alfe
Единственная проблема в том, что вы не учитываете часовой пояс.
Blairg23
это не должно Если вам нужно настроить TZ, вы должны сделать это в первую очередь.
Марцин Орловский
69

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

>>> import datetime
>>> datetime.datetime(2012,04,01,0,0).strftime('%s')
'1333234800'
>>> (datetime.datetime(2012,04,01,0,0) - datetime.datetime(1970,1,1)).total_seconds()
1333238400.0

В Python 3.3+ вы можете использовать timestamp()вместо:

>>> import datetime
>>> datetime.datetime(2012,4,1,0,0).timestamp()
1333234800.0
Франсиско Коста
источник
5
Не учитывает часовые пояса.
Blairg23
3
Вы не хотите использовать %s, так как он будет привязан к часам системы, в которой вы сейчас находитесь. Вы должны только когда-либо использовать, .timestamp()чтобы получить правильное время Epoch / UNIX.
Blairg23
3
%sне работает в системах Windows ( ValueError: Invalid format string)
chrki
1
@ amitnair92 просто положил 8или9
Франциско Коста
54

Вместо этого выражения для создания метки времени POSIX dt,

(dt - datetime(1970,1,1)).total_seconds()

Использовать это:

int(dt.strftime("%s"))

Я получаю правильный ответ в вашем примере, используя второй метод.

РЕДАКТИРОВАТЬ: Некоторое продолжение ... После некоторых комментариев (см. Ниже), мне было любопытно по поводу отсутствия поддержки или документации для %sв strftime. Вот что я нашел:

В исходном коде Python для datetimeи time, строка STRFTIME_FORMAT_CODESговорит нам:

"Other codes may be available on your platform.
 See documentation for the C library strftime function."

Так что теперь, если мы man strftime(в системах BSD, таких как Mac OS X), вы найдете поддержку для %s:

"%s is replaced by the number of seconds since the Epoch, UTC (see mktime(3))."

В любом случае, именно поэтому %sработает на системах, которые это делает. Но есть и более эффективные решения проблемы ОП (с учетом часовых поясов). Смотрите принятый ответ @ abarnert здесь.

Даррен Стоун
источник
4
Это недокументированное поведение (я считаю). Например, в Windows это приводит к «Неверной строке формата».
Crescent Fresh
@CrescentFresh, интересно. Вы можете быть правы. Хотя я не вижу strftime("%s")в документации, я только что подтвердил это для работы на Mac и Linux. Спасибо.
Даррен Стоун
1
по-видимому, он игнорирует поле tzinfo.
Даниэль Ф
1
@DarrenStone: вам не нужно читать источник. Обе time.strftime()и datetime.strftimeдокументация делегируют платформам strftime(3)функцию для неподдерживаемых директив. %sможет произойти сбой даже в Mac OS X, например, datetime.strftime ('% s') должен учитывать tzinfo .
Jfs
1
Вы никогда не должны использовать, %sпоскольку вы будете использовать только локализованное системное время системы, в которой вы находитесь. Вы хотите использовать, .timestamp()если вы пытаетесь получить реальную метку времени UNIX.
Blairg23
6

Для работы с часовыми поясами UTC:

time_stamp = calendar.timegm(dt.timetuple())

datetime.utcfromtimestamp(time_stamp)
Эмин Темиз
источник
После часа поисков, это был единственный ответ, который сработал: ')
Кураши,
4

Вы пропустили информацию о часовом поясе (уже ответили, согласились)

arrowупаковка позволяет избежать этой пытки с указанием даты; Он уже написан, протестирован, опубликован на pypi, кросс-Python (2.6 - 3.xx).

Все что вам нужно: pip install arrow(или добавить в зависимости)

Решение для вашего случая

dt = datetime(2013,9,1,11)
arrow.get(dt).timestamp
# >>> 1378033200

bc = arrow.get(1378033200).datetime
print(bc)
# >>> datetime.datetime(2013, 9, 1, 11, 0, tzinfo=tzutc())
print(bc.isoformat())
# >>> '2013-09-01T11:00:00+00:00'
maxkoryukov
источник
3

Если ваш объект datetime представляет время UTC, не используйте time.mktime, так как предполагается, что кортеж находится в вашем местном часовом поясе. Вместо этого используйте calendar.timegm:

>>> import datetime, calendar
>>> d = datetime.datetime(1970, 1, 1, 0, 1, 0)
>>> calendar.timegm(d.timetuple())
60
Мэтт Крамер
источник
1
def dt2ts(dt, utc=False):
    if utc:
        return calendar.timegm(dt.timetuple())
    if dt.tzinfo is None:
        return int(time.mktime(dt.timetuple()))
    utc_dt = dt.astimezone(tz.tzutc()).timetuple()
    return calendar.timegm(utc_dt)

Если вам нужна метка времени UTC: time.mktimeтолько для локального dt. Использование calendar.timegmбезопасно, но dt должна быть зоной utc, поэтому измените зону на utc. Если DT в UTC, просто используйте calendar.timegm.

wyx
источник
1
def datetime_to_epoch(d1):

    # create 1,1,1970 in same timezone as d1
    d2 = datetime(1970, 1, 1, tzinfo=d1.tzinfo)
    time_delta = d1 - d2
    ts = int(time_delta.total_seconds())
    return ts


def epoch_to_datetime_string(ts, tz_name="UTC"):
    x_timezone = timezone(tz_name)
    d1 = datetime.fromtimestamp(ts, x_timezone)
    x = d1.strftime("%d %B %Y %H:%M:%S")
    return x
rjha94
источник
0

Этот класс покрывает ваши потребности, вы можете передать переменную в ConvertUnixToDatetime и вызвать функцию, для которой вы хотите, чтобы она работала на основе.

from datetime import datetime
import time

class ConvertUnixToDatetime:
    def __init__(self, date):
        self.date = date

    # Convert unix to date object
    def convert_unix(self):
        unix = self.date

        # Check if unix is a string or int & proceeds with correct conversion
        if type(unix).__name__ == 'str':
            unix = int(unix[0:10])
        else:
            unix = int(str(unix)[0:10])

        date = datetime.utcfromtimestamp(unix).strftime('%Y-%m-%d %H:%M:%S')

        return date

    # Convert date to unix object
    def convert_date(self):
        date = self.date

        # Check if datetime object or raise ValueError
        if type(date).__name__ == 'datetime':
            unixtime = int(time.mktime(date.timetuple()))
        else:
            raise ValueError('You are trying to pass a None Datetime object')
        return type(unixtime).__name__, unixtime


if __name__ == '__main__':

    # Test Date
    date_test = ConvertUnixToDatetime(datetime.today())
    date_test = date_test.convert_date()
    print(date_test)

    # Test Unix
    unix_test = ConvertUnixToDatetime(date_test[1])
    print(unix_test.convert_unix())
cryptopotluck
источник