J'ai une chaîne de dates au format '13/05/2009 19:19:30 -0400'. Il semble que les versions précédentes de Python peut avoir pris en charge une balise de format% z dans strptime pour la spécification de fuseau horaire de fin, mais 2.6.x semble avoir supprimé cela.
Quelle est la bonne façon d'analyser cette chaîne dans un objet datetime?
Vous pouvez utiliser la fonction d'analyse de 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 cette façon, vous obtenez un objet datetime que vous pouvez ensuite utiliser.
Comme répond , dateutil2.0 est écrit pour Python 3.0 et ne fonctionne pas avec Python 2.x. Pour Python 2.x dateutil1.5 doit être utilisé.
%z
est pris en charge dans 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)))
Sur les versions antérieures:
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
où FixedOffset
est une classe basée sur l'exemple de code des documents :
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)
Voici une correction du "%z"
problème pour Python 2.7 et versions antérieures
À la place d'utiliser:
datetime.strptime(t,'%Y-%m-%dT%H:%M %z')
Utilisez le timedelta
pour tenir compte du fuseau horaire, comme ceci:
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
Notez que les dates seraient converties en GMT
, ce qui permettrait de faire l'arithmétique des dates sans se soucier des fuseaux horaires.
Le problème avec l'utilisation de dateutil est que vous ne pouvez pas avoir la même chaîne de format pour la sérialisation et la désérialisation, car dateutil a des options de formatage limitées (uniquement dayfirst
et yearfirst
).
Dans mon application, je stocke la chaîne de format dans un fichier .INI, et chaque déploiement peut avoir son propre format. Ainsi, je n'aime vraiment pas l'approche dateutil.
Voici une méthode alternative qui utilise pytz à la place:
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 liner pour les vieux Pythons. Vous pouvez multiplier un timedelta par 1/-1 en fonction du signe +/-, comme dans:
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)