web-dev-qa-db-fra.com

Comment analyser les dates avec la chaîne de fuseau horaire -0400 en Python?

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?

72
fields

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é.

103
txwikinger

%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

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

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.

21
Uri Goren

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

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)
1
Eric Sellin