ISO to datetime object: 'z' is a bad directive

49,818

Solution 1

Welcome to Python datetime! Dealing with dates and times is necessarily complex, and Python doesn't come fully with batteries included in this case. You can't use %z in strptime because Python has no classes to represent timezones (you are supposed to implement your own, or better yet include some other libraries).

You want to use pytz and python-dateutil. For more details see here:

Python strptime() and timezones?

Solution 2

problem with python 2.7

I have had a similar problem parsing commit dates from the output of git log --date=iso8601 which actually isn't the ISO8601 format (hence the addition of --date=iso8601-strict in a later version).

>>> import datetime, sys
>>> datetime.datetime.strptime("2013-07-23T15:10:59.342107+01:00", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_strptime.py", line 317, in _strptime
    (bad_directive, format))
ValueError: 'z' is a bad directive in format '%Y-%m-%dT%H:%M:%S.%f%z'
>>> sys.version
'2.7.10 (default, Feb  7 2017, 00:08:15) \n[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)]'

problem with python 3:

If python 2 does not support %z because of the underlying strptime() implementation, then a possible way to fix your issue is to migrate to python 3. But it didn't work for me:

>>> import datetime, sys
>>> datetime.datetime.strptime("2013-07-23T15:10:59.342107+01:00", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/_strptime.py", line 565, in _strptime_datetime
    tt, fraction = _strptime(data_string, format)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/_strptime.py", line 362, in _strptime
    (data_string, format))
ValueError: time data '2013-07-23T15:10:59.342107+01:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
>>> sys.version
'3.6.3 (v3.6.3:2c5fed86e0, Oct  3 2017, 00:32:08) \n[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]'

The problem is because is not strictly ISO8601 (note how there was a : in the timezone info).

>>> datetime.datetime.strptime("2013-07-23T15:10:59.342107+0100", "%Y-%m-%dT%H:%M:%S.%f%z")
datetime.datetime(2013, 7, 23, 15, 10, 59, 342107, tzinfo=datetime.timezone(datetime.timedelta(0, 3600)))

solution with django & pytz:

Since I am using django I can leverage the utilities there.

https://github.com/django/django/blob/master/django/utils/dateparse.py

>>> from django.utils.dateparse import parse_datetime
>>> parse_datetime('2013-07-23T15:10:59.342107+01:00')
datetime.datetime(2013, 7, 23, 15, 10, 59, 342107, tzinfo=+0100)

Instead of strptime you could use your own regular expression.

Please note that django does use pytz under the hood for things like the utc singleton.

solution without django & pytz:

If you want a quick and dirty solution, you can use something similar to django without all the requirements.

https://gist.github.com/dnozay/3cd554a818ed768bff804bc04484905d

>>> from datetime_z import parse_datetime
>>> parse_datetime("2013-07-23T15:10:59.342107+01:00")
datetime.datetime(2013, 7, 23, 15, 10, 59, 342107, tzinfo=+0100)

Please note: YMMV, there is no support.

Share:
49,818
user2667326
Author by

user2667326

Updated on July 09, 2022

Comments

  • user2667326
    user2667326 almost 2 years

    I am trying to convert ISO to datetime using the code below:

    dt = datetime.datetime.strptime("2013-07-23T15:10:59.342107+01:00",
                                    "%Y-%m-%dT%H:%M:%S.%f%z")
    

    and I'm getting the error below:

    'z' is a bad directive in format '%Y-%m-%dT%H:%M:%S.%f%z'
    

    What is the best way to convert an ISO string of above the format to a datetime object? I'm using Python version 2.7.6.

  • Kevin Parker
    Kevin Parker about 10 years
    My god python is like the phase of the moon - this week we'll do it this way, next week who cares!
  • Csaba Toth
    Csaba Toth almost 9 years
    This is really great! No 3rd party package or own parser code is needed! I think many people use Django.
  • Csaba Toth
    Csaba Toth almost 9 years
    This should be the correct answer, given someone uses Django!
  • Rho Phi
    Rho Phi over 6 years
    I have to say that the absence of %z is quite disappointing indeed ...
  • Hemã Vidal
    Hemã Vidal over 6 years
    Do you have a solution for this question without Django?
  • theannouncer
    theannouncer almost 6 years
    Even parse_datetime can't help you if your timezone doesn't have - and :s T_T. I found a roundabout pure python way to do it that I wrote out here: stackoverflow.com/a/25878651/738924