Перетворіть дату в часову позначку Unix і перетворіть її назад у python


186

У мене є dt = datetime(2013,9,1,11), і я хотів би отримати часову позначку Unix цього об’єкта datetime.

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

При перетворенні його назад за допомогою datetime.fromtimestampI got 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 - шафа фіксованої ширини і / або те саме, що і Unicode) повинні бути покарані, щоб привести їх у відповідність.
abarnert

Відповіді:


105

Що ви тут пропустили - це часові пояси.

Імовірно, у вас п’ять годин поза UTC, тому 2013-09-01T11: 00: 00 місцеві та 2013-09-01T06: 00: 00Z - це той самий час.

Вам потрібно прочитати верхню частину datetimeдокументів, де пояснюються часові пояси та «наївні» та «обізнані» об’єкти.

Якщо ваш початковий наївний час був UTC, спосіб відновити його - це використовувати utcfromtimestampзамість fromtimestamp.

З іншого боку, якщо ваш початковий наївний час був місцевим, ви не повинні віднімати від нього часову позначку UTC в першу чергу; використовувати datetime.fromtimestamp(0)замість цього.

Або якщо у вас був обізнаний об’єкт дати, вам потрібно або використовувати локальну (обізнану) епоху з обох сторін, або явно перетворити на UTC та з нього.

Якщо у вас є або ви можете оновити його до Python 3.3 або пізнішої версії, ви можете уникнути всіх цих проблем, просто скориставшись timestampметодом, а не намагаючись зрозуміти, як це зробити самостійно. І навіть якщо ви цього не зробите, ви можете розглянути можливість запозичення його вихідного коду .

(І якщо ви можете зачекати на Python 3.4, схоже, що PEP 341 , швидше за все, перетворить його на остаточний реліз, а це означає, що все те, про що я говорив у коментарях, і JF Себастьян слід виконувати лише за stdlib, і працює однаково і в Unix, і в Windows.)


1
якщо dtв локальному часовому поясі, то формула у питанні невірна datetime.fromtimestamp(0)(епоха в поточному часовому поясі) слід використовувати замість datetime(1970, 1,1)(епоха Unix у UTC).
jfs

@JFSebastian: Я не хотів детально висвітлювати всі три можливості, але, мабуть, ти маєш рацію, я повинен.
abarnert

btw, fromtimestamp(0)може вийти з ладу, якщо система не зберігає інформацію про часовий пояс, наприклад, у Windows. pytzможе бути використаний у цьому випадку.
jfs

@JFSebastian: Але pytzце не допомагає, якщо ви вже не знаєте, в якому часовому поясі перебуваєте; щоб це зробити програмно, вам потрібна інша бібліотека, яка отримує поточний часовий пояс Windows і перетворює його в pytzчасовий пояс і / або шукає його за назвою.
abarnert

є tzlocalмодуль, який також працює в Windows. Ось як можна перетворити utc time на місцевий час .
jfs

141

рішення є

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

unixtime = time.mktime(d.timetuple())

12
він передбачає, що dце наївний об'єкт дати / дати, який представляє місцевий час (він може вийти з ладу для неоднозначних часів або для минулих / майбутніх дат, якщо ОС не надає історичний tz db (зсув UTC міг би бути іншим у минулому на місцевому рівні) часовий пояс)). Більше варіантів .
jfs

6
Схоже, ніхто не заперечує рішення з плаваючою комою. Це очевидно для всіх?
Іван Балашов

4
Це скидає мікросекунди. Можливо, буде цікаво.
Альфе

Єдина проблема полягає в тому, що ви не враховуєте часовий пояс.
Blairg23

це не повинно. Якщо вам потрібно відрегулювати TZ, вам потрібно зробити це спочатку.
Марцін Орловський

69

Якщо ви хочете перетворити час дати пітона в секунди з епохи, слід зробити це явно:

>>> 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()лише точний час для отримання епохи / 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"))

Я отримую правильну відповідь у вашому прикладі, використовуючи другий метод.

EDIT: Деякі Followup ... Після того, як деякі коментарі (дивись нижче), мені було цікаво по приводу відсутності підтримки або документації %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 це призводить до "Недійсний рядок формату".
Півмісяць Свіжий

@CrescentFresh, цікаво. Можливо ти правий. Поки я не бачу strftime("%s")в документації, я просто підтвердив це для роботи на Mac та Linux. Дякую.
Даррен Стоун

1
мабуть, воно ігнорує поле tzinfo.
Даніель Ф

1
@DarrenStone: вам не потрібно читати джерело. Як time.strftime()і datetime.strftimeдокументація делегат на платформи strftime(3)працює для підтримуються директив. %sможе вийти з ладу навіть у Mac OS X, наприклад, datetime.strearch ('% s') має відповідати tzinfo .
jfs

1
Ніколи не слід використовувати, %sоскільки ви будете використовувати лише локалізований системний час системи, на якій ви перебуваєте. Ви хочете використовувати, .timestamp()якщо ви намагаєтеся отримати реальну мітку часу UNIX.
Blairg23

6

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

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

datetime.utcfromtimestamp(time_stamp)

Після години пошуків це була єдина відповідь, яка спрацювала: ')
Кураші

4

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

arrowпакет дозволяє уникнути цих тортур з датами; Це вже написано, протестовано, опубліковано pypi, крос-пітон (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'

3

Якщо ваш об'єкт дати представляє час UTC, не використовуйте time.mktime, оскільки він передбачає, що кортеж знаходиться у вашому локальному часовому поясі. Замість цього використовуйте Calendar.timegm:

>>> import datetime, calendar
>>> d = datetime.datetime(1970, 1, 1, 0, 1, 0)
>>> calendar.timegm(d.timetuple())
60

2

Добре, при перетворенні часової позначки TO unix, python в основному передбачає UTC, але при перетворенні назад він дасть вам дату, перетворену у ваш локальний часовий пояс.

Дивіться це питання / відповідь; Використовуйте часовий пояс, використовуючи datetime.datetime.fromtimestamp ()


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.


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

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())
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.