Перетворення datetime.date в часову позначку UTC в Python


295

Я маю справу з датами в Python, і мені потрібно конвертувати їх у часові позначки UTC, щоб використовуватись у Javascript. Наступний код не працює:

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(time.mktime(d.timetuple()))
datetime.datetime(2010, 12, 31, 23, 0)

Перетворення об'єкта дати спочатку в дату також не допомагає. Я спробував приклад за цим посиланням , але:

from pytz import utc, timezone
from datetime import datetime
from time import mktime
input_date = datetime(year=2011, month=1, day=15)

а тепер або:

mktime(utc.localize(input_date).utctimetuple())

або

mktime(timezone('US/Eastern').localize(input_date).utctimetuple())

справді працює.

Отже, загальне питання: як я можу отримати дату, перетворену на секунди з епохи відповідно до UTC?



29
Я не впевнений, що згоден би позначити це як дублікат. Хоча рішення подібні, питань немає. Один (цей) намагається створити часову позначку з datetime.date, інший намагається перетворити рядкове представлення з одного часового поясу в інший. Оскільки хтось шукає рішення цієї проблеми, я не можу зробити висновок, що останній дасть відповідь, яку я шукаю.
DRH

5
datetime (date.year, date.month, date.day) .timestamp ()
Джуліан

Відповіді:


462

Якщо d = date(2011, 1, 1)в UTC:

>>> from datetime import datetime, date
>>> import calendar
>>> timestamp1 = calendar.timegm(d.timetuple())
>>> datetime.utcfromtimestamp(timestamp1)
datetime.datetime(2011, 1, 1, 0, 0)

Якщо dв локальному часовому поясі:

>>> import time
>>> timestamp2 = time.mktime(d.timetuple()) # DO NOT USE IT WITH UTC DATE
>>> datetime.fromtimestamp(timestamp2)
datetime.datetime(2011, 1, 1, 0, 0)

timestamp1і timestamp2може відрізнятися, якщо опівночі в локальному часовому поясі не є тим самим екземпляром часу, як півночі в UTC.

mktime()може повернути неправильний результат, якщо dвідповідає неоднозначному місцевому часу (наприклад, під час переходу DST) або якщо dє минулою (майбутньою) датою, коли зміщення utc могло бути іншим і C mktime()не має доступу до бази даних tz на даній платформі . Ви можете використовувати pytzмодуль (наприклад, через tzlocal.get_localzone()), щоб отримати доступ до бази даних tz на всіх платформах . Крім того, він utcfromtimestamp()може вийти з ладу і mktime()може повернути часові позначки, що не є POSIX, якщо "right"використовується часовий пояс .


Для перетворення datetime.dateоб'єкта, що представляє дату в UTC, без calendar.timegm():

DAY = 24*60*60 # POSIX day in seconds (exact value)
timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * DAY
timestamp = (utc_date - date(1970, 1, 1)).days * DAY

Як я можу отримати дату, перетворену на секунди з епохи відповідно до UTC?

Перетворити datetime.datetime(не datetime.date) об'єкт, який уже представляє час у UTC, у відповідну часову позначку (a float) POSIX .

Python 3.3+

datetime.timestamp():

from datetime import timezone

timestamp = dt.replace(tzinfo=timezone.utc).timestamp()

Примітка. Необхідно timezone.utcчітко вказати інше .timestamp()припущення, що ваш наївний об'єкт часу в локальному часовому поясі.

Пітон 3 (<3.3)

З документів для datetime.utcfromtimestamp():

Немає способу отримання позначки часу від екземпляра дати, але POSIX часу, який відповідає dt екземпляра дати, може бути легко обчислений наступним чином. Для наївного дт:

timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)

І для обізнаного dt:

timestamp = (dt - datetime(1970,1,1, tzinfo=timezone.utc)) / timedelta(seconds=1)

Цікаве прочитання: Час епохи проти часу доби на різницю між Часом? і скільки секунд минуло?

Дивіться також: datetime потребує методу "епохи"

Пітон 2

Щоб адаптувати вищевказаний код для Python 2:

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

де timedelta.total_seconds()еквівалентно (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6обчисленому з увімкненим істинним поділом.

Приклад

from __future__ import division
from datetime import datetime, timedelta

def totimestamp(dt, epoch=datetime(1970,1,1)):
    td = dt - epoch
    # return td.total_seconds()
    return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6 

now = datetime.utcnow()
print now
print totimestamp(now)

Остерігайтеся питань з плаваючою комою .

Вихідні дані

2012-01-08 15:34:10.022403
1326036850.02

Як перетворити обізнаний datetimeоб'єкт у часову позначку POSIX

assert dt.tzinfo is not None and dt.utcoffset() is not None
timestamp = dt.timestamp() # Python 3.3+

На Python 3:

from datetime import datetime, timedelta, timezone

epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
timestamp = (dt - epoch) / timedelta(seconds=1)
integer_timestamp = (dt - epoch) // timedelta(seconds=1)

На Python 2:

# utc time = local time              - utc offset
utc_naive  = dt.replace(tzinfo=None) - dt.utcoffset()
timestamp = (utc_naive - datetime(1970, 1, 1)).total_seconds()

2
Для випадку Python 2, чому б не timestamp = (dt - datetime.fromtimestamp(0)).total_seconds():?
m01

7
@ m01: явніше datetime(1970, 1, 1). fromtimestamp()тут неправильно ( dtзнаходиться в UTC, тому його utcfromtimestamp()слід використовувати замість).
jfs

1
@fedorqui подивіться на totimestamp()функцію у відповіді.
jfs

14
наскільки я люблю python, його дата-час lib серйозно зламане.
Realfun

3
@Realfun: часові зони, переходи DST, база даних tz, високосні секунди існують незалежно від Python.
jfs

81

Тільки для систем Unix :

>>> import datetime
>>> d = datetime.date(2011,01,01)
>>> d.strftime("%s")  # <-- THIS IS THE CODE YOU WANT
'1293832800'

Примітка 1: запаморочення зауважило, що це стосується локалізованих часових поясів . Не використовуйте у виробництві.

Примітка 2: Якуб Нарбський зазначав, що це ігнорує інформацію про часовий пояс навіть для встановленого за змістом дати (тестоване на Python 2.7).


4
Хороша відповідь, якщо ви знаєте, під якою системою працюватиме ваш код, але важливо зауважити, що рядок формату '% s' існує не на всіх ОС, тому цей код не є портативним. Якщо ви хочете перевірити, чи підтримується він, ви можете перевірити man strftimeUnix-подібні системи.
Аліса Хітон

5
Слід зазначити, що це скидає частину дробових секунд (мікросекунд чи що завгодно).
Альфе

11
УВАГА: Це стосується локалізованих часових поясів! Ви отримаєте різну поведінку стриллінгу для однакових об'єктів дати, залежно від часу роботи машини
запаморочення

3
Більше того, це ігнорує інформацію про часовий пояс і припускає, що час у локальному часовому поясі навіть для встановленого часу, встановленого зсувом, з встановленим tzinfo. Ну, принаймні, в Python 2.7.
Якуб Нарубський

1
javascript timestamp = мітка часу python * 1000
Trinh Hoang Nhu

38
  • Припущення 1: Ви намагаєтесь перетворити дату в часову позначку, однак, оскільки дата охоплює 24 години, не існує жодної часової позначки, яка б представляла цю дату. Я припускаю, що ви хочете представити часову позначку цієї дати опівночі (00: 00: 00.000).

  • Припущення 2: Дату, яку ви представляєте, не пов’язано з певним часовим поясом, проте ви хочете визначити зміщення від певного часового поясу (UTC). Не знаючи часового поясу, в якому знаходиться дата, неможливо розрахувати часову позначку для конкретного часового поясу. Я припускаю, що ви хочете ставитись до дати так, ніби вона знаходиться в часовому поясі локальної системи.

По-перше, ви можете перетворити екземпляр дати в кортеж, що представляє різні компоненти часу за допомогою timetuple()члена:

dtt = d.timetuple() # time.struct_time(tm_year=2011, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=1, tm_isdst=-1)

Потім ви можете перетворити його в часову позначку, використовуючи time.mktime:

ts = time.mktime(dtt) # 1293868800.0

Ви можете перевірити цей метод, протестувавши його з самим часом епохи (1970-01-01), і в цьому випадку функція повинна повернути зміщення часового поясу для локального часового поясу на цю дату:

d = datetime.date(1970,1,1)
dtt = d.timetuple() # time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=-1)
ts = time.mktime(dtt) # 28800.0

28800.0 становить 8 годин, що було б правильним для тихоокеанського часового поясу (де я перебуваю).


Я не впевнений, чому це було знято. Він вирішує питання ОП (робочий код, який ОП позначає як "не працює"). Він не відповідає на моє запитання (конвертувати UTC datetime.datetime у часову позначку), але все-таки… оновлення.
Танатос

9
Будь обережний. Припущення про те, що "ви хочете поводитись із датою так, ніби вона є в часовій зоні локальної системи, є коренем усіх проблем із часовим поясом у python. Якщо ви не впевнені на 100% у локалізації машини, яка запускатиме ваш сценарій, і особливо якщо ви обслуговуєте клієнтів, які можуть приїхати з будь-якої точки світу, ВСЕГА припускайте, що дати є в UTC, і зробіть конверсію якомога раніше. Це збереже волоски на голові, повірте мені
Romain G

8

дотримуйтесь документа python2.7, ви повинні використовувати Calendar.timegm () замість time.mktime ()

>>> d = datetime.date(2011,01,01)
>>> datetime.datetime.utcfromtimestamp(calendar.timegm(d.timetuple()))
datetime.datetime(2011, 1, 1, 0, 0)

3
чим вона відрізняється від моєї відповіді, якщо вона dзнаходиться в UTC?
jfs

8

Я визначив власні дві функції

  • utc_time2datetime (utc_time, tz = немає)
  • datetime2utc_time (дата)

тут:

import time
import datetime
from pytz import timezone
import calendar
import pytz


def utc_time2datetime(utc_time, tz=None):
    # convert utc time to utc datetime
    utc_datetime = datetime.datetime.fromtimestamp(utc_time)

    # add time zone to utc datetime
    if tz is None:
        tz_datetime = utc_datetime.astimezone(timezone('utc'))
    else:
        tz_datetime = utc_datetime.astimezone(tz)

    return tz_datetime


def datetime2utc_time(datetime):
    # add utc time zone if no time zone is set
    if datetime.tzinfo is None:
        datetime = datetime.replace(tzinfo=timezone('utc'))

    # convert to utc time zone from whatever time zone the datetime is set to
    utc_datetime = datetime.astimezone(timezone('utc')).replace(tzinfo=None)

    # create a time tuple from datetime
    utc_timetuple = utc_datetime.timetuple()

    # create a time element from the tuple an add microseconds
    utc_time = calendar.timegm(utc_timetuple) + datetime.microsecond / 1E6

    return utc_time

1
Я переглянув прямо ваш твір. Шлях до брудного і складного для такої простої речі .. Не пітонічний.
Сердитий 84

@Mayhem: Я трохи прибрав це і додав коментарі. Якщо у вас є краще і більш загальне рішення для перетворення часу на дату і назад, здатне встановити часовий пояс - повідомте нас про це. Також дайте мені знати, як повинен виглядати більш пітонічний приклад мого коду.
агітарій

3

питання трохи плутається. часові позначки не є UTC - вони річ Unix. дата може бути UTC? якщо припустити, що це так, і якщо ви використовуєте Python 3.2+, проста дата робить це дрібницею:

>>> SimpleDate(date(2011,1,1), tz='utc').timestamp
1293840000.0

якщо у вас є рік, місяць і день, вам не потрібно створювати date:

>>> SimpleDate(2011,1,1, tz='utc').timestamp
1293840000.0

а якщо дата є в іншому часовому поясі (це має значення, оскільки ми припускаємо півночі без відповідного часу):

>>> SimpleDate(date(2011,1,1), tz='America/New_York').timestamp
1293858000.0

[ідея простої дати полягає в тому, щоб зібрати всі дані про дату та час python в одному послідовному класі, щоб ви могли здійснити будь-яке перетворення. так, наприклад, він піде й іншим шляхом:

>>> SimpleDate(1293858000, tz='utc').date
datetime.date(2011, 1, 1)

]


SimpleDate(date(2011,1,1), tz='America/New_York')підвищує NoTimezone, хоча pytz.timezone('America/New_York')не створює жодних винятків ( simple-date==0.4.8, pytz==2014.7).
jfs

Я отримую помилку pip install simple-date, схоже, це лише python3 / pip3.
NoBugs

3

Використання пакету стрілок :

>>> import arrow
>>> arrow.get(2010, 12, 31).timestamp
1293753600
>>> time.gmtime(1293753600)
time.struct_time(tm_year=2010, tm_mon=12, tm_mday=31, 
    tm_hour=0, tm_min=0, tm_sec=0, 
    tm_wday=4, tm_yday=365, tm_isdst=0)

Примітка: arrowвикористовує dateutil та dateutilзнає вже багато років помилки, пов’язані з обробкою часових поясів. Не використовуйте його, якщо вам потрібні правильні результати для часових поясів із нефіксованим зміщенням UTC (нормально використовувати його для часового поясу UTC, але stdlib також обробляє регістр UTC).
jfs

@JFSebastian Ця помилка виникає лише під час переходу, у неоднозначний час.
Павло

Схоже, помилка була виправлена ​​28 березня
Матьє Лонгтін

Схоже, це найкраще і крос-пітонне рішення. Просто використовуйте стрілку . Спасибі;)
maxkoryukov

@MathieuLongtin dateutil можуть мати інші проблеми
jfs

1

Повний часовий рядок містить:

  • дата
  • час
  • utcoffset [+HHMM or -HHMM]

Наприклад:

1970-01-01 06:00:00 +0500 == 1970-01-01 01:00:00 +0000 == UNIX timestamp:3600

$ python3
>>> from datetime import datetime
>>> from calendar import timegm
>>> tm = '1970-01-01 06:00:00 +0500'
>>> fmt = '%Y-%m-%d %H:%M:%S %z'
>>> timegm(datetime.strptime(tm, fmt).utctimetuple())
3600

Примітка:

UNIX timestamp- число з плаваючою комою, виражене в секундах з епохи, в UTC .


Редагувати:

$ python3
>>> from datetime import datetime, timezone, timedelta
>>> from calendar import timegm
>>> dt = datetime(1970, 1, 1, 6, 0)
>>> tz = timezone(timedelta(hours=5))
>>> timegm(dt.replace(tzinfo=tz).utctimetuple())
3600

6
І як це відповідає на моє запитання?
Андреас Юнг

1
@ User908088 Перетворити , 1970-01-01 06:00:00 +0500щоб , 3600як ви просили.
кев

1
@ user908088 Немає timezoneв python2, ви можете зробити розрахунок ( 06:00:00- +0500= 01:00:00) вручну.
кев

5
чому ця відповідь на вершині навіть у неї є 1 голос, щось не так
Umair

1

Зважаючи на те, що ви маєте назву datetimeоб'єкта d, скористайтеся наступним, щоб отримати позначку часу в UTC:

d.strftime("%Y-%m-%dT%H:%M:%S.%fZ")

А для зворотного напрямку використовуйте наступне:

d = datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")

0

Я вражений глибокою дискусією.

мої 2 копійки:

from datetime import date datetime час імпорту

мітка часу в utc:

timestamp = \
(datetime.utcnow() - datetime(1970,1,1)).total_seconds()

або,

timestamp = time.time()

якщо зараз результат datetime.now (), у тому самому DST

utcoffset = (datetime.now() - datetime.utcnow()).total_seconds()
timestamp = \
(now - datetime(1970,1,1)).total_seconds() - utcoffset
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.