Re: Give the zero time value a location, and it won't survive a roundtrip to JSON?

154 views
Skip to first unread message

Robert Ma

unread,
Nov 27, 2020, 7:15:03 PM11/27/20
to golang-nuts
Is this because the 2-second offset of LMT gets lost in RFC 3339 representation?

On Fri, Nov 27, 2020 at 6:33 PM Robert Ma <robe...@google.com> wrote:
Hi all,

Time is complicated.

Today I found myself in a rabbit hole. For some unrelated reason, I got a time value in a non-UTC location that's otherwise zero (IsZero=true). This value doesn't seem to survive a roundtrip to JSON. See this playground for a minimal reproduction: https://play.golang.org/p/QdglfKYkstS

Is this expected, a bug, or an undefined behaviour? I checked RFC 3339, which time uses as the JSON serialization format, and it seems to support 0000AD to 9999AD, but I have to admit I know little about time.

Cheers,
Robert

Robert Ma

unread,
Nov 27, 2020, 7:15:04 PM11/27/20
to golang-nuts

b.ca...@pobox.com

unread,
Nov 28, 2020, 4:11:09 AM11/28/20
to golang-nuts
I didn't think that 0000AD was a thing, even though RFC 3339 says it is.

Wikipedia says that dates in Common Era and Dionysius BC/AD are equivalent,  and that 1BC led straight onto 1AD.

Matt Harden

unread,
Nov 28, 2020, 4:35:58 AM11/28/20
to Robert Ma, golang-nuts
On Fri, Nov 27, 2020 at 4:14 PM 'Robert Ma' via golang-nuts <golan...@googlegroups.com> wrote:
Is this because the 2-second offset of LMT gets lost in RFC 3339 representation?

Yes, that's exactly it.
 
On Fri, Nov 27, 2020 at 6:33 PM Robert Ma <robe...@google.com> wrote:
Hi all,

Time is complicated.

Wibbly-wobbly, even.

Today I found myself in a rabbit hole. For some unrelated reason, I got a time value in a non-UTC location that's otherwise zero (IsZero=true). This value doesn't seem to survive a roundtrip to JSON. See this playground for a minimal reproduction: https://play.golang.org/p/QdglfKYkstS

Is this expected, a bug, or an undefined behaviour? I checked RFC 3339, which time uses as the JSON serialization format, and it seems to support 0000AD to 9999AD, but I have to admit I know little about time.

RFC 3339 doesn't support sub-minute timezone offsets, so it's not possible to format the LMT zone precisely.

I am concerned that the time printed is incorrect by 2 seconds in this case, and (I imagine) could be anywhere from 0 to 59 seconds off depending on the particular sub-minute timezone offset used. That seems like a real bug, and I don't know what a proper fix would look like. Perhaps when the timezone is formatted in numeric form, the printed time should be adjusted to account for the loss of precision in the zone info. In your case this would print "0000-12-31T19:04:00-04:56" instead of "0000-12-31T19:03:58-04:56". That solution has its own issues though.

You probably should only use IsZero() to detect uninitialized time values; never on a value that's been parsed from any source, even JSON.

To get arbitrary time values to survive a JSON round trip, I think using UTC exclusively would be the best option.


Cheers,
Robert

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAOPAaN%2BWvZ73Z-oMVaGmDt-Gr5DEk7ZtQeU43x5fKrCFW42%3DqQ%40mail.gmail.com.

Axel Wagner

unread,
Nov 28, 2020, 5:22:41 AM11/28/20
to Matt Harden, Robert Ma, golang-nuts
It would also be possible to implement `json.Marshaler` to use a different time format. In particular, it might be reasonable to encode the zero value of time.Time as `null`, instead of a string (though mixed types in json messages are… icky).

Personally, I'm always very cautious about encoding and decoding times. There isn't really a standard way to transmit timezone information, especially as timezone definitions can even change over time, AIUI. So, even if you specify which timezone a point in time should be in, you can't rely on the receiver of the message having the same understanding of that timezone.

Using UTC for storage and transmission is probably a better option. You essentially treat the stored data purely as "a point in time", while timezone info is specific to the presentation. A time.Time isn't just a point in time, though, so if you really marshal "a time.Time", you have to somehow include timezone info - so I kinda understand why the default MarshalJSON method doesn't convert it to UTC first.

Michael Jones

unread,
Nov 29, 2020, 5:34:48 PM11/29/20
to Axel Wagner, Matt Harden, Robert Ma, golang-nuts
Matt’s formatting example suggests interval arithmetic for time. That is a nice idea. 

--
Michael T. Jones
michae...@gmail.com
Reply all
Reply to author
Forward
0 new messages