133

So in Python 3, you can generate an ISO 8601 date with .isoformat(), but you can't convert a string created by isoformat() back into a datetime object because Python's own datetime directives don't match properly. That is, %z = 0500 instead of 05:00 (which is produced by .isoformat()).

For example:

>>> strDate = d.isoformat()
>>> strDate
'2015-02-04T20:55:08.914461+00:00'

>>> objDate = datetime.strptime(strDate,"%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python34\Lib\_strptime.py", line 500, in _strptime_datetime
    tt, fraction = _strptime(data_string, format)
  File "C:\Python34\Lib\_strptime.py", line 337, in _strptime
    (data_string, format))
ValueError: time data '2015-02-04T20:55:08.914461+00:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'

From Python's strptime documentation: (https://docs.python.org/2/library/datetime.html#strftime-strptime-behavior)

%z UTC offset in the form +HHMM or -HHMM (empty string if the the object is naive). (empty), +0000, -0400, +1030

So, in short, Python does not even adhere to its own string formatting directives.

I know datetime is already terrible in Python, but this really goes beyond unreasonable into the land of plain stupidity.

Tell me this isn't true.

5
  • 2
    Have you considered stripping the last colon from the isoformatted string and then parsing back to a datetime object? It's a workaround, that can still be done somewhat elegantly. Commented Feb 4, 2015 at 22:04
  • @OliverW. I did consider this, in the end I ended up giving in and installing python-dateutil. I don't actually consider this a good solution either. I think those who are responsible for python need to take a closer look at what they've done with datetime. As of now i've given up and will just jump through the hoops like everyone else does. Commented Feb 5, 2015 at 0:16
  • I have always actually been particularly impressed with python for even HAVING functions like strptime and strftime, I never even took the time to notice this particular deficiency because as already stated, it can be rather elegantly avoided. Commented Feb 5, 2015 at 0:24
  • 2
    I agree, this is ridiculous - the native platform can convert to an ISO date but it cannot convert it back, except if you use another third party module. Commented Feb 9, 2017 at 18:21
  • 1
    Starting from Python 3.7 a new functionality has been introduced to the %z: Changed in version 3.7: When the %z directive is provided to the strptime() method, the UTC offsets can have a colon as a separator between hours, minutes and seconds. For example, '+01:00:00' will be parsed as an offset of one hour. In addition, providing 'Z' is identical to '+00:00'. Commented Feb 5, 2020 at 13:30

2 Answers 2

141

Python 3.7+

As of Python 3.7 there is a method datetime.fromisoformat() which is exactly the reverse for isoformat().

Older Python

If you have older Python, then this is the current best "solution" to this question:

pip install python-dateutil

Then...

import datetime
import dateutil

def getDateTimeFromISO8601String(s):
    d = dateutil.parser.parse(s)
    return d
Sign up to request clarification or add additional context in comments.

6 Comments

Sad state of Python's datetime...
Worth noting that if your system's time is set to UTC, the timezone of the parsed date will be tzlocal(), which != tzutc in comparisons, according to this link: coderwall.com/p/dpauza/dateutil-parse-timezone
@DanieleVenzano I don't get it. How can this string return back in a more happy way?
@raratiru I was referring to the fact that you need an external module to parse a timestamp in ISO format, but you can generate one with the standard library. Inconsistencies make me sad.
In python 3.7 there is a method datetime.fromisoformat() which is exactly the reverse for isoformat()
|
27

Try this:

>>> def gt(dt_str):
...     dt, _, us = dt_str.partition(".")
...     dt = datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S")
...     us = int(us.rstrip("Z"), 10)
...     return dt + datetime.timedelta(microseconds=us)

Usage:

>>> gt("2008-08-12T12:20:30.656234Z")
datetime.datetime(2008, 8, 12, 12, 20, 30, 656234)

2 Comments

Your solution doesn't address the problem that was laid out (and is pretty much a repetition of the possible duplicate that was already linked to). In the original problem, the OP has asked for an efficient way to parse the UTC offset, which has a colon in it.
This solution is good whenever installing one extra library is somewhat inconvenient (e.g. on Google Appengine)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.