Search code examples
pythondjangoformatpython-datetimeiso8601

Convert string datetime with 9 digits in last to ISO format datetime object in python


I have string datetime (eg: "2022-11-11T06:19:32.776289776Z"). I want to convert the same in ISO format.

I tried below

>>> datetime.datetime.fromisoformat("2022-11-11T06:19:32.776289776Z")

But it is throwing error.

ValueError: Invalid isoformat string: '2022-11-11T06:19:32.776289776Z

It is working if I give only 6 digits in the last without Z.

Below example is working for me, however not working for the strings with 9 digits and z in the end:

>>> datetime.datetime.fromisoformat("2022-11-11T06:19:32.776289")

datetime.datetime(2022, 11, 11, 6, 19, 32, 776289)

Solution

  • If Python 3.11 is available

    If you can upgrade to Python 3.11, then fromisoformat can handle this:

    # Python 3.11 and above only
    dt = datetime.datetime.fromisoformat("2022-11-11T06:19:32.776289776Z")
    print(dt)
    

    giving

    2022-11-11 06:19:32.776289+00:00
    

    Earlier versions of Python

    If you're restricted to earlier versions of Python, then as @SalmanA said in the comments, you can get to a valid timestamp by (1) trimming the microseconds down to 6 digits, using re.sub, and (2) replacing Z with +00:00 (str.replace will do that).

    This assumes the timestamp is otherwise validly formatted (e.g. no letter Z anywhere else in the string).

    For example:

    import datetime
    import re
    
    def clean_str(ts):
        return re.sub("(\d{6})(\d+)", r"\1", ts).replace("Z", "+00:00")
    
    tests = ["2022-11-11T06:19:32.776289776Z",
             "2022-11-11T06:19:32.776289Z",
             "2022-11-11T06:19:32.7762897+03:00",
             "2022-11-11T06:19:32.776Z"]
    
    for i, ts in enumerate(tests, 1):
        clean = clean_str(ts)
        dt = datetime.datetime.fromisoformat(clean)
        print(f"({i}): {ts} -> {clean} -> {dt}")
    

    giving:

    (1): 2022-11-11T06:19:32.776289776Z -> 2022-11-11T06:19:32.776289+00:00 -> 2022-11-11 06:19:32.776289+00:00
    (2): 2022-11-11T06:19:32.776289Z -> 2022-11-11T06:19:32.776289+00:00 -> 2022-11-11 06:19:32.776289+00:00
    (3): 2022-11-11T06:19:32.7762897+03:00 -> 2022-11-11T06:19:32.776289+03:00 -> 2022-11-11 06:19:32.776289+03:00
    (4): 2022-11-11T06:19:32.776Z -> 2022-11-11T06:19:32.776+00:00 -> 2022-11-11 06:19:32.776000+00:00
    

    Note that as (1) and (3) show, the microseconds are truncated, not rounded, i.e. 32.776289776 goes to 32.776289 not 32.776290. (And the same goes for the native Python 3.11 solution.) If you wanted true rounding then you would need to parse the string further.