Conversione di datetime.date in data / ora UTC in Python


295

Ho a che fare con le date in Python e devo convertirle in timestamp UTC per essere utilizzate all'interno di Javascript. Il seguente codice non funziona:

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

Anche la conversione dell'oggetto data prima in datetime non aiuta. Ho provato l'esempio a questo link da, ma:

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

e ora neanche:

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

o

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

funziona.

Quindi una domanda generale: come posso convertire una data in secondi dall'epoca secondo UTC?



29
Non sono sicuro che sarei d'accordo a contrassegnarlo come duplicato. Mentre le soluzioni sono simili, le domande non lo sono. Uno (questo) sta tentando di creare un timestamp da a datetime.date, l'altro sta tentando di convertire una rappresentazione di stringa da un fuso orario a un altro. Come qualcuno che cerca una soluzione a questo problema, non posso concludere che quest'ultimo fornirà la risposta che sto cercando.
DRH,

5
datetime (date.year, date.month, date.day) .timestamp ()
Julian

Risposte:


462

Se d = date(2011, 1, 1)è in UTC:

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

Se dè nel fuso orario locale:

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

timestamp1e timestamp2può differire se la mezzanotte nel fuso orario locale non è la stessa istanza della mezzanotte in UTC.

mktime()può restituire un risultato errato se dcorrisponde a un orario locale ambiguo (ad esempio, durante la transizione dell'ora legale) o se dè una data passata (futura) in cui l'offset utc potrebbe essere stato diverso e la C mktime()non ha accesso al database tz sulla piattaforma data . È possibile utilizzare il pytzmodulo (ad es. Via tzlocal.get_localzone()) per ottenere l'accesso al database tz su tutte le piattaforme . Inoltre, utcfromtimestamp()potrebbe non riuscire e mktime()potrebbe restituire un timestamp non POSIX se "right"si utilizza il fuso orario .


Per convertire un datetime.dateoggetto che rappresenta la data in UTC senza 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

Come posso ottenere una data convertita in secondi dall'epoca secondo UTC?

Per convertire datetime.datetime(non datetime.date) un oggetto che rappresenta già l'ora in UTC nel corrispondente timestamp POSIX (a float).

Python 3.3+

datetime.timestamp():

from datetime import timezone

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

Nota: è necessario fornire timezone.utcesplicitamente altrimenti .timestamp()presupporre che l'oggetto datetime ingenuo sia nel fuso orario locale.

Python 3 (<3.3)

Dai documenti per datetime.utcfromtimestamp():

Non esiste un metodo per ottenere il timestamp da un'istanza datetime, ma il timestamp POSIX corrispondente a un'istanza datetime dt può essere facilmente calcolato come segue. Per un ingenuo dt:

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

E per un dt consapevole:

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

Lettura interessante: tempo di epoca vs. ora del giorno sulla differenza tra che ora è? e quanti secondi sono trascorsi?

Vedi anche: datetime ha bisogno di un metodo "epoca"

Python 2

Per adattare il codice sopra per Python 2:

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

dove timedelta.total_seconds()equivale a (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6calcolato con vera divisione abilitata.

Esempio

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)

Attenzione ai problemi in virgola mobile .

Produzione

2012-01-08 15:34:10.022403
1326036850.02

Come convertire un datetimeoggetto consapevole nel timestamp POSIX

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

Su 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)

Su 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
Per il caso Python 2, perché no timestamp = (dt - datetime.fromtimestamp(0)).total_seconds():?
m01

7
@ m01: datetime(1970, 1, 1)è più esplicito. fromtimestamp()non è corretto qui ( dtè in UTC quindi utcfromtimestamp()dovrebbe essere usato invece).
jfs

1
@fedorqui guarda la totimestamp()funzione nella risposta.
jfs,

14
per quanto adoro Python, la sua libreria di data e ora è seriamente rotta.
Realfun,

3
@Realfun: fusi orari, transizioni DST, database tz, secondi intercalari indipendenti da Python.
jfs

81

Solo per sistemi unix :

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

Nota 1: dizzyf ha osservato che ciò applica fusi orari localizzati . Non usare in produzione.

Nota 2: Jakub Narębski ha notato che ciò ignora le informazioni sul fuso orario anche per un datetime sensibile all'offset (testato per Python 2.7).


4
Una buona risposta se sai con quale sistema verrà eseguito il tuo codice, ma è importante notare che la stringa di formato '% s' non esiste su tutti i sistemi operativi, quindi questo codice non è portatile. Se vuoi verificare se è supportato puoi controllare man strftimesu sistemi simili a Unix.
Alice Heaton,

5
Va detto che questo lascia cadere la parte dei secondi frazionaria (microsecondi o altro).
Alfe,

11
ATTENZIONE: questo applica fusi orari localizzati! Otterrai un diverso comportamento in orario per oggetti datetime identici a seconda del tempo della macchina
dizzyf

3
Inoltre, questo ignora le informazioni sul fuso orario e presuppone che il datetime sia nel fuso orario locale anche per il datetime sensibile all'offset , con tzinfo impostato. Bene, almeno in Python 2.7.
Jakub Narębski,

1
timestamp javascript = timestamp python * 1000
Trinh Hoang Nhu

38
  • Presupposto 1: stai tentando di convertire una data in un timestamp, tuttavia poiché una data copre un periodo di 24 ore, non esiste un singolo timestamp che rappresenti quella data. Presumo che tu voglia rappresentare il timestamp di quella data a mezzanotte (00: 00: 00.000).

  • Presupposto 2: la data presentata non è associata a un particolare fuso orario, tuttavia si desidera determinare l'offset da un determinato fuso orario (UTC). Senza conoscere il fuso orario in cui si trova la data, non è possibile calcolare un timestamp per un fuso orario specifico. Presumo che tu voglia trattare la data come se fosse nel fuso orario del sistema locale.

Innanzitutto, puoi convertire l'istanza di data in una tupla che rappresenta i vari componenti dell'ora utilizzando il timetuple()membro:

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)

Puoi quindi convertirlo in un timestamp usando time.mktime:

ts = time.mktime(dtt) # 1293868800.0

È possibile verificare questo metodo testandolo con l'ora stessa (1970-01-01), nel qual caso la funzione dovrebbe restituire la differenza di fuso orario per il fuso orario locale in quella data:

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 è di 8 ore, il che sarebbe corretto per il fuso orario del Pacifico (dove mi trovo).


Non sono sicuro del motivo per cui questo è stato sottoposto a downgrade. Risolve il problema del PO (il codice di lavoro il PO etichettato come "non funzionante"). Non risponde alla mia domanda (converti datetime.datetime UTC in timestamp), ma comunque ... upvoting.
Thanatos,

9
Stai attento. Il presupposto che "vuoi trattare la data come se fosse nel fuso orario del sistema locale è la radice di tutti i problemi con il fuso orario in Python. A meno che tu non sia sicuro al 100% della localizzazione della macchina che eseguirà il tuo script e soprattutto se serve clienti che possono venire da qualsiasi parte del mondo, presumi SEMPRE che le date siano in UTC e faccia la conversione il più presto possibile. Ti terrà i capelli in testa, fidati di me.
Romain G

8

segui il documento python2.7, devi usare calendar.timegm () invece di time.mktime ()

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

3
come è diverso da mia risposta se dè in UTC?
jfs

8

Ho definito le mie due funzioni

  • utc_time2datetime (utc_time, tz = None)
  • datetime2utc_time (datetime)

Qui:

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
Ho sfogliato il tuo esempio. Modo di essere disordinato e complicato per una cosa così semplice .. Non è affatto pitone.
Arrabbiato, 84

@Mayhem: l'ho pulito un po 'e ho aggiunto commenti. Se hai una soluzione migliore e più generica per convertire di volta in volta e tornare indietro e essere in grado di impostare il fuso orario, faccelo sapere. Fammi anche sapere come dovrebbe essere un esempio più pitonico del mio codice.
agittario

3

la domanda è un po 'confusa. i timestamp non sono UTC - sono una cosa Unix. la data potrebbe essere UTC? supponendo che lo sia, e se stai usando Python 3.2+, simple-date lo rende banale:

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

se hai effettivamente l'anno, il mese e il giorno, non è necessario creare date:

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

e se la data è in qualche altro fuso orario (questo è importante perché stiamo assumendo la mezzanotte senza un orario associato):

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

[l'idea alla base di simple-date è quella di raccogliere tutte le cose relative a data e ora di Python in una classe coerente, in modo da poter fare qualsiasi conversione. quindi, per esempio, andrà anche nell'altra direzione:

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

]


SimpleDate(date(2011,1,1), tz='America/New_York')genera NoTimezone, mentre pytz.timezone('America/New_York')non genera alcuna eccezione ( simple-date==0.4.8, pytz==2014.7).
jfs,

pip install simple-dateViene visualizzato un errore , sembra che sia solo python3 / pip3.
NoBugs,

3

Utilizzando il pacchetto freccia :

>>> 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)

nota: arrowutilizza dateutil e dateutilconosce da molti anni bug relativi alla gestione del fuso orario. Non utilizzarlo se hai bisogno di risultati corretti per i fusi orari con un offset UTC non fisso (va bene usarlo per il fuso orario UTC ma lo stdlib gestisce anche il caso UTC ok).
jfs

@JFSebastian Quel bug si verifica solo durante una transizione, in un tempo ambiguo.
Paul

Sembra che il bug sia stato corretto il 28 marzo
Mathieu Longtin il

Sembra che sia la soluzione migliore e cross-python. Usa la freccia . Grazie;)
maxkoryukov,

@MathieuLongtin dateutil potrebbe avere altri problemi
jfs

1

Una stringa di tempo completa contiene:

  • Data
  • tempo
  • utcoffset [+HHMM or -HHMM]

Per esempio:

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

Nota:

UNIX timestampè un numero in virgola mobile espresso in secondi dall'epoca, in UTC .


Modificare:

$ 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
E come risponde alla mia domanda?
Andreas Jung,

1
@ user908088 Converti 1970-01-01 06:00:00 +0500in 3600come richiesto.
kev

1
@ user908088 Non c'è timezonein python2, è possibile fare il calcolo ( 06:00:00- +0500= 01:00:00) manualmente.
kev

5
perché questa risposta in cima ha anche -1 voti, qualcosa di sbagliato in SO
Umair

1

Considerando che hai datetimechiamato un oggetto d, usa quanto segue per ottenere il timestamp in UTC:

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

E per la direzione opposta, utilizzare quanto segue:

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

0

Sono impressionato dalla discussione profonda.

i miei 2 centesimi:

da datetime import tempo di importazione datetime

il timestamp in utc è:

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

o,

timestamp = time.time()

se ora risulta da datetime.now (), nello stesso DST

utcoffset = (datetime.now() - datetime.utcnow()).total_seconds()
timestamp = \
(now - datetime(1970,1,1)).total_seconds() - utcoffset
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.