You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As is, time.Time is not transmittable/storable on its own where it will preserve its location. All of the Marshal/Unmarshal implementations and all the supported string formats encode only the offset or the timezone abbreviation. When parsed or unmarshalled, you often end up with a time.Time in a fixed offset, rather than the original location. In particular, this can lead to unexpected results when performing some operations around Daylight Savings Time because the fixed offset time.Time may or may not observe DST when the original location-laden time.Time would have. Depending on the server location, it's also possible to end up applying DST when it shouldn't apply because of time.Parse's behavior to use time.Local if the local offset matches the parsed timestamp.
As a hallmark example of the problem here is to ask: what month will it be if you add 2840 hours to the reference time used for string formatting? See: https://play.golang.org/p/XQiDRKpKtSa
Overall, this subtlety breaks assumptions that encoded timestamps convey all the information on the original time.Time, and that a decoded time.Time will behave consistently no matter where it's decoded. While a workaround is possible by always separately storing/transmitting the location string in addition to the timestamp, the time package could make that more transparent or at least alert users that the encode/decode may behave unexpectedly with regard to precise timezone rules.
The proposal is to do one or more of the following options. Note that as the author here, my current thinking is option 2 plus option 5 would be the best way forward, but I've listed more possibilities for completeness.
Add the IANA location to what MarshalBinary and UnmarshalBinary do
This will allow gob to losslessly transmit timestamps that will behave the same after encoding/decoding. There are a couple considerations that may rule out this idea:
The change might be backward incompatible. I'm not sure if there are Go programs relying on the fact that currently the encoding format results in fixed-offset times after decode.
The "improved" parser would still need to handle encoded data from before the change
Add a Format/Parse directive that includes the IANA location string
This idea is less transparent than the Binary/Gob approach because it requires specifically using a format that includes location. This means it's backward compatible and introduces minimal additional API surface. This possibility still must tackle the problem of how to proceed on systems without available tzdata.
Add an exported struct field to time.Time to toggle encoding behavior
Because the current JSON/text encoding mechanisms for time.Time specify RFC3339, they cannot be changed outright to include the actual location. However, a field could be added to time.Time to alter the encoding behavior. Because the default state of this field could be to encode as the existing format, backward compatibility would be preserved. The decoding routines would need to account for all possible formats and could set that field as necessary.
The two major downsides to this approach I can think of:
Additional memory required by time.Time objects
That == comparisons would include this field, which seems beyond its intended purpose
Break compatibility in Go2 and include the IANA location in all Marshal/Unmarshal encodings
The big benefit here would be to make time.Time, as an implementation of package encoding's interfaces, to be more true to those interfaces. While package encoding doesn't currently specify whether or not implementations must completely represent the receiver's contents, one might assume they do since generally the process is often treated such that x = decode(encode(x)).
Document this behavior
It's worth warning people that existing encoding methods do not preserve the full location information and that if the program needs to do calendrical operations which may be affected by changes like DST, to transmit/store both the encoded time.Time and its location separately. In particular, since the docs don't specify any format or specifics about the data, one might assume the binary/Gob encoding preserves the full state of the time.Time, when it actually doesn't. While technically this fact is implied for the JSON/text encodings, it's probably a common misconception that the offset in RFC3339 adequately represents location via the offset.
edit: added some formatting
The text was updated successfully, but these errors were encountered:
bradfitz
changed the title
time: proposal: add an encoding or formatting feature to encode and transmit time.Time including location
proposal: time: add an encoding or formatting feature to encode and transmit time.Time including location
Oct 26, 2018
After discussion with the proposal committee, we agreed it's best to go with option 5 and document the behavior since you can implement option 2 on your own without any changes to the standard library.
Accepting this proposal to document the behavior only. Thanks.
andybons
changed the title
proposal: time: add an encoding or formatting feature to encode and transmit time.Time including location
time: document that time.Time does not include location
Oct 31, 2018
bradfitz
changed the title
time: document that time.Time does not include location
time: document that a marshaled time does not include location name
Oct 31, 2018
As is,
time.Time
is not transmittable/storable on its own where it will preserve its location. All of the Marshal/Unmarshal implementations and all the supported string formats encode only the offset or the timezone abbreviation. When parsed or unmarshalled, you often end up with atime.Time
in a fixed offset, rather than the original location. In particular, this can lead to unexpected results when performing some operations around Daylight Savings Time because the fixed offsettime.Time
may or may not observe DST when the original location-ladentime.Time
would have. Depending on the server location, it's also possible to end up applying DST when it shouldn't apply because oftime.Parse
's behavior to usetime.Local
if the local offset matches the parsed timestamp.As a hallmark example of the problem here is to ask: what month will it be if you add 2840 hours to the reference time used for string formatting? See: https://play.golang.org/p/XQiDRKpKtSa
Overall, this subtlety breaks assumptions that encoded timestamps convey all the information on the original
time.Time
, and that a decodedtime.Time
will behave consistently no matter where it's decoded. While a workaround is possible by always separately storing/transmitting the location string in addition to the timestamp, the time package could make that more transparent or at least alert users that the encode/decode may behave unexpectedly with regard to precise timezone rules.The proposal is to do one or more of the following options. Note that as the author here, my current thinking is option 2 plus option 5 would be the best way forward, but I've listed more possibilities for completeness.
MarshalBinary
andUnmarshalBinary
doThis will allow
gob
to losslessly transmit timestamps that will behave the same after encoding/decoding. There are a couple considerations that may rule out this idea:This idea is less transparent than the Binary/Gob approach because it requires specifically using a format that includes location. This means it's backward compatible and introduces minimal additional API surface. This possibility still must tackle the problem of how to proceed on systems without available tzdata.
time.Time
to toggle encoding behaviorBecause the current JSON/text encoding mechanisms for
time.Time
specify RFC3339, they cannot be changed outright to include the actual location. However, a field could be added to time.Time to alter the encoding behavior. Because the default state of this field could be to encode as the existing format, backward compatibility would be preserved. The decoding routines would need to account for all possible formats and could set that field as necessary.The two major downsides to this approach I can think of:
time.Time
objects==
comparisons would include this field, which seems beyond its intended purposeMarshal
/Unmarshal
encodingsThe big benefit here would be to make
time.Time
, as an implementation of packageencoding
's interfaces, to be more true to those interfaces. While packageencoding
doesn't currently specify whether or not implementations must completely represent the receiver's contents, one might assume they do since generally the process is often treated such thatx = decode(encode(x))
.It's worth warning people that existing encoding methods do not preserve the full location information and that if the program needs to do calendrical operations which may be affected by changes like DST, to transmit/store both the encoded
time.Time
and its location separately. In particular, since the docs don't specify any format or specifics about the data, one might assume the binary/Gob encoding preserves the full state of thetime.Time
, when it actually doesn't. While technically this fact is implied for the JSON/text encodings, it's probably a common misconception that the offset in RFC3339 adequately represents location via the offset.edit: added some formatting
The text was updated successfully, but these errors were encountered: