TimeZones.jl icon indicating copy to clipboard operation
TimeZones.jl copied to clipboard

`ZonedDateTime` can't parse it's own output

Open Keno opened this issue 7 years ago • 21 comments

This seems unfortunate:

julia> ZonedDateTime(DateTime(2014,1,1), TimeZone("Europe/Warsaw"))
2014-01-01T00:00:00+01:00

julia> ZonedDateTime("2014-01-01T00:00:00+01:00")
ERROR: ArgumentError: Unable to parse date time. Expected directive Delim(.) at char 20

Keno avatar Aug 10 '17 16:08 Keno

It's definite possible to get it to parse the output.

Unfortunately the output only shows the UTC offset for that specific timestamp which means that parsing it will result a loss of information since we would have to parse the string as a FixedTimeZone("+01:00") rather than TimeZone("Europe/Warsaw"). Maybe I should change the default show to be 2014-01-01T00:00:00 Europe/Warsaw.

omus avatar Aug 10 '17 17:08 omus

The output is actually fine with me. My concern is more the parsing. In Base, we hack the ISODateTime parsing to make the .sss optional, but here we don't. Of course the ISO8601 complete representation doesn't include milliseconds at all.

Keno avatar Aug 10 '17 17:08 Keno

Although it does say

If necessary for a particular application a decimal fraction of hour, minute or second may be included. If a
decimal fraction is included, lower order time elements (if any) shall be omitted and the decimal fraction shall
be divided from the integer part by the decimal sign specified in ISO 31-0, i.e. the comma [,] or full stop [.]. Of
these, the comma is the preferred sign. If the magnitude of the number is less than unity, the decimal sign
shall be preceded by two zeros in accordance with 3.6. 

so maybe in parsing of various components, we should always allow . and , and treat it as decimal separators?

Keno avatar Aug 10 '17 17:08 Keno

cc also @quinnj

Keno avatar Aug 10 '17 17:08 Keno

I'm following along :)

quinnj avatar Aug 10 '17 17:08 quinnj

The DateFormat specification could use a re-think. It would be nice if when parsing you could state a section was optional. Something like df"yyyy-mm-dd HH:MM:SS(.sss)zzz". Additionally, I dislike that I'm using zzz for reading in +01:00. It would be better to break up that into multiple components like sign, hour, minutes. Something like ±{zone-hour}:{zone-minute} (I'm not sure what letters would be good here...)

omus avatar Aug 10 '17 17:08 omus

I don't think I actually mind zzz that much, because for UTC, the standard specifies Z, which I'd like to match as well.

Keno avatar Aug 10 '17 17:08 Keno

As for letters, there's always hᶻ of course.

Keno avatar Aug 10 '17 17:08 Keno

Mostly the optional specifiers for parsing would be useful.

omus avatar Aug 10 '17 17:08 omus

@rofinn just ran into this one:

julia> ZonedDateTime("2017-01-28T11:59:59+00:00")
ERROR: ArgumentError: Unable to parse date time. Expected directive Delim(.) at char 20

omus avatar May 02 '18 20:05 omus

I ran into the same problem as well (I ended up adding a silly Millisecond(1) to everything). But this touches the problem that the string representation of this type is not complete (the location of the time zone is missing). How to save zoned date & time is an issue a lot of people get hit by. It would be great if there was a function that took an instance of ZonedDateTime and output all the necessary parts (local time and location?) as strings so it could get saved correctly.

yakir12 avatar Nov 02 '18 15:11 yakir12

Something related would be to make sure that repr for ZonedDateTime print as parsable Julia code ZonedDateTime(2018, 11, 2, 12, 55, tz"America/Winnipeg") and have print display the current compact representation 2018-11-02T12:55:00-05:00.

omus avatar Nov 02 '18 17:11 omus

Related: https://github.com/JuliaLang/julia/issues/29909

omus avatar Nov 02 '18 18:11 omus

Excuse my ignorance, but is 2018-11-02T12:55:00-05:00 really enough for correctly saving a zoned date & time...? I thought you'd need the location as well, and not "just" the UTC-offset, à la this example.

yakir12 avatar Nov 02 '18 20:11 yakir12

You correctly save the instant in time but you lose information for, e.g., date math.

iamed2 avatar Nov 02 '18 20:11 iamed2

This is still a problem:

julia> ZonedDateTime("2017-04-14 10:58:40-04", dateformat"YYYY-mm-dd HH:MM:SS.ssszz")
ERROR: ArgumentError: Unable to parse string "2017-04-14 10:58:40-04" using format dateformat"YYYY-mm-dd HH:MM:SS.ssszz". Unable to parse date time. Expected directive Delim(.) at char 20
Stacktrace:
 [1] macro expansion at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.1/Dates/src/parse.jl:104 [inlined]
 [2] tryparsenext_core(::String, ::Int64, ::Int64, ::DateFormat{Symbol("YYYY-mm-dd HH:MM:SS.ssszz"),Tuple{Dates.DatePart{'Y'},Dates.Delim{Char,1},Dates.DatePart{'m'},Dates.Delim{Char,1},Dates.DatePart{'d'},Dates.Delim{Char,1},Dates.DatePart{'H'},Dates.Delim{Char,1},Dates.DatePart{'M'},Dates.Delim{Char,1},Dates.DatePart{'S'},Dates.Delim{Char,1},Dates.DatePart{'s'},Dates.DatePart{'z'}}}, ::Bool) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.1/Dates/src/parse.jl:40
 [3] macro expansion at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.1/Dates/src/parse.jl:150 [inlined]
 [4] tryparsenext_internal at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.1/Dates/src/parse.jl:127 [inlined]
 [5] parse at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.1/Dates/src/parse.jl:282 [inlined]
 [6] ZonedDateTime(::String, ::DateFormat{Symbol("YYYY-mm-dd HH:MM:SS.ssszz"),Tuple{Dates.DatePart{'Y'},Dates.Delim{Char,1},Dates.DatePart{'m'},Dates.Delim{Char,1},Dates.DatePart{'d'},Dates.Delim{Char,1},Dates.DatePart{'H'},Dates.Delim{Char,1},Dates.DatePart{'M'},Dates.Delim{Char,1},Dates.DatePart{'S'},Dates.Delim{Char,1},Dates.DatePart{'s'},Dates.DatePart{'z'}}}) at /home/glynch/.julia/packages/TimeZones/Z0kpK/src/parse.jl:85
 [7] top-level scope at none:0

galenlynch avatar Jun 11 '19 14:06 galenlynch

This is still a problem

Your particular example will always be an issue. If you are passing in a DateFormat explicitly where you are trying to parse milliseconds the parsing will fail.

As a stop gap measure I'm planning on supporting:

parse(ZonedDateTime, str)

which would handle both dateformat"YYYY-mm-dd HH:MM:SSzzz" and dateformat"YYYY-mm-dd HH:MM:SS.ssszzz" as a special case.

I'm also doing so research into different date parsing/formatting syntaxes to see what alternatives I can find and introduce into the Dates stdlib.

omus avatar Jun 14 '19 19:06 omus

Hi @omus , we just came across this too:

julia> str = string(ZonedDateTime("2020-08-12T12:00:00.000+00:00"))
"2020-08-12T12:00:00+00:00"

julia> parse(ZonedDateTime, str)
ERROR: ArgumentError: Unable to parse date time. Expected directive Delim(.) at char 20
Stacktrace:
 [1] macro expansion at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.5/Dates/src/parse.jl:104 [inlined]
 [2] tryparsenext_core(::String, ::Int64, ::Int64, ::DateFormat{Symbol("yyyy-mm-ddTHH:MM:SS.ssszzz"),Tuple{Dates.DatePart{'y'},Dates.Delim{Char,1},Dates.DatePart{'m'},Dates.Delim{Char,1},Dates.DatePart{'d'},Dates.Delim{Char,1},Dates.DatePart{'H'},Dates.Delim{Char,1},Dates.DatePart{'M'},Dates.Delim{Char,1},Dates.DatePart{'S'},Dates.Delim{Char,1},Dates.DatePart{'s'},Dates.DatePart{'z'}}}, ::Bool) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.5/Dates/src/parse.jl:38
 [3] macro expansion at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.5/Dates/src/parse.jl:150 [inlined]
 [4] tryparsenext_internal at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.5/Dates/src/parse.jl:125 [inlined]
 [5] parse(::Type{ZonedDateTime}, ::String, ::DateFormat{Symbol("yyyy-mm-ddTHH:MM:SS.ssszzz"),Tuple{Dates.DatePart{'y'},Dates.Delim{Char,1},Dates.DatePart{'m'},Dates.Delim{Char,1},Dates.DatePart{'d'},Dates.Delim{Char,1},Dates.DatePart{'H'},Dates.Delim{Char,1},Dates.DatePart{'M'},Dates.Delim{Char,1},Dates.DatePart{'S'},Dates.Delim{Char,1},Dates.DatePart{'s'},Dates.DatePart{'z'}}}) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.5/Dates/src/parse.jl:282
 [6] parse(::Type{ZonedDateTime}, ::String) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.5/Dates/src/parse.jl:281
 [7] top-level scope at REPL[13]:1

Ref the Time Standard: https://en.wikipedia.org/wiki/ISO_8601

dehann avatar Aug 12 '20 14:08 dehann

This seems unfortunate:

julia> ZonedDateTime(DateTime(2014,1,1), TimeZone("Europe/Warsaw"))
2014-01-01T00:00:00+01:00

julia> ZonedDateTime("2014-01-01T00:00:00+01:00")
ERROR: ArgumentError: Unable to parse date time. Expected directive Delim(.) at char 20

Is there plans to fix this? I was going to open an issue as I ran to this exact problem. Shouldn't ZonedDateTime be able to parse an valid ISO 8601 string?

acdupont avatar Nov 13 '22 14:11 acdupont

It's definite possible to get it to parse the output.

Unfortunately the output only shows the UTC offset for that specific timestamp which means that parsing it will result a loss of information since we would have to parse the string as a FixedTimeZone("+01:00") rather than TimeZone("Europe/Warsaw"). Maybe I should change the default show to be 2014-01-01T00:00:00 Europe/Warsaw.

The loss of information did not occur at parse time, it occured when converting the ZonedDateTime to a String

acdupont avatar Nov 13 '22 14:11 acdupont

Also note that while this 2014-01-01T00:00:00+01:00 fails, this 2014-01-01T00:00:00.000+01:00 can be parsed (added .000 milliseconds).

acdupont avatar Nov 13 '22 14:11 acdupont