Parse "Z" timezone suffix in datetime

Most have been said here already, just wanted to add that I also arrived at this page after receiving JSON data from a JavaScript application.

Also, using dateutil gives a slightly different result. Compare:

import dateutil.parser as dt
date_time_obj = dt.parse('2020-02-25T13:13:43.913Z')
date_time_obj
# out: datetime.datetime(2020, 2, 25, 13, 13, 43, 913000, tzinfo=tzutc())
date_time_obj2 = datetime.fromisoformat('2020-02-25T13:13:43.913Z'.replace('Z', '+00:00'))
date_time_obj2
# out: datetime.datetime(2020, 2, 25, 13, 13, 43, 913000, tzinfo=datetime.timezone.utc)

A difference in tzinfo with tzutc() vs datetime.timezone.utc.
Although that doesn’t seem to be a problem:

date_time_obj == date_time_obj2
# out: True
1 Like

It would be really useful if the documentation explicitly stated that Z is not parsed and must be replaced with +00:00

4 Likes

I think one thing that might be worth to add is that Django’s DjangoJSONEncoder also uses ‘Z’. This is the reason that I ended up here.

So it’s not just other popular languages like Javascript that uses the format. One of the most popular Python web frameworks uses it for JSON encoding datetimes.

5 Likes

I wanted to note that the replace('Z', '00+00') workaround makes parsing dates around 5x as slow (I ran a benchmark of various date parsing libraries and functions; see linked gist).

I think having a very fast ISO parsing function in the python standard library is important.

Date parsing benchmark (requires packages: pytest, pytest-benchmark, python-dateutil, ciso8601, iso8601)

4 Likes

Another gotcha is that datetime.datetime.fromisoformat is picky about fractional seconds:

  • 2020-01-01T12:33:56 :white_check_mark:
  • 2020-01-01T12:33:56.0 :x:
  • 2020-01-01T12:33:56.000 :white_check_mark: (milliseconds)
  • 2020-01-01T12:33:56.0000 :x:
  • 2020-01-01T12:33:56.000000 :white_check_mark: (microseconds)
  • 2020-01-01T12:33:56.00000 :x: # commonly generated by JavaScript libraries
  • 2020-01-01T12:33:56.0000000 :x:
  • 2020-01-01T12:33:56.000000000 :x: (nanoseconds) # golang, tc39 Temporal
10 Likes

So, it’s been awhile now and I wanted to ask if there is still a strong opposition to a minimal code and documentation patch adding support for Z?

We have to interact with other languages a lot and depending on third-party packages is very cumbersome for stdlib-only CI scripts and such.

Timezone stuff landing in Python was a huge deal, because we had to depend on third-party stuff in the past, and now we don’t have to anymore. Getting this small wart out of the way is a small step for Python developers, but a big step for humanity.

5 Likes

datetime.fromisoformat() is the inverse operation of datetime.isoformat(), which is to say that every valid input to datetime.fromisoformat() is a possible output of datetime.isoformat(), and every possible output of datetime.isoformat() is a valid input to datetime.fromisoformat().

I am not sure if I fully am in line with this objection. If “Z” is taken to denote the same referent as “00+00”, then the symmetry still stands:

  1. Python has symmetrical functions datetime.fromisoformat() and datetime.isoformat(), which point to the same referent.

  2. Python adds an ad-hoc rule to datetime.fromisoformat(), which makes this function recognize one input (strings ending with “Z”) as having the same referent as another (strings ending with “+00:00”).

  3. Python now has symmetrical functions datetime.fromisoformat() and datetime.isoformat() which point to the same referent, but the former has been enriched with an ad-hoc rule.

Do Python developers want to avoid scenarios where "2014-12-10 12:00:00Z" is transformed into a Python object, which then gets translated back into a different string with the same referent "2014-12-10 12:00:00+00:00"? I personally see nothing wrong or asymmetrical with this conversion rule, since I cannot envision a scenario where this might cause problems further down the road. Are there any reasonable kind of scenarios where one would strictly expect a datetime object generated from a string with an explicit “Z” to never covert back to a string with an explicit “+00:00”?

1 Like