Take the 2-minute tour ×

I have a date string of the form '2009/05/13 19:19:30 -0400'. It seems that previous versions of python may have supported a %z format tag in strptime for the trailing timezone specification, but 2.6.x seems to have removed that.

What's the right way to parse this string into a datetime object?

share|improve this question

4 Answers 4

up vote 45 down vote accepted

You can use the parse function from 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))

This way you obtain a datetime object you can then use.

UPDATE: As answered dateutil2.0 is written for python3.0 and does not work with python2.x. For python2.x dateutil1.5 needs to be used.

share|improve this answer
THis doesn't work in dateutil version 2.0 on Python 2.6 –  Peter Bengtsson Mar 28 '11 at 17:54
This works fine for me (dateutil 2.1) with Python 2.7.2; Python 3 isn't required. Note that if you're installing from pip, the package name is python-dateutil. –  BigglesZX May 7 '13 at 9:58

If you are on linux, then you can use the external date command to dwim :

import commands, datetime

def parsedate(text):
  output=commands.getoutput('date -d "%s" +%%s' % text )
      print output
  return datetime.datetime.frometimestamp(stamp)

This is of course less portable that dateutil, but slightly more flexible, because date will also accept inputs like "yesterday" or "last year" :-)

share|improve this answer
I don't think it is good to call an external programm for this. And the next weak point: eval(): If you now that a webserver executes this code, you could do arbitrary code execution on the server! –  guettli Nov 14 '11 at 8:45
It all depends on the context: if what we're after is only a write-and-throw-away script, then these weaknesses are just irrelevant :-) –  Gyom Nov 15 '11 at 15:43
Down-voting this because: 1) It makes a system call for something trivial, 2) It injects strings directly into a shell call, 3) It calls eval(), and 4) It has an exception catch-all. Basically this is an example of how not to do things. –  benjaoming Apr 9 at 14:22
agreed @benjaoming! –  richard Nov 10 at 4:34

The problem with using dateutil is that you can't have the same format string for both serialization and deserialization, as dateutil has limited formatting options (only dayfirst and yearfirst).

In my application, I store the format string in .INI file, and each deployment can have its own format. Thus, I really don't like the dateutil approach.

Here's an alternative method that uses pytz instead:

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)
share|improve this answer

%z is supported in Python 3.2+:

>>> from datetime import datetime
>>> datetime.strptime('2009/05/13 19:19:30 -0400', '%Y/%m/%d %H:%M:%S %z')
                  tzinfo=datetime.timezone(datetime.timedelta(-1, 72000)))

On earlier versions:

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))
# -> datetime.datetime(2009, 5, 13, 19, 19, 30, tzinfo=FixedOffset(-240))
# -> 2009-05-13 19:19:30-04:00

where FixedOffset is a class based on 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)
share|improve this answer

Your Answer


