Dates/TimeZones clash when decoding string date with terminal Z

Dates.DateTime() can decode string dates with a terminal Z (for UTC). However, if TimeZones has been loaded, it fails, even using the module name (Dates.DateTime()).

What’s the best way of dealing with this?

julia> using Dates

julia> Dates.DateTime("2024-11-23T15:32:14.211Z", "yyyy-mm-ddTHH:MM:SS.sssZ")
2024-11-23T15:32:14.211

julia> using TimeZones

julia> Dates.DateTime("2024-11-23T15:32:14.211Z", "yyyy-mm-ddTHH:MM:SS.sssZ")
ERROR: ArgumentError: Unable to parse date time. Expected directive DatePart(Z) at char 24

A few different things are going on here:

There is no character code Z in the stdlib Dates per the Julia documentation. So, you’re matching a literal Z in your first example. It’s parsed like any of the delimiters.

TimeZones.jl overloads the Dates.jl functions with additional methods that accept Z or z as the character codes for time zones. So, it doesn’t matter if you prefix the functions with Dates..

TimeZones.jl uses the IANA database that doesn’t have a zone called Z. See the valid names with TimeZones.timezone_abbrs() or here.

I know, Z is often used to indicate UTC but it’s not in the official database. I usually just cut the Z off or replace it:

julia> DateTime(chopsuffix("2024-11-23T15:32:14.211Z", "Z"), dateformat"yyyy-mm-ddTHH:MM:SS.sssZ")

julia> DateTime(replace("2024-11-23T15:32:14.211Z", "Z"=>"+0000"), dateformat"yyyy-mm-ddTHH:MM:SS.ssszzzz")

Note the use of the dateformat"" macro instead of passing a plain format string. The latter is pretty expensive if you do that often.

2 Likes

This is a good example of why you should always escape character literals that shouldn’t have a special meaning in the dateformat[1] (like T and Z in this example):

julia> using Dates

julia> fmt = "yyyy-mm-dd\\THH:MM:SS.sss\\Z"; # note the double \\

julia> Dates.DateTime("2024-11-23T15:32:14.211Z", fmt)
2024-11-23T15:32:14.211

julia> using TimeZones

julia> Dates.DateTime("2024-11-23T15:32:14.211Z", fmt)
2024-11-23T15:32:14.211

Note also that it is better to create the dateformat ones (instead of having to parse the formatting string on every call). This can be done with the dateformat string macro:

julia> dfmt = dateformat"yyyy-mm-dd\THH:MM:SS.sss\Z" # note the single \
dateformat"yyyy-mm-dd\THH:MM:SS.sss\Z"

julia> Dates.DateTime("2024-11-23T15:32:14.211Z", dfmt)
2024-11-23T15:32:14.211

  1. The way Dates and TimeZones interact this way isn’t very nice though, but atleast you are immune to these types of errors if you escape. ↩︎

3 Likes

This must be due to some piracy in the TimeZones package, right? One doesn’t really expect simply loading another package (that may happen indirectly) to affect basic Date parsing. Escaping non-control characters isn’t even mentioned in the DateFormat docs afaict.

See https://github.com/JuliaTime/TimeZones.jl/issues/303 and https://github.com/JuliaLang/julia/pull/38959 for some prior discussion.

1 Like

Thanks for the very informative replies. The treatment of Z is a bit irritating, but I see it has a logic. Both chopsuffix() and escaping look like sufficient fixes for me.