Tengo una cadena de fecha con el formato '2009/05/13 19:19:30 -0400'. Parece que las versiones anteriores de Python pueden haber admitido una etiqueta de formato% z en strptime para la especificación de la zona horaria final, pero 2.6.x parece haber eliminado eso.
¿Cuál es la forma correcta de analizar esta cadena en un objeto de fecha y hora?
dateutil
2.1) con Python2.7.2
; No se requiere Python 3. Tenga en cuenta que si está instalando desde pip, el nombre del paquete espython-dateutil
.%z
es compatible con Python 3.2+:>>> from datetime import datetime >>> datetime.strptime('2009/05/13 19:19:30 -0400', '%Y/%m/%d %H:%M:%S %z') datetime.datetime(2009, 5, 13, 19, 19, 30, tzinfo=datetime.timezone(datetime.timedelta(-1, 72000)))
En versiones anteriores:
from datetime import datetime date_str = '2009/05/13 19:19:30 -0400' naive_date_str, _, offset_str = date_str.rpartition(' ') naive_dt = datetime.strptime(naive_date_str, '%Y/%m/%d %H:%M:%S') offset = int(offset_str[-4:-2])*60 + int(offset_str[-2:]) if offset_str[0] == "-": offset = -offset dt = naive_dt.replace(tzinfo=FixedOffset(offset)) print(repr(dt)) # -> datetime.datetime(2009, 5, 13, 19, 19, 30, tzinfo=FixedOffset(-240)) print(dt) # -> 2009-05-13 19:19:30-04:00
donde
FixedOffset
es una clase basada en el ejemplo de código de los documentos :from datetime import timedelta, tzinfo class FixedOffset(tzinfo): """Fixed offset in minutes: `time = utc_time + utc_offset`.""" def __init__(self, offset): self.__offset = timedelta(minutes=offset) hours, minutes = divmod(offset, 60) #NOTE: the last part is to remind about deprecated POSIX GMT+h timezones # that have the opposite sign in the name; # the corresponding numeric value is not used e.g., no minutes self.__name = '<%+03d%02d>%+d' % (hours, minutes, -hours) def utcoffset(self, dt=None): return self.__offset def tzname(self, dt=None): return self.__name def dst(self, dt=None): return timedelta(0) def __repr__(self): return 'FixedOffset(%d)' % (self.utcoffset().total_seconds() / 60)
fuente
ValueError: 'z' is a bad directive in format '%Y-%m-%d %M:%H:%S.%f %z'
en mi caso (Python 2.7).Aquí hay una solución del
"%z"
problema para Python 2.7 y versiones anterioresEn lugar de usar:
datetime.strptime(t,'%Y-%m-%dT%H:%M %z')
Utilice
timedelta
para dar cuenta de la zona horaria, así:from datetime import datetime,timedelta def dt_parse(t): ret = datetime.strptime(t[0:16],'%Y-%m-%dT%H:%M') if t[18]=='+': ret-=timedelta(hours=int(t[19:22]),minutes=int(t[23:])) elif t[18]=='-': ret+=timedelta(hours=int(t[19:22]),minutes=int(t[23:])) return ret
Tenga en cuenta que las fechas se convertirían a
GMT
, lo que permitiría realizar operaciones aritméticas de fechas sin preocuparse por las zonas horarias.fuente
+
personaje de la timedelta debe restarse , y viceversa. He editado y corregido el código.El problema con el uso de dateutil es que no puede tener la misma cadena de formato tanto para la serialización como para la deserialización, ya que dateutil tiene opciones de formato limitadas (solo
dayfirst
yyearfirst
).En mi aplicación, almaceno la cadena de formato en un archivo .INI y cada implementación puede tener su propio formato. Por lo tanto, realmente no me gusta el enfoque de dateutil.
Aquí hay un método alternativo que usa pytz en su lugar:
from datetime import datetime, timedelta from pytz import timezone, utc from pytz.tzinfo import StaticTzInfo class OffsetTime(StaticTzInfo): def __init__(self, offset): """A dumb timezone based on offset such as +0530, -0600, etc. """ hours = int(offset[:3]) minutes = int(offset[0] + offset[3:]) self._utcoffset = timedelta(hours=hours, minutes=minutes) def load_datetime(value, format): if format.endswith('%z'): format = format[:-2] offset = value[-5:] value = value[:-5] return OffsetTime(offset).localize(datetime.strptime(value, format)) return datetime.strptime(value, format) def dump_datetime(value, format): return value.strftime(format) value = '2009/05/13 19:19:30 -0400' format = '%Y/%m/%d %H:%M:%S %z' assert dump_datetime(load_datetime(value, format), format) == value assert datetime(2009, 5, 13, 23, 19, 30, tzinfo=utc) \ .astimezone(timezone('US/Eastern')) == load_datetime(value, format)
fuente
Un trazador de líneas para pitones viejos que hay. Puede multiplicar un timedelta por 1 / -1 dependiendo del signo +/-, como en:
datetime.strptime(s[:19], '%Y-%m-%dT%H:%M:%S') + timedelta(hours=int(s[20:22]), minutes=int(s[23:])) * (-1 if s[19] == '+' else 1)
fuente
Si está en Linux, puede usar el
date
comando externo para dwim:import commands, datetime def parsedate(text): output=commands.getoutput('date -d "%s" +%%s' % text ) try: stamp=eval(output) except: print output raise return datetime.datetime.frometimestamp(stamp)
Esto, por supuesto, es menos portátil que dateutil, pero un poco más flexible, porque
date
también aceptará entradas como "ayer" o "el año pasado" :-)fuente