Python strptime () y zonas horarias?

157

Tengo un archivo de volcado CSV de una copia de seguridad de Blackberry IPD, creado usando IPDDump. Las cadenas de fecha / hora aquí se parecen a esto (donde ESThay una zona horaria australiana):

Tue Jun 22 07:46:22 EST 2010

Necesito poder analizar esta fecha en Python. Al principio, traté de usar la strptime()función desde datettime.

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

Sin embargo, por alguna razón, el datetimeobjeto que regresa no parece estar tzinfoasociado con él.

Leí en esta página que aparentemente datetime.strptimedescarta en silencio tzinfo, sin embargo, verifiqué la documentación y no puedo encontrar nada documentado a tal efecto aquí .

He podido analizar la fecha usando una biblioteca de Python de terceros, dateutil , pero todavía tengo curiosidad sobre cómo estaba usando strptime()incorrectamente la versión incorporada . ¿Hay alguna forma strptime()de jugar bien con las zonas horarias?

victorhooi
fuente
1
¿No puedes ... convertir todas las fechas a GMT?
Robus
2
@Robus: Hmm, esperaba hacer eso, pero estaba asumiendo que el tiempo de espera / fecha y hora de alguna manera podría hacer eso. De cualquier manera, necesito almacenar / analizar el hecho de que las fechas y horas están en la zona horaria EST, o cualquier zona horaria que me sucedan. El script debe poder analizar fechas y horas genéricas con información de zona horaria (por ejemplo, ETC podría ser cualquier otra zona horaria).
victorhooi
3
EST también es una abreviatura de zona horaria de EE. UU. (De manera similar, BST es una abreviatura de zona horaria del Reino Unido y de Brasil). Estas abreviaturas son intrínsecamente ambiguas. Utilice compensaciones relativas a UTC / GMT en su lugar. (Si tiene que apoyar abreviaturas, que necesita para hacer el mapeo dependiente de la localización y eso es un sucio agujero de rata.)
Donal Fellows

Respuestas:

58

La datetimedocumentación del módulo dice:

Devuelve una fecha y hora correspondiente a date_string, analizada según el formato. Esto es equivalente a datetime(*(time.strptime(date_string, format)[0:6])).

¿Ves eso [0:6]? Eso te atrapa (year, month, day, hour, minute, second). Nada más. No se mencionan las zonas horarias.

Curiosamente, [Win XP SP2, Python 2.6, 2.7] pasando su ejemplo a time.strptimeno funciona, pero si quita el "% Z" y el "EST" sí funciona. También utiliza "UTC" o "GMT" en lugar de "EST". "PST" y "MEZ" no funcionan. Misterioso.

Vale la pena señalar que esto se ha actualizado a partir de la versión 3.2 y la misma documentación ahora también establece lo siguiente:

Cuando se proporciona la directiva% z al método strptime (), se generará un objeto datetime consciente. La información tz del resultado se establecerá en una instancia de zona horaria.

Tenga en cuenta que esto no funciona con% Z, por lo que el caso es importante. Vea el siguiente ejemplo:

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
fuente
13
Error
jfs
354

Recomiendo usar python-dateutil . Su analizador ha sido capaz de analizar todos los formatos de fecha que he incluido hasta ahora.

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

y así. No se trata de strptime()tonterías de formato ... solo dale una fecha y hace lo correcto.

Actualización : ¡Vaya! Me perdí en tu pregunta original que mencionaste que usaste dateutil, perdón por eso. Pero espero que esta respuesta siga siendo útil para otras personas que se topan con esta pregunta cuando tienen preguntas de análisis de fechas y ven la utilidad de ese módulo.

Joe Shaw
fuente
Dado que muchas personas tienden a usar python-dateutil, me gustaría señalarnos una limitación de esa biblioteca. >>> 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 necesita reemplazar la última coma con punto. Entoncesparser.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
77
@flyingfoxlee, sí, lo entiendo. Solo quiero decirle a la gente la limitación de python-dateutil. Hace cosas mágicas, pero a veces no lo hace. Así que "solo dale una cita y hace lo correcto". No es 100% cierto.
wanghq
44
dateutil.parser.parse("10-27-2016 09:06 AM PDT")regresa: datetime.datetime(2016, 10, 27, 9, 6)no puede determinar la zona horaria ...
HaPsantran
2
Depende de la meta de uno. dateutil parserPuede ser simple de usar, pero strptime()es más rápido. Además, sus formatos son bastante fáciles de aprender.
éxtasis
9

Su cadena de tiempo es similar al formato de hora en rfc 2822 (formato de fecha en correo electrónico, encabezados http) . Puede analizarlo usando solo 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)

Vea soluciones que producen objetos de fecha y hora con reconocimiento de zona horaria para varias versiones de Python: análisis de fecha con zona horaria desde un correo electrónico .

En este formato, ESTes semánticamente equivalente a-0500 . Aunque, en general, una abreviatura de zona horaria no es suficiente, para identificar una zona horaria de forma única .

jfs
fuente
0

Me encontré con este problema exacto.

Lo que terminé haciendo:

# 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)
Christopher
fuente