Search code examples
pythondatetimetimezonepython-dateutil

Difference between datetime.strptime and parse from dateutil?


I am getting two different results in seconds when I parse the following time string:

Method 1:

from datetime import datetime

int(datetime.strptime('2015-03-25T19:46:23.286966Z', '%Y-%m-%dT%H:%M:%S.%fZ').timestamp())

yields 1427309183

Method 2:

from dateutil.parser import parse

int(parse('2015-03-25T19:46:23.286966Z').timestamp())

yields 1427312783

It seems that method 1 ignores the TZ vs method do (I run it from a UTC+1 tz).

Question: Why do these two methods yield different second timestamps? Can someone please explain what's going on under the hood and how to best handle such situations.

My goal is to convert the string to seconds in unix epoch time (i.e. utc).


Solution

  • If you take a look at the repr of your intermediate result (the datetime objects), you notice a difference:

    from datetime import datetime
    from dateutil.parser import parse
    
    print(repr(datetime.strptime('2015-03-25T19:46:23.286966Z', '%Y-%m-%dT%H:%M:%S.%fZ')))
    # datetime.datetime(2015, 3, 25, 19, 46, 23, 286966)
    
    print(repr(parse('2015-03-25T19:46:23.286966Z')))
    # datetime.datetime(2015, 3, 25, 19, 46, 23, 286966, tzinfo=tzutc())
    

    The first one is naive, no tzinfo set since you use a literal Z in the parsing directive. The second one is aware; tzinfo is set to UTC since dateutil's parser recognizes the Z to signal UTC. That makes for the difference in the timestamp, since Python treats naive datetime as local time - thus the difference of 1 hour, which is your local time's UTC offset.

    You can correctly parse like

    print(repr(datetime.fromisoformat('2015-03-25T19:46:23.286966Z'.replace('Z', '+00:00'))))
    # datetime.datetime(2015, 3, 25, 19, 46, 23, 286966, tzinfo=datetime.timezone.utc)
    

    see also here.

    Or less convenient (imho), with strptime:

    print(repr(datetime.strptime('2015-03-25T19:46:23.286966Z', '%Y-%m-%dT%H:%M:%S.%f%z')))
    # datetime.datetime(2015, 3, 25, 19, 46, 23, 286966, tzinfo=datetime.timezone.utc)