Python strptime () et fuseaux horaires?

157

J'ai un fichier de vidage CSV à partir d'une sauvegarde Blackberry IPD, créé à l'aide d'IPDDump. Les chaînes de date / heure ici ressemblent à ceci (où ESTest un fuseau horaire australien):

Tue Jun 22 07:46:22 EST 2010

J'ai besoin de pouvoir analyser cette date en Python. Au début, j'ai essayé d'utiliser la strptime()fonction de datettime.

>>> datetime.datetime.strptime('Tue Jun 22 12:10:20 2010 EST', '%a %b %d %H:%M:%S %Y %Z')

Cependant, pour une raison quelconque, l' datetimeobjet qui revient ne semble en avoir aucun tzinfoassocié.

J'ai lu sur cette page qui apparemment se datetime.strptimedéfait en silence tzinfo, cependant, j'ai vérifié la documentation, et je ne trouve rien à cet effet documenté ici .

J'ai pu obtenir la date analysée à l'aide d'une bibliothèque Python tierce, dateutil , mais je suis toujours curieux de savoir comment j'utilisais strptime()incorrectement le intégré ? Existe-t-il un moyen strptime()de jouer correctement avec les fuseaux horaires?

Victorhooi
la source
1
Ne pouvez-vous pas simplement ... convertir toutes les dates en GMT?
Robus
2
@Robus: Hmm, j'espérais faire ça - mais je supposais que strftime / datetime pouvait en quelque sorte faire ça? Dans tous les cas, j'ai besoin de stocker / analyser le fait que les heures de données sont dans le fuseau horaire EST, ou quel que soit le fuseau horaire auquel elles m'arrivent. Le script doit être capable d'analyser les dates-heures génériques avec des informations de fuseau horaire (par exemple, ETC pourrait être n'importe quel autre fuseau horaire).
victorhooi
3
EST est également une abréviation de fuseau horaire américain. (De même, BST est à la fois un abrégé de fuseau horaire britannique et brésilien.) De telles abréviations sont tout simplement ambiguës par nature. Utilisez plutôt des décalages par rapport à UTC / GMT. (Si vous avez besoin de prendre en charge les abréviations, vous devez rendre le mappage dépendant des paramètres régionaux et c'est un trou de rat en désordre.)
Donal Fellows

Réponses:

58

La datetimedocumentation du module dit:

Renvoie un datetime correspondant à date_string, analysé selon le format. C'est équivalent à datetime(*(time.strptime(date_string, format)[0:6])).

Vous voyez ça [0:6]? Cela vous obtient (year, month, day, hour, minute, second). Rien d'autre. Aucune mention des fuseaux horaires.

Fait intéressant, [Win XP SP2, Python 2.6, 2.7] transmettre votre exemple à time.strptimene fonctionne pas, mais si vous supprimez le "% Z" et le "EST", cela fonctionne. Utiliser également "UTC" ou "GMT" au lieu de "EST" fonctionne. "PST" et "MEZ" ne fonctionnent pas. Déroutant.

Il convient de noter que cela a été mis à jour à partir de la version 3.2 et que la même documentation indique désormais également ce qui suit:

Lorsque la directive% z est fournie à la méthode strptime (), un objet datetime conscient sera produit. Le tzinfo du résultat sera défini sur une instance de fuseau horaire.

Notez que cela ne fonctionne pas avec% Z, donc le cas est important. Consultez l'exemple suivant:

In [1]: from datetime import datetime

In [2]: start_time = datetime.strptime('2018-04-18-17-04-30-AEST','%Y-%m-%d-%H-%M-%S-%Z')

In [3]: print("TZ NAME: {tz}".format(tz=start_time.tzname()))
TZ NAME: None

In [4]: start_time = datetime.strptime('2018-04-18-17-04-30-+1000','%Y-%m-%d-%H-%M-%S-%z')

In [5]: print("TZ NAME: {tz}".format(tz=start_time.tzname()))
TZ NAME: UTC+10:00
John Machin
la source
13
Bogue
jfs
354

Je recommande d'utiliser python-dateutil . Son analyseur a été en mesure d'analyser tous les formats de date que je lui ai lancés jusqu'à présent.

>>> from dateutil import parser
>>> parser.parse("Tue Jun 22 07:46:22 EST 2010")
datetime.datetime(2010, 6, 22, 7, 46, 22, tzinfo=tzlocal())
>>> parser.parse("Fri, 11 Nov 2011 03:18:09 -0400")
datetime.datetime(2011, 11, 11, 3, 18, 9, tzinfo=tzoffset(None, -14400))
>>> parser.parse("Sun")
datetime.datetime(2011, 12, 18, 0, 0)
>>> parser.parse("10-11-08")
datetime.datetime(2008, 10, 11, 0, 0)

etc. Pas de problème de strptime()format ... il suffit de lui donner une date et il fait la bonne chose.

Mise à jour : Oups. J'ai manqué dans votre question initiale que vous avez mentionnée que vous avez utilisée dateutil, désolé pour cela. Mais j'espère que cette réponse sera toujours utile à d'autres personnes qui tombent sur cette question lorsqu'elles ont des questions d'analyse de date et voient l'utilité de ce module.

Joe Shaw
la source
Étant donné que tant de gens ont tendance à utiliser python-dateutil, j'aimerais nous signaler une limitation de cette lib. >>> parser.parse("Thu, 25 Sep 2003 10:49:41,123 -0300") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Users/wanghq/awscli/lib/python2.7/site-packages/dateutil/parser.py", line 748, in parse return DEFAULTPARSER.parse(timestr, **kwargs) File "/Users/wanghq/awscli/lib/python2.7/site-packages/dateutil/parser.py", line 310, in parse res, skipped_tokens = self._parse(timestr, **kwargs) TypeError: 'NoneType' object is not iterable
wanghq
1
@wanghq vous devez remplacer la dernière virgule par un point. Puisparser.parse("Thu, 25 Sep 2003 10:49:41.123 -0300") returns: datetime.datetime(2003, 9, 25, 10, 49, 41, 123000, tzinfo=tzoffset(None, -10800))
flyingfoxlee
7
@flyingfoxlee, oui, je comprends cela. Je veux juste dire aux gens la limitation de python-dateutil. Il fait des choses magiques, mais échoue parfois à le faire. Alors "il suffit de lui donner un rendez-vous et il fait la bonne chose." n'est pas vrai à 100%.
wanghq
4
dateutil.parser.parse("10-27-2016 09:06 AM PDT")renvoie: datetime.datetime(2016, 10, 27, 9, 6)ne parvient pas à déterminer le fuseau horaire ...
HaPsantran
2
Cela dépend de son objectif. dateutil parserpeut être simple à utiliser, mais strptime()est plus rapide. De plus, ses formats sont assez faciles à apprendre.
ravissement
9

Votre chaîne d'heure est similaire au format d'heure dans rfc 2822 (format de date dans l'e-mail, en-têtes http) . Vous pouvez l'analyser en utilisant uniquement stdlib:

>>> from email.utils import parsedate_tz
>>> parsedate_tz('Tue Jun 22 07:46:22 EST 2010')
(2010, 6, 22, 7, 46, 22, 0, 1, -1, -18000)

Découvrez les solutions qui génèrent des objets datetime tenant compte du fuseau horaire pour différentes versions de Python: analyse de la date avec le fuseau horaire d'un e-mail .

Dans ce format, ESTest sémantiquement équivalent à-0500 . Cependant, en général, une abréviation de fuseau horaire ne suffit pas pour identifier un fuseau horaire de manière unique .

jfs
la source
0

Ran dans ce problème exact.

Ce que j'ai fini par faire:

# starting with date string
sdt = "20190901"
std_format = '%Y%m%d'

# create naive datetime object
from datetime import datetime
dt = datetime.strptime(sdt, sdt_format)

# extract the relevant date time items
dt_formatters = ['%Y','%m','%d']
dt_vals = tuple(map(lambda formatter: int(datetime.strftime(dt,formatter)), dt_formatters))

# set timezone
import pendulum
tz = pendulum.timezone('utc')

dt_tz = datetime(*dt_vals,tzinfo=tz)
Christophe
la source