2009-07-09 5 views
64

J'ai une chaîne de date de la forme '2009/05/13 19:19:30 -0400'. Il semble que les versions précédentes de Python aient supporté une balise de format% z dans strptime pour la spécification du fuseau horaire, mais 2.6.x semble avoir supprimé cela.Comment analyser les dates avec la chaîne -0400 timezone en Python?

Quelle est la bonne façon d'analyser cette chaîne dans un objet datetime?

Répondre

93

Vous pouvez utiliser la fonction d'analyse syntaxique 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 vous pouvez utiliser.

En tant que answered, 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é.

+1

Cela ne fonctionne pas sur Python 2.6 –

+11

dans dateutil version 2.0 Cela fonctionne bien pour moi ('dateutil' 2.1) avec Python '2.7.2'; Python 3 n'est pas requis. Notez que si vous installez à partir de pip, le nom du paquet est 'python-dateutil'. – BigglesZX

-8

Si vous êtes sous Linux, vous pouvez utiliser la commande date externe à 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) 

Ceci est bien sûr moins portable que dateutil, mais un peu plus souple, car date va également accepter des entrées comme " hier » ou « l'année dernière » :-)

+3

Je ne pense pas qu'il est bon d'appeler un programme externe pour cela. Et le point faible suivant: eval(): Si vous maintenant qu'un serveur web exécute ce code, vous pouvez exécuter du code arbitraire sur le serveur! – guettli

+5

Tout dépend du contexte: si ce que nous recherchons est seulement un script d'écriture et de destruction, alors ces faiblesses sont tout simplement hors de propos :-) – Gyom

+8

Down-voting ceci parce que: 1) Il fait un appel système pour Quelque chose de trivial, 2) Il injecte des chaînes directement dans un appel shell, 3) Il appelle eval(), et 4) Il a une exception catch-all. Fondamentalement, c'est un exemple de comment * pas * de faire les choses. – benjaoming

7

le problème avec l'utilisation dateutil est que vous ne pouvez pas avoir la même chaîne de format pour les deux sérialisation et désérialisation, comme dateutil a des options de mise en forme limitée (seulement dayfirst et yearfirst).

Dans mon application, je stocke la chaîne de format dans le fichier .ini et chaque déploiement peut avoir son propre format. Ainsi, je n'aime vraiment pas l'approche dateutil.

est ici 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) 
32

%z est en 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 un classe basée sur the code example from the docs:

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

Cela provoque un 'ValueError: 'z' est une mauvaise directive au format '% Y-% m-% d% M:% H:% S.% f% z'' dans mon cas (Python 2.7). – Sheljohn

+0

@Sheljohn il n'est pas censé fonctionner sur Python 2.7 Regardez le tout en haut de la réponse. – jfs

+0

bizarre, d'ailleurs, que ce n'est pas du tout mentionné sur Python * 2.7 * docs: https://docs.python.org/2.7/library/datetime.html?highlight=datetime#strftime-strptime-behavior – 62mkv

12

Voici une solution du "%z" problème pour Python 2.7 et versions antérieures

Au lieu d'utiliser:

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

Utilisez le timedelta pour tenir compte du décalage 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, whi ch permettrait de faire des calculs arithmétiques sans se soucier des fuseaux horaires.

+0

I comme ceci, bien que vous deviez changer 'secondes =' en 'minutes ='. – Dave

+0

merci @Dave, fixe –

+1

qui convertit en GMT ... si c'est ce que vous voulez vraiment ... –

0

Une doublure pour les vieux Pythons là-bas.Vous pouvez multiplier par un timedelta 1/-1 en fonction de +/- 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) 
Questions connexes