Il formato ISO dell'oggetto datetime di Python UTC non include Z (Zulu o Zero offset)


120

Perché python 2.7 non include il carattere Z (Zulu o offset zero) alla fine della stringa isoformat dell'oggetto datetime UTC a differenza di JavaScript?

>>> datetime.datetime.utcnow().isoformat()
'2013-10-29T09:14:03.895210'

Mentre in javascript

>>>  console.log(new Date().toISOString()); 
2013-10-29T09:38:41.341Z

1
I valori datetime di Python NON hanno informazioni sul fuso orario. Prova pytz o Babel se desideri che le informazioni sul fuso orario siano memorizzate nel tuo timestamp.
tnknepp

67
datetime.datetime.utcnow().isoformat() + 'Z'
jfs


3
..e la Z mancante fa sì che alcune cose non funzionino sorprendentemente, ad esempio chiamata API
cardamomo

Peggio ancora, se l'ultima parte di datetime è 0, la
troncerà

Risposte:


52

Gli datetimeoggetti Python non hanno informazioni sul fuso orario per impostazione predefinita e, senza di esse, Python effettivamente viola la specifica ISO 8601 ( se non viene fornita alcuna informazione sul fuso orario, si presume che sia l'ora locale ). Puoi utilizzare il pacchetto pytz per ottenere alcuni fusi orari predefiniti o sottoclasse direttamente tzinfote stesso:

from datetime import datetime, tzinfo, timedelta
class simple_utc(tzinfo):
    def tzname(self,**kwargs):
        return "UTC"
    def utcoffset(self, dt):
        return timedelta(0)

Quindi puoi aggiungere manualmente le informazioni sul fuso orario a utcnow():

>>> datetime.utcnow().replace(tzinfo=simple_utc()).isoformat()
'2014-05-16T22:51:53.015001+00:00'

Si noti che questo è conforme al formato ISO 8601, che consente l'uno Zo l'altro o +00:00come suffisso per UTC. Si noti che quest'ultimo si conforma meglio allo standard, con il modo in cui i fusi orari sono rappresentati in generale (UTC è un caso speciale).


108
come includo Zinvece di +00:00?
leopoodle

14
AVVERTIMENTO!!! pytzviola il tzinfoprotocollo di Python ed è pericoloso da usare! Vedi blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html
ivan_pozdeev

@ivan_pozdeev grazie per il link, molto interessante. Si noti che le modifiche che consentono dateutildi funzionare non sono state implementate fino a Python 3.6 con PEP 495, quindi dire che pytznon è stata utilizzata l'interfaccia standard è ingiusto perché l'interfaccia è cambiata. Prima del cambiamento l'unico modo per farlo funzionare era il modo in cui lo pytzfaceva.
Mark Ransom

67

Opzione: isoformat()

Python datetimenon supporta i suffissi del fuso orario militare come il suffisso "Z" per UTC. La seguente semplice sostituzione della stringa fa il trucco:

In [1]: import datetime

In [2]: d = datetime.datetime(2014, 12, 10, 12, 0, 0)

In [3]: str(d).replace('+00:00', 'Z')
Out[3]: '2014-12-10 12:00:00Z'

str(d) è essenzialmente lo stesso di d.isoformat(sep=' ')

Vedi: Datetime, Python Standard Library

Opzione: strftime()

Oppure potresti usare strftimeper ottenere lo stesso effetto:

In [4]: d.strftime('%Y-%m-%d %H:%M:%SZ')
Out[4]: '2014-12-10 12:00:00Z'

Nota: questa opzione funziona solo quando sai che la data specificata è in UTC.

Vedi: datetime.strftime ()


Aggiuntivo: fuso orario leggibile dall'uomo

Andando oltre, potresti essere interessato a visualizzare informazioni sul fuso orario leggibili dall'uomo, pytzcon il strftime %Zflag del fuso orario:

In [5]: import pytz

In [6]: d = datetime.datetime(2014, 12, 10, 12, 0, 0, tzinfo=pytz.utc)

In [7]: d
Out[7]: datetime.datetime(2014, 12, 10, 12, 0, tzinfo=<UTC>)

In [8]: d.strftime('%Y-%m-%d %H:%M:%S %Z')
Out[8]: '2014-12-10 12:00:00 UTC'

1
Il separatore non dovrebbe essere Tinvece di uno spazio vuoto?
Marcello Romani

1
dovrebbe essere d = datetime.datetime(2014, 12, 10, 12, 0, 0, tzinfo=datetime.timezone.utc)nella riga 2. Btw., in RFC3339 dice che uno spazio è "ok" (invece della "T").
MrFuppes

16

I seguenti script javascript e python forniscono output identici. Penso che sia quello che stai cercando.

JavaScript

new Date().toISOString()

Pitone

import datetime

datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'

L'output che danno è l'ora utc (zelda) formattata come una stringa ISO con una cifra significativa di 3 millisecondi e aggiunta con una Z.

2019-01-19T23:20:25.459Z

12

In Python> = 3.2 puoi semplicemente usare questo:

>>> from datetime import datetime, timezone
>>> datetime.now(timezone.utc).isoformat()
'2019-03-14T07:55:36.979511+00:00'

3
Soluzione molto bella. Per la loro domanda sul Z, puoi .isoformat()[:-6] + 'Z'
ritagliare

11

I datetimes di Python sono un po 'goffi. Usa arrow.

> str(arrow.utcnow())
'2014-05-17T01:18:47.944126+00:00'

Arrow ha essenzialmente la stessa API di datetime, ma con fusi orari e alcune sottigliezze extra che dovrebbero essere nella libreria principale.

Un formato compatibile con Javascript può essere ottenuto tramite:

arrow.utcnow().isoformat().replace("+00:00", "Z")
'2018-11-30T02:46:40.714281Z'

Javascript eliminerà Date.parsesilenziosamente i microsecondi dal timestamp.


7
Questo non risponde alla domanda poiché la stringa non termina con "Z".
kaleissin

3

Ci sono molte buone risposte sul post, ma volevo che il formato uscisse esattamente come fa con JavaScript. Questo è quello che sto usando e funziona bene.

In [1]: import datetime

In [1]: now = datetime.datetime.utcnow()

In [1]: now.strftime('%Y-%m-%dT%H:%M:%S') + now.strftime('.%f')[:4] + 'Z'
Out[3]: '2018-10-16T13:18:34.856Z'

3
pip install python-dateutil
>>> a = "2019-06-27T02:14:49.443814497Z"
>>> dateutil.parser.parse(a)
datetime.datetime(2019, 6, 27, 2, 14, 49, 443814, tzinfo=tzutc())

3

Ho affrontato questo problema con alcuni obiettivi:

  • generare una stringa data / ora "consapevole" UTC in formato ISO 8601
  • utilizzare solo le funzioni della libreria standard Python per la creazione di stringhe e oggetti datetime
  • convalidare l'oggetto e la stringa datetime con una timezonefunzione di utilità Django e il dateutilparser
  • utilizzare le funzioni JavaScript per verificare che la stringa data / ora ISO 8601 sia compatibile con UTC

Questo approccio non include un suffisso Z e non viene utilizzato utcnow(), ma è basato sulla raccomandazione nella documentazione di Python e supera l'adunanza con Django e JavaScript.

Iniziamo passando un oggetto fuso orario UTC a datetime.now()invece di utilizzare datetime.utcnow():

from datetime import datetime, timezone

datetime.now(timezone.utc)
>>> datetime.datetime(2020, 1, 8, 6, 6, 24, 260810, tzinfo=datetime.timezone.utc)

datetime.now(timezone.utc).isoformat()
>>> '2020-01-08T06:07:04.492045+00:00'

Che sembra buona, così vediamo cosa Django e dateutilpensare:

from django.utils.timezone import is_aware

is_aware(datetime.now(timezone.utc))
>>> True

import dateutil.parser

is_aware(dateutil.parser.parse(datetime.now(timezone.utc).isoformat()))
>>> True

Ok, l'oggetto datetime di Python e la stringa ISO 8601 sono "consapevoli" di UTC. Ora diamo un'occhiata a cosa pensa JavaScript della stringa datetime. Prendendo in prestito da questa risposta otteniamo:

let date= '2020-01-08T06:07:04.492045+00:00';
const dateParsed = new Date(Date.parse(date))

document.write(dateParsed);
document.write("\n");
// Tue Jan 07 2020 22:07:04 GMT-0800 (Pacific Standard Time)

document.write(dateParsed.toISOString());
document.write("\n");
// 2020-01-08T06:07:04.492Z

document.write(dateParsed.toUTCString());
document.write("\n");
// Wed, 08 Jan 2020 06:07:04 GMT

Potresti anche voler leggere questo post del blog .


1
Bonus per rimanere fedeli alle funzioni di libreria di base e seguire un post precedente.
Victor Romeo il

1

Combinando tutte le risposte sopra, ho ottenuto la seguente funzione:

from datetime import datetime, tzinfo, timedelta
class simple_utc(tzinfo):
    def tzname(self,**kwargs):
        return "UTC"
    def utcoffset(self, dt):
        return timedelta(0)


def getdata(yy, mm, dd, h, m, s) :
    d = datetime(yy, mm, dd, h, m, s)
    d = d.replace(tzinfo=simple_utc()).isoformat()
    d = str(d).replace('+00:00', 'Z')
    return d


print getdata(2018, 02, 03, 15, 0, 14)

1

Uso il pendolo:

import pendulum


d = pendulum.now("UTC").to_iso8601_string()
print(d)

>>> 2019-10-30T00:11:21.818265Z

0
>>> import arrow

>>> now = arrow.utcnow().format('YYYY-MM-DDTHH:mm:ss.SSS')
>>> now
'2018-11-28T21:34:59.235'
>>> zulu = "{}Z".format(now)
>>> zulu
'2018-11-28T21:34:59.235Z'

Oppure, per ottenerlo in un colpo solo:

>>> zulu = "{}Z".format(arrow.utcnow().format('YYYY-MM-DDTHH:mm:ss.SSS'))
>>> zulu
'2018-11-28T21:54:49.639Z'
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.