Le format ISO de l'objet Python UTC datetime n'inclut pas Z (Zulu ou Zero offset)


120

Pourquoi python 2.7 n'inclut pas le caractère Z (Zulu ou décalage zéro) à la fin de la chaîne d'isoformat de l'objet datetime UTC contrairement à JavaScript?

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

Alors qu'en javascript

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

1
Les valeurs datetime Python n'ont AUCUNE information de fuseau horaire. Essayez pytz ou Babel si vous souhaitez que les informations de fuseau horaire soient stockées dans votre horodatage.
tnknepp

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


3
..et le Z manquant empêche étonnamment certaines choses de fonctionner, par exemple appel API
cardamome

Cela devient encore pire, si la dernière partie de datetime est 0, il la tronquera ...
ntg

Réponses:


52

Les datetimeobjets Python n'ont pas d'informations de fuseau horaire par défaut, et sans cela, Python viole en fait la spécification ISO 8601 ( si aucune information de fuseau horaire n'est donnée, supposée être l'heure locale ). Vous pouvez utiliser le package pytz pour obtenir des fuseaux horaires par défaut, ou tzinfovous sous - classer directement :

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

Ensuite, vous pouvez ajouter manuellement les informations de fuseau horaire à utcnow():

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

Notez que cela est conforme au format ISO 8601, qui autorise l'un Zou l' autre ou +00:00comme suffixe pour UTC. Notez que ce dernier est en fait mieux conforme à la norme, avec la façon dont les fuseaux horaires sont représentés en général (UTC est un cas particulier.)


108
comment puis-je inclure Zau lieu de +00:00?
leopoodle

14
AVERTISSEMENT!!! pytzviole le tzinfoprotocole de Python et est dangereux à utiliser! Voir blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html
ivan_pozdeev

@ivan_pozdeev merci pour le lien, très intéressant. Notez que les changements permettant dateutilde fonctionner n'ont pas été implémentés avant Python 3.6 avec PEP 495, donc pytzne pas utiliser l'interface standard est injuste car l'interface a changé. Avant le changement, la seule façon de le faire fonctionner était de le pytzfaire.
Mark Ransom

67

Option: isoformat()

Python datetimene prend pas en charge les suffixes de fuseau horaire militaire comme le suffixe «Z» pour UTC. Le remplacement de chaîne simple suivant fait l'affaire:

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) est essentiellement le même que d.isoformat(sep=' ')

Voir: Datetime, bibliothèque standard Python

Option: strftime()

Ou vous pouvez utiliser strftimepour obtenir le même effet:

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

Remarque: cette option ne fonctionne que lorsque vous savez que la date spécifiée est en UTC.

Voir: datetime.strftime ()


Supplémentaire: fuseau horaire lisible par l'homme

Pour aller plus loin, vous voudrez peut-être afficher des informations de fuseau horaire lisibles par l'homme, pytzavec un strftime %Zindicateur de fuseau horaire:

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
Le séparateur ne devrait-il pas être Tau lieu d'un blanc?
Marcello Romani

1
il devrait être d = datetime.datetime(2014, 12, 10, 12, 0, 0, tzinfo=datetime.timezone.utc)à la ligne 2. Btw., dans RFC3339, il est dit qu'un espace est «ok» (au lieu du «T»).
MrFuppes le

16

Les scripts javascript et python suivants donnent des sorties identiques. Je pense que c'est ce que vous recherchez.

JavaScript

new Date().toISOString()

Python

import datetime

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

La sortie qu'ils donnent est l'heure utc (zelda) formatée comme une chaîne ISO avec un chiffre significatif de 3 millisecondes et ajoutée avec un Z.

2019-01-19T23:20:25.459Z

12

En Python> = 3.2, vous pouvez simplement utiliser ceci:

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

3
Très belle solution. Pour leur question sur le Z, vous pouvez .isoformat()[:-6] + 'Z'
découper

11

Les datetimes Python sont un peu maladroits. Utilisez arrow.

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

Arrow a essentiellement la même API que datetime, mais avec des fuseaux horaires et des subtilités supplémentaires qui devraient être dans la bibliothèque principale.

Un format compatible avec Javascript peut être obtenu par:

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

Javascript Date.parsesupprimera discrètement les microsecondes de l'horodatage.


7
Cela ne répond pas à la question car la chaîne ne se termine pas par «Z».
kaleissin le

3

Il y a beaucoup de bonnes réponses sur le post, mais je voulais que le format soit exactement comme il le fait avec JavaScript. C'est ce que j'utilise et cela fonctionne bien.

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

J'ai abordé ce problème avec quelques objectifs:

  • générer une chaîne datetime "consciente" UTC au format ISO 8601
  • utiliser uniquement les fonctions de la bibliothèque standard Python pour la création d'objets datetime et de chaînes
  • valider l'objet datetime et la chaîne avec une timezonefonction utilitaire Django et l' dateutilanalyseur
  • utiliser des fonctions JavaScript pour valider que la chaîne datetime ISO 8601 est compatible UTC

Cette approche n'inclut pas de suffixe Z et ne l' utilise pasutcnow() , mais elle est basée sur la recommandation de la documentation Python et passe par Django et JavaScript.

Commençons par passer un objet fuseau horaire UTC au datetime.now()lieu d'utiliser 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'

Cela semble bon, alors voyons ce que Django et dateutilpensons:

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

D'accord, l'objet datetime Python et la chaîne ISO 8601 sont compatibles UTC. Voyons maintenant ce que JavaScript pense de la chaîne datetime. En empruntant à cette réponse, nous obtenons:

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

Vous pouvez également lire ce billet de blog .


1
Bonus pour s'en tenir aux fonctions de base de la bibliothèque et faire le suivi d'un article plus ancien.
Victor Romeo le

1

En combinant toutes les réponses ci-dessus, je suis venu avec la fonction suivante:

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

J'utilise le pendule:

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'

Ou, pour l'obtenir d'un seul coup:

>>> zulu = "{}Z".format(arrow.utcnow().format('YYYY-MM-DDTHH:mm:ss.SSS'))
>>> zulu
'2018-11-28T21:54:49.639Z'
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.