¿Cómo analizar las fechas con la cadena de zona horaria -0400 en Python?

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 tiempo de ejecución para la especificación de 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?

Puedes usar la función parse desde dateutil:

>>> from dateutil.parser import parse >>> d = parse('2009/05/13 19:19:30 -0400') >>> d datetime.datetime(2009, 5, 13, 19, 19, 30, tzinfo=tzoffset(None, -14400)) 

De esta manera obtienes un objeto de fecha y hora que puedes usar.

Como se respondió , dateutil2.0 está escrito para Python 3.0 y no funciona con Python 2.x. Para Python 2.x se debe usar dateutil1.5.

%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 eg, 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) 

Aquí hay una solución del problema "%z" para Python 2.7 y versiones anteriores

En lugar de usar:

 datetime.strptime(t,'%Y-%m-%dT%H:%M %z') 

Usa el timedelta para tener en cuenta la zona horaria, como esto:

 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 aritmética de fechas sin tener que preocuparse por las zonas horarias.

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 el dayfirst y el yearfirst ).

En mi aplicación, almaceno la cadena de formato en el archivo .INI y cada implementación puede tener su propio formato. Por lo tanto, realmente no me gusta el enfoque 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) 

Un trazador de líneas para los viejos pitones que hay. Puede multiplicar un timedelta por 1 / -1 según el 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) 

Si está en Linux, puede usar el comando de date externa 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 la date también aceptará entradas como “ayer” o “año pasado” 🙂