808 votes

Comment analyser une date au format ISO 8601 ?

Je dois analyser RFC 3339 des chaînes de caractères comme "2008-09-03T20:56:35.450686Z" dans le langage Python datetime type.

J'ai trouvé strptime dans la bibliothèque standard de Python, mais elle n'est pas très pratique.

Quelle est la meilleure façon de procéder ?

8 votes

Pour être clair : ISO 8601 est la norme principale. RFC 3339 est un "profil" autoproclamé de l'ISO 8601 qui fait quelques des dérogations peu judicieuses des règles de la norme ISO 8601.

598voto

Flimm Points 8870

isoparse de la fonction python-dateutil

Le site python-dateutil Le paquet a dateutil.parser.isoparse pour analyser non seulement les chaînes de date RFC 3339, comme celle de la question, mais aussi d'autres chaînes de date RFC 3339. ISO 8601 les chaînes de date et d'heure qui ne sont pas conformes à la RFC 3339 (comme celles qui n'ont pas de décalage UTC ou celles qui ne représentent qu'une date).

>>> import dateutil.parser
>>> dateutil.parser.isoparse('2008-09-03T20:56:35.450686Z') # RFC 3339 format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())
>>> dateutil.parser.isoparse('2008-09-03T20:56:35.450686') # ISO 8601 extended format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.isoparse('20080903T205635.450686') # ISO 8601 basic format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.isoparse('20080903') # ISO 8601 basic format, date only
datetime.datetime(2008, 9, 3, 0, 0)

Le site python-dateutil Le paquet a également dateutil.parser.parse . Par rapport à isoparse il est vraisemblablement moins strict, mais les deux sont assez indulgents et tenteront d'interpréter la chaîne de caractères que vous passez. Si vous voulez éliminer toute possibilité de lecture erronée, vous devez utiliser quelque chose de plus strict que l'une ou l'autre de ces fonctions.

Comparaison avec la version intégrée de Python 3.7+. datetime.datetime.fromisoformat

dateutil.parser.isoparse est un analyseur complet du format ISO-8601, mais fromisoformat est délibérément no . Veuillez consulter la documentation de cette dernière fonction pour cette mise en garde (voir cette réponse ).

97 votes

Pour les paresseux, il s'installe via python-dateutil no dateutil donc : pip install python-dateutil .

31 votes

Soyez averti que le dateutil.parser est intentionnellement maladroit : il essaie de deviner le format et fait des suppositions inévitables (personnalisables uniquement à la main) dans les cas ambigus. Donc, utilisez-le UNIQUEMENT si vous avez besoin d'analyser des entrées de format inconnu et si vous êtes prêt à tolérer des erreurs de lecture occasionnelles.

2 votes

D'accord. Un exemple est de passer une "date" de 9999. Cela renverra la même chose que datetime(9999, mois actuel, jour actuel). Ce n'est pas une date valide à mon avis.

215voto

sethbc Points 907

Notez que dans Python 2.6+ et Py3K, le caractère %f capture les microsecondes.

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

Voir la question ici

4 votes

Note - si vous utilisez des dates naïves, je pense que vous n'obtiendrez pas de TZ du tout - Z peut ne correspondre à rien.

32 votes

Cette réponse (dans sa forme actuelle, modifiée) repose sur le codage en dur d'un décalage UTC particulier (à savoir "Z", qui signifie +00:00) dans la chaîne de format. C'est une mauvaise idée car l'analyse de toute date avec un décalage UTC différent échouera et une exception sera levée. Voir ma réponse qui décrit comment analyser la RFC 3339 avec strptime est en fait impossible.

1 votes

Dans mon cas, %f a attrapé les microsecondes plutôt que Z, datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f') alors ceci a fait l'affaire

83voto

Nicholas Riley Points 26161

Essayez le iso8601 qui fait exactement cela.

Il existe plusieurs autres options mentionnées sur le site de l WorkingWithTime sur le wiki de python.org.

0 votes

Simple comme bonjour iso8601.parse_date("2008-09-03T20:56:35.450686Z")

3 votes

La question n'était pas "comment analyser les dates ISO 8601", mais "comment analyser ce format de date exact".

3 votes

@tiktak Le PO a demandé "J'ai besoin d'analyser des chaînes de caractères comme X" et ma réponse à cela, après avoir essayé les deux bibliothèques, est d'en utiliser une autre, car iso8601 a des problèmes importants encore ouverts. Mon implication ou non dans un tel projet n'a rien à voir avec la réponse.

36voto

tzot Points 32224

Quelle est l'erreur exacte que vous obtenez ? Est-ce que cela ressemble à ce qui suit ?

>>> datetime.datetime.strptime("2008-08-12T12:20:30.656234Z", "%Y-%m-%dT%H:%M:%S.Z")
ValueError: time data did not match format:  data=2008-08-12T12:20:30.656234Z  fmt=%Y-%m-%dT%H:%M:%S.Z

Si oui, vous pouvez diviser votre chaîne d'entrée sur ".", et ensuite ajouter les microsecondes à la date que vous avez obtenue.

Essayez ça :

>>> def gt(dt_str):
        dt, _, us= dt_str.partition(".")
        dt= datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S")
        us= int(us.rstrip("Z"), 10)
        return dt + datetime.timedelta(microseconds=us)

>>> gt("2008-08-12T12:20:30.656234Z")
datetime.datetime(2008, 8, 12, 12, 20, 30, 656234)

12 votes

Vous ne pouvez pas simplement enlever le .Z car il signifie le fuseau horaire et peut être différent. J'ai besoin de convertir la date dans le fuseau horaire UTC.

0 votes

Un simple objet datetime n'a pas de concept de fuseau horaire. Si toutes vos heures se terminent par "Z", toutes les dates que vous obtiendrez seront en UTC (heure zouloue).

0 votes

Si le fuseau horaire est différent de "" o "Z" alors il doit s'agir d'un décalage en heures/minutes, qui peut être directement ajouté ou soustrait de l'objet datetime. vous pourrait créer une sous-classe de tzinfo pour le gérer, mais ce n'est probablement pas recommandé.

31voto

Ted Points 854
import re
import datetime
s = "2008-09-03T20:56:35.450686Z"
d = datetime.datetime(*map(int, re.split(r'[^\d]', s)[:-1]))

84 votes

Je ne suis pas d'accord, c'est pratiquement illisible et pour autant que je puisse dire, cela ne prend pas en compte le Zulu (Z) qui rend cette date naïve même si les données de fuseau horaire ont été fournies.

15 votes

Je le trouve assez lisible. En fait, c'est probablement le moyen le plus simple et le plus performant de faire la conversion sans installer de paquets supplémentaires.

3 votes

Ceci est équivalent à d=datetime.datetime(*map(int, re.split(' \D ', s)[:-1])) je suppose.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X