Jak parsować daty za pomocą ciągu -0400 strefy czasowej w Pythonie?

I have a date string of the form '2009/05/13 19:19:30 -0400'. Wydaje się, że poprzednie wersje Pythona mogły obsługiwać znacznik formatu % z w strptime dla końcowej specyfikacji strefy czasowej, ale 2.6.x chyba to usunął.

Jaki jest właściwy sposób przetworzenia tego ciągu do obiektu datetime?

Author: Peter Mortensen, 2009-07-09

6 answers

Możesz użyć funkcji parse z 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))

W ten sposób uzyskasz obiekt datetime, którego możesz następnie użyć.

Jak odpowiedział , dateutil2.0 jest napisany dla Pythona 3.0 i nie działa z Pythonem 2.x. dla Pythona 2.x dateutil1. 5 musi być użyty.

 101
Author: txwikinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-04 11:12:01

%z jest obsługiwany w Pythonie 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)))

Na wcześniejszych wersjach:

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

Gdzie FixedOffset jest klasą opartą na przykładzie kodu z 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)
 35
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-29 14:01:38

Oto poprawka "%z" problemu dla Pythona 2.7 i wcześniejszych

Zamiast używać:

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

Użyj timedelta, aby obliczyć strefę czasową, tak:

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

Zauważ, że daty zostaną zamienione na GMT, co pozwoli na arytmetykę dat bez martwienia się o strefy czasowe.

 17
Author: Uri Goren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-14 10:14:26

Problem z używaniem dateutil polega na tym, że nie można mieć tego samego ciągu formatującego zarówno dla serializacji, jak i deserializacji, ponieważ dateutil ma ograniczone opcje formatowania(tylko dayfirst i yearfirst).

W mojej aplikacji zapisuję łańcuch formatu .Pliku INI, a każde wdrożenie może mieć swój własny format. Tak więc, naprawdę nie podoba mi się podejście dateutil.

Oto alternatywna metoda, która używa pytz zamiast:

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
Author: sayap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-20 05:41:41

Jedna linijka dla starych pytonów. Można pomnożyć timedelta przez 1 / -1 w zależności od znaku+/ -, jak w:

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
Author: Eric Sellin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-03 14:19:42

Jeśli korzystasz z Linuksa, możesz użyć zewnętrznego polecenia date do 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)

Jest oczywiście mniej przenośny niż dateutil, ale nieco bardziej elastyczny, ponieważ date będzie również akceptował wejścia typu "wczoraj" lub "w zeszłym roku": -)

 -9
Author: Gyom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-04 11:12:43