-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ambiguities in UTC()'s spec and mis-specification of DaylightSavingTA() #725
Comments
Unfortunately, because the Date() constructor [0] embeds a call to UTC(), the problems above with UTC()'s spec do in fact imply problems for constructing Date() objects representing certain dates and times. Because the Date(y,mon,[d,h,min,s,ms]) constructor interprets its arguments in local time, yet yields a result that wants to believe it is a UTC time, there are times (in local time's fall-backs) that cannot be represented (the implicit disambiguation selects the other local time with the same seconds since epoch, but a different implicit nominal epoch). If a zone makes its DST transition at midnight, this can break the "obvious" way to construct a Date() representing the day the transition happens; Date(year, month, day) selects 00:00 local-time at the day's start, but UTC()'s half-hearted attempt at correcting for local offset when determining DaylightSavingTA() leads to it getting a time actually an hour earlier, in the preceding day once represented as a local time: see [1]. [1] https://bugreports.qt.io/browse/QTBUG-54559 In short, the failure to make UTC(Localtime(t)) == t and Localtime(UTC(t)) == t, combined with embedding UTC() in Date(), makes some times (in any zone with DST) unrepresentable and (in some zones) forces unnatural handling of some dates as Date()s. This could be ameliorated by giving Date() more optional arguments, to avoid the ambiguity by letting the caller specify the given data is in fact in UTC already, or specify which side of the DST transition they are. [Edit: well, "unrepresentable" claims too much; there are times that Date() can't directly give me, but other methods can solve the problem. It remains painful that Date() is broken.] |
I can and will fix the lacking notes you mention (that UTC(Local(t)) !== t, Local(UTC(t)) !== t, and that t is presumed to be a UTC time), but most of this is a valid critique of the current date design. Changes here must be advanced via the standard staged proposal (see https://github.com/tc39/proposals for more details). |
While working on https://bugs.chromium.org/p/v8/issues/detail?id=3547, I realized that Ecma262 spec has a few issues. I second @ediosyncratic on Date() in general and the timezone offset handling ( UTC() , LocalTime() and Date(y,mon,[d,h,min,s,ms]) interpreting the input parameters in local timezone ) in particular. Assuming that LocalTZA is constant over the history of a given timezone is very problematic. For instance, Europe/Moscow changed its timezone offset (and whether or not to use DST) multiple times, but LocalTZA is always calculated to be the LocalTZA at run-time. One example: Europe/Moscow was UTC+0400 in Sep 2014, but it's now UTC+0300 (year-round). See the result below:
|
It turned out that Firefox does the right thing from a user's point of view not following the spec to its letters. (the result in my previous comment was taken from v8/Chromium). WIth the timezone set to Europe/Moscow, I got this from Firefox.
As for skipped wall time (e.g. Spring forward) and repeated wall time (Fall backward), ICU Calendar API has SkippedWallTimeOption and RepeatedWallTimeOption |
Safari/JSC also behaves like Firefox. From Firefox:
From Safari:
It appears that both Firefox and Safari treat LocalTZA as time-dependent (as it should) even though the spec does not say so (otoh, the spec makes it explicit that DST Adj is time-dependent). I fully agree with @ediosyncratic that OffsetFromUTC(u) is a cleaner/better way to handle timezone offset than splitting the offset into two parts (LocalTZA and DST Adj). Moreover, a way to handle skipped or repeated wall times need to be introduced as well. That requires a rather extensive change in the spec. In the medium-to-long term, that must be done. In the meantime, I wonder if it's possible to make a "minor edit" (maybe via a PR) of making LocalTZA time-dependent given that that's what at least two implementations do and I'm planning to align v8 with them. ( (will check Edge and IE 11) ). @bterlson , @littledan , @ediosyncratic , what do you think of that?
|
@jungshik I would likely take such a PR, although I'm not an expert here (I wonder if @maggiepint has any thoughts). I'd suggest doing a PR this week so we can discuss next week in committee if necessary! |
@bterlson thank you for the reply. It seems more involved than a simple edit because UTC(t) is defined as following (OTOH, LocalTime(t) abstract operation would be well-defined with LocalTZA being t-dependent).
If we make LocalTZA depend on 't' where 't' is UTC, there'd be a 'cycle'.
LocalTZA can be spec'd to take either UTC or local time (ugly...) and a warning can be added that it'd be ambiguous when there's a timezone offset change, but it's rather ugly. Perhaps, better would be to introduce In the future, options for skipped and repeated walltimes can be added. |
Europe/Moscow timezone changes in this decade are summarized at https://www.timeanddate.com/time/zone/russia/moscow On 2011-02-27, 2:00 AM (localtime) => 3:00AM in Moscow
On 2014-10-26, 2:00 AM (localtime) => 1:00 AM in Moscow
Both Firefox and Safari use a 'new' offset (offset from UTC after a timezone change) to interpret skipped and repeated wall times. |
Currently, LocalTimezoneAdjustment is assumed to be constant across time zone rule changes and does not take any argument. Daylight Savings Time Adjustment does take an argument t. DSTA(t) can 'absorb' the standard time zone offset changes (e.g. Europe/Moscow changed the standard time zone offset multiple times in the last decade.). However, the spec stipulates that it should only reprsent an additional adjustment necessary when DST is in effect. In practice, at least two implementations (Spidermonkey/Firefox and JSC/Safari) do not follow the spec to the letter to produce results expected by users across time zone rule changes. This PR drops LocalTZA and DaylightSavingsTimeAdjustment(t) and introduce TZOffsetUTC(t) and TZOffsetLocal(t_local). The former takes 't' (UTC) and gives a time zone offset in effect at t for the local time zone (for US ET, it'd be 4*msPerHour in summer and 5*msPerHour in winter). The latter takes 't_local' (local time value) and gives a time zone offset in effect at t_local. It's also specified that TZOffsetLocal(t_local) will return the offset *after* the transition when t_local is wall time repeating multiple times at a negative transition (e.g. fall backward) or skipped wall time at a positive time zone transition (e.g. spring forward). This is in line with the aforementioned two implementations. In the future, it may as well be considered to introduce options to control this behavior. [1] [2] UTC(t_local) and Localtime(t) are reformulated with TZOffsetLocal(t_local) and TZOffsetUTC(t). Will fix tc39#725 modulo an option to specify the behavior to handle skipped or repeated wall time. [1] ICU Calendar API has skipped wall time option (https://goo.gl/h0bP26) and repeated wall time option (https://goo.gl/Q1VX3j). [2] Python proposal to handle skipped/repeated time: https://www.python.org/dev/peps/pep-0495/
Currently, LocalTimezoneAdjustment is assumed to be constant across time zone rule changes and does not take any argument. Daylight Savings Time Adjustment does take an argument t. DSTA(t) can 'absorb' the standard time zone offset changes (e.g. Europe/Moscow changed the standard time zone offset multiple times in the last decade.). However, the spec stipulates that it should only reprsent an additional adjustment necessary when DST is in effect. In practice, at least two implementations (Spidermonkey/Firefox and JSC/Safari) do not follow the spec to the letter to produce results expected by users across time zone rule changes. This PR revises LocalTZA to accept two arguments, 't' and 'isUTC' and drop DaylightSavingsTimeAdjustment(t). When isUTC is true, LocalTZA interprets 't' as a time value representing UTC and gives a time zone offset in effect at time t for the local time zone (for US ET, it'd be 4*msPerHour in summer and 5*msPerHour in winter). When isUTC is false, 't' is interpreted as local time value and gives a time zone offset in effect at t = t_local. It's also specified that LocalTZA(t = t_local, false) will return the offset *after* the transition when t_local is wall time repeating multiple times at a negative transition (e.g. fall backward) or skipped wall time at a positive time zone transition (e.g. spring forward). This is in line with the aforementioned two implementations. In the future, it may as well be considered to introduce options to control this behavior. [1] [2] UTC(t) and Localtime(t) are reformulated with LocalTZA(t = t_local, false) and LocalTZA(t = t_UTC, true). Will fix tc39#725 modulo an option to specify the behavior to handle skipped or repeated wall time. [1] ICU Calendar API has skipped wall time option (https://goo.gl/h0bP26) and repeated wall time option (https://goo.gl/Q1VX3j). [2] Python proposal to handle skipped/repeated time: https://www.python.org/dev/peps/pep-0495/
Safari's handling of Pacific/Apia is interesting. At the end of 2011-12-29 local time, Pacific/Apia moved from UTC-1000 to UTC+1400. So, the entire day of 2011-12-30 is skipped in Pacific/Apia. Up to (not including) 2011-12-30 23:00 (local time) is interpreted as in the time zone before the switch, but 2011-12-30 23:00 or later is treated in the time zone after the switch.
Anyway, JSC/Safari can't be blamed for this behavior because the spec was ambiguous. PR #778 clarified the behavior around a time zone offset change. |
Firefox cannot be tested because its handling of Pacific/Apia is broken at least on Mac OS.
|
Currently, LocalTimezoneAdjustment is assumed to be constant across time zone rule changes and does not take any argument. Daylight Savings Time Adjustment does take an argument t. DSTA(t) can 'absorb' the standard time zone offset changes (e.g. Europe/Moscow changed the standard time zone offset multiple times in the last decade.). However, the spec stipulates that it should only reprsent an additional adjustment necessary when DST is in effect. In practice, at least two implementations (Spidermonkey/Firefox and JSC/Safari) do not follow the spec to the letter to produce results expected by users across time zone rule changes. This PR revises LocalTZA to accept two arguments, 't' and 'isUTC' and drop DaylightSavingsTimeAdjustment(t). When isUTC is true, LocalTZA interprets 't' as a time value representing UTC and gives a time zone offset in effect at time t for the local time zone (for US ET, it'd be 4*msPerHour in summer and 5*msPerHour in winter). When isUTC is false, 't' is interpreted as local time value and gives a time zone offset in effect at t = t_local. It's also specified that LocalTZA(t = t_local, false) will return the offset *before* the transition when t_local is wall time repeating multiple times at a negative transition (e.g. fall backward) or skipped wall time at a positive time zone transition (e.g. spring forward). This is to get rid of an ambiguity in handling a time zone offset transition. Due to the ambiguity, different implemenations have different behaviors and some implementations have changed their behavior over the time. UTC(t) and Localtime(t) are reformulated with LocalTZA(t = t_local, false) and LocalTZA(t = t_UTC, true). Will fix tc39#725. In the future, it might as well be considered to add an option to specify the behavior to handle skipped or repeated wall time. See [1] and [2]. [1] ICU Calendar API has skipped wall time option (https://goo.gl/h0bP26) and repeated wall time option (https://goo.gl/Q1VX3j). [2] Python proposal to handle skipped/repeated time: https://www.python.org/dev/peps/pep-0495/
Currently, LocalTimezoneAdjustment is assumed to be constant across time zone rule changes and does not take any argument. Daylight Savings Time Adjustment does take an argument t. DSTA(t) can 'absorb' the standard time zone offset changes (e.g. Europe/Moscow changed the standard time zone offset multiple times in the last decade.). However, the spec stipulates that it should only reprsent an additional adjustment necessary when DST is in effect. In practice, at least two implementations (Spidermonkey/Firefox and JSC/Safari) do not follow the spec to the letter to produce results expected by users across time zone rule changes. This PR revises LocalTZA to accept two arguments, 't' and 'isUTC' and drop DaylightSavingsTimeAdjustment(t). When isUTC is true, LocalTZA interprets 't' as a time value representing UTC and gives a time zone offset in effect at time t for the local time zone (for US ET, it'd be 4*msPerHour in summer and 5*msPerHour in winter). When isUTC is false, 't' is interpreted as local time value and gives a time zone offset in effect at t = t_local. It's also specified that LocalTZA(t = t_local, false) will return the offset *before* the transition when t_local is wall time repeating multiple times at a negative transition (e.g. fall backward) or skipped wall time at a positive time zone transition (e.g. spring forward). This is to get rid of an ambiguity in handling a time zone offset transition. Due to the ambiguity, different implemenations have different behaviors and some implementations have changed their behavior over the time. UTC(t) and Localtime(t) are reformulated with LocalTZA(t = t_local, false) and LocalTZA(t = t_UTC, true). Will fix tc39#725. In the future, it might as well be considered to add an option to specify the behavior to handle skipped or repeated wall time. See [1] and [2]. [1] ICU Calendar API has skipped wall time option (https://goo.gl/h0bP26) and repeated wall time option (https://goo.gl/Q1VX3j). [2] Python proposal to handle skipped/repeated time: https://www.python.org/dev/peps/pep-0495/
Currently, LocalTimezoneAdjustment is assumed to be constant across time zone rule changes and does not take any argument. Daylight Savings Time Adjustment does take an argument t. DSTA(t) can 'absorb' the standard time zone offset changes (e.g. Europe/Moscow changed the standard time zone offset multiple times in the last decade.). However, the spec stipulates that it should only reprsent an additional adjustment necessary when DST is in effect. In practice, at least two implementations (Spidermonkey/Firefox and JSC/Safari) do not follow the spec to the letter to produce results expected by users across time zone rule changes. This PR revises LocalTZA to accept two arguments, 't' and 'isUTC' and drop DaylightSavingsTimeAdjustment(t). When isUTC is true, LocalTZA interprets 't' as a time value representing UTC and gives a time zone offset in effect at time t for the local time zone (for US ET, it'd be 4*msPerHour in summer and 5*msPerHour in winter). When isUTC is false, 't' is interpreted as local time value and gives a time zone offset in effect at t = t_local. It's also specified that LocalTZA(t = t_local, false) will return the offset *before* the transition when t_local is wall time repeating multiple times at a negative transition (e.g. fall backward) or skipped wall time at a positive time zone transition (e.g. spring forward). This is to get rid of an ambiguity in handling a time zone offset transition. Due to the ambiguity, different implemenations have different behaviors and some implementations have changed their behavior over the time. UTC(t) and Localtime(t) are reformulated with LocalTZA(t = t_local, false) and LocalTZA(t = t_UTC, true). Will fix tc39#725. In the future, it might as well be considered to add an option to specify the behavior to handle skipped or repeated wall time. See [1] and [2]. [1] ICU Calendar API has skipped wall time option (https://goo.gl/h0bP26) and repeated wall time option (https://goo.gl/Q1VX3j). [2] Python proposal to handle skipped/repeated time: https://www.python.org/dev/peps/pep-0495/
Currently, LocalTimezoneAdjustment is assumed to be constant across time zone rule changes and does not take any argument. Daylight Savings Time Adjustment does take an argument t. DSTA(t) can 'absorb' the standard time zone offset changes (e.g. Europe/Moscow changed the standard time zone offset multiple times in the last decade.). However, the spec stipulates that it should only reprsent an additional adjustment necessary when DST is in effect. In practice, at least two implementations (Spidermonkey/Firefox and JSC/Safari) do not follow the spec to the letter to produce results expected by users across time zone rule changes. This PR revises LocalTZA to accept two arguments, 't' and 'isUTC' and drop DaylightSavingsTimeAdjustment(t). When isUTC is true, LocalTZA interprets 't' as a time value representing UTC and gives a time zone offset in effect at time t for the local time zone (for US ET, it'd be 4*msPerHour in summer and 5*msPerHour in winter). When isUTC is false, 't' is interpreted as local time value and gives a time zone offset in effect at t = t_local. It's also specified that LocalTZA(t = t_local, false) will return the offset *before* the transition when t_local is wall time repeating multiple times at a negative transition (e.g. fall backward) or skipped wall time at a positive time zone transition (e.g. spring forward). This is to get rid of an ambiguity in handling a time zone offset transition. Due to the ambiguity, different implemenations have different behaviors and some implementations have changed their behavior over the time. UTC(t) and Localtime(t) are reformulated with LocalTZA(t = t_local, false) and LocalTZA(t = t_UTC, true). Will fix tc39#725. In the future, it might as well be considered to add an option to specify the behavior to handle skipped or repeated wall time. See [1] and [2]. [1] ICU Calendar API has skipped wall time option (https://goo.gl/h0bP26) and repeated wall time option (https://goo.gl/Q1VX3j). [2] Python proposal to handle skipped/repeated time: https://www.python.org/dev/peps/pep-0495/
Currently, LocalTimezoneAdjustment is assumed to be constant across time zone rule changes and does not take any argument. Daylight Savings Time Adjustment does take an argument t. DSTA(t) can 'absorb' the standard time zone offset changes (e.g. Europe/Moscow changed the standard time zone offset multiple times in the last decade.). However, the spec stipulates that it should only reprsent an additional adjustment necessary when DST is in effect. In practice, at least two implementations (Spidermonkey/Firefox and JSC/Safari) do not follow the spec to the letter to produce results expected by users across time zone rule changes. This PR revises LocalTZA to accept two arguments, 't' and 'isUTC' and drop DaylightSavingsTimeAdjustment(t). When isUTC is true, LocalTZA interprets 't' as a time value representing UTC and gives a time zone offset in effect at time t for the local time zone (for US ET, it'd be 4*msPerHour in summer and 5*msPerHour in winter). When isUTC is false, 't' is interpreted as local time value and gives a time zone offset in effect at t = t_local. It's also specified that LocalTZA(t = t_local, false) will return the offset *before* the transition when t_local is wall time repeating multiple times at a negative transition (e.g. fall backward) or skipped wall time at a positive time zone transition (e.g. spring forward). This is to get rid of an ambiguity in handling a time zone offset transition. Due to the ambiguity, different implemenations have different behaviors and some implementations have changed their behavior over the time. UTC(t) and Localtime(t) are reformulated with LocalTZA(t = t_local, false) and LocalTZA(t = t_UTC, true). Will fix tc39#725. In the future, it might as well be considered to add an option to specify the behavior to handle skipped or repeated wall time. See [1] and [2]. [1] ICU Calendar API has skipped wall time option (https://goo.gl/h0bP26) and repeated wall time option (https://goo.gl/Q1VX3j). [2] Python proposal to handle skipped/repeated time: https://www.python.org/dev/peps/pep-0495/
Currently, LocalTimezoneAdjustment is assumed to be constant across time zone rule changes and does not take any argument. Daylight Savings Time Adjustment does take an argument t. DSTA(t) can 'absorb' the standard time zone offset changes (e.g. Europe/Moscow changed the standard time zone offset multiple times in the last decade.). However, the spec stipulates that it should only reprsent an additional adjustment necessary when DST is in effect. In practice, at least two implementations (Spidermonkey/Firefox and JSC/Safari) do not follow the spec to the letter to produce results expected by users across time zone rule changes. This PR revises LocalTZA to accept two arguments, 't' and 'isUTC' and drop DaylightSavingsTimeAdjustment(t). When isUTC is true, LocalTZA interprets 't' as a time value representing UTC and gives a time zone offset in effect at time t for the local time zone (for US ET, it'd be 4*msPerHour in summer and 5*msPerHour in winter). When isUTC is false, 't' is interpreted as local time value and gives a time zone offset in effect at t = t_local. It's also specified that LocalTZA(t = t_local, false) will return the offset *before* the transition when t_local is wall time repeating multiple times at a negative transition (e.g. fall backward) or skipped wall time at a positive time zone transition (e.g. spring forward). This is to get rid of an ambiguity in handling a time zone offset transition. Due to the ambiguity, different implemenations have different behaviors and some implementations have changed their behavior over the time. UTC(t) and Localtime(t) are reformulated with LocalTZA(t = t_local, false) and LocalTZA(t = t_UTC, true). Will fix tc39#725. In the future, it might as well be considered to add an option to specify the behavior to handle skipped or repeated wall time. See [1] and [2]. [1] ICU Calendar API has skipped wall time option (https://goo.gl/h0bP26) and repeated wall time option (https://goo.gl/Q1VX3j). [2] Python proposal to handle skipped/repeated time: https://www.python.org/dev/peps/pep-0495/
* Make LocalTZA take 't' and 'isUTC', Drop DSTA(t). Currently, LocalTimezoneAdjustment is assumed to be constant across time zone rule changes and does not take any argument. Daylight Savings Time Adjustment does take an argument t. DSTA(t) can 'absorb' the standard time zone offset changes (e.g. Europe/Moscow changed the standard time zone offset multiple times in the last decade.). However, the spec stipulates that it should only reprsent an additional adjustment necessary when DST is in effect. In practice, at least two implementations (Spidermonkey/Firefox and JSC/Safari) do not follow the spec to the letter to produce results expected by users across time zone rule changes. This PR revises LocalTZA to accept two arguments, 't' and 'isUTC' and drop DaylightSavingsTimeAdjustment(t). When isUTC is true, LocalTZA interprets 't' as a time value representing UTC and gives a time zone offset in effect at time t for the local time zone (for US ET, it'd be 4*msPerHour in summer and 5*msPerHour in winter). When isUTC is false, 't' is interpreted as local time value and gives a time zone offset in effect at t = t_local. It's also specified that LocalTZA(t = t_local, false) will return the offset *before* the transition when t_local is wall time repeating multiple times at a negative transition (e.g. fall backward) or skipped wall time at a positive time zone transition (e.g. spring forward). This is to get rid of an ambiguity in handling a time zone offset transition. Due to the ambiguity, different implemenations have different behaviors and some implementations have changed their behavior over the time. UTC(t) and Localtime(t) are reformulated with LocalTZA(t = t_local, false) and LocalTZA(t = t_UTC, true). Will fix #725. In the future, it might as well be considered to add an option to specify the behavior to handle skipped or repeated wall time. See [1] and [2]. [1] ICU Calendar API has skipped wall time option (https://goo.gl/h0bP26) and repeated wall time option (https://goo.gl/Q1VX3j). [2] Python proposal to handle skipped/repeated time: https://www.python.org/dev/peps/pep-0495/ * Addressed the review comments 1. Interpret skipped and repeating local time in the time zone offset before the transition. 2. s/wall time/local time/ 3. Daylight savings time => Daylight saving time.
Just an update on one of @jungshik's comments,
These links are now broken and the relevant options seem to have turned into UCalendarWallTimeOption |
As noted at the end of [1], UTC(Localtime(t)) is not always equal to t.
Indeed, Localtime(UTC(t)) is not always equal to t, either, although neither [0] nor [1] notes this.
Nor does [1] address the question of what to do when the parameter passed to UTC is either invalid (because it represents a time that local time skipped over when starting DST, during a "spring forward") or ambiguous (because it represents a time that was reused at the end of DST - first in DST then in standard time - in a "fall back").
The spec [2] of DaylightSavingTA() neglects to say that the input parameter, t, is presumed to be a UTC time; this is, however, made clear by the way it is used in the stipulated implementations of UTC() and Localtime(). Those uses make clear that, for any t, LocalTZA + DaylightSavingTA(t) should give local time's applicable offset from UTC at time t.
The spec [2] should make clear that t is presumed to be given in UTC.
LocalTZA is specified to be local time's present (at run-time, not at time t) standard offset from UTC, without any DST correction. If local time has ever had a change of standard offset, the tacit requirement that LocalTZA + DaylightSavingTA(t) was local time's applicable offset from UTC at time t then comes into conflict with the wording of the specification for DaylightSavingTA(); when t refers to a time the other side (from run-time) of a standard offset change in local time, DaylightSavingTA(t) needs to incorporate both the standard offset change and any DST that may have been in effect at time t, while the wording of the spec says it should only return the DST offset applicable at that time.
In the case of Pacific/Kiritimati looking at times before 1995, or Pacific/Apia before 2012, this implies a DaylightSavingTA() of 24 hours at times when DST was not in effect. For a zone that's now given up DST in favour of setting their standard offset to what used to be their summer offset, this shall mean reporting a negative DaylightSavingTA() before the change for non-DST times, while reporting a zero offset for when DST was in force.
The cause of this is the oversimplification of using LocalTZA as base offset and treating all deviations from it as DST. In practice, a good time offset API would have an OffsetFromUTC(t) method, taking a UTC time and returning the actual offset at that time; for legacy purposes, you can define LocalTZA as at present and DaylightSavingTA(t) as OffsetFromUTC(t) - LocalTZA. There may be some use to also providing StandardOffsetFromUTC(t) and DaylightSavingAdjustment(t) with which to decompose OffsetFromUTC(t), but it's OffsetFromUTC(t) that most code is going to actually care about.
You can then define Localtime(t) to be t + OffsetFromUTC(t). I encourage you to, at the same time, change the spec of UTC(); doing that right shall be contentious, but I do recommend looking at python's definitions [3] in this area.
Some mechanism for disambiguating fall-back hours is necessary; while client code shall often now know the needed hint, requiring sensible default behaviour (and matching present behaviour shall make sense for that), it is worth providing some mechanism for callers to
Whatever mechanism disambiguates fall-backs can also be used to decide what to do with invalid local time values in a spring forward. (My usual model for these is that someone stepped forward or backwards an hour, a day or a week from a valid time and landed in a gap. If the prior time was in DST, that step (backwards) should have landed in the hour before the spring forward; if the prior time was in standard time, the step (forward) should land in the hour after. So I select the time that actually contradicts the alleged DSTness, where handling of a fall-back seeks to match it.) Actual changes in a zone's standard offset can be handled the same way as a DST transition in the same direction.
Ideally it should be the case that UTC(t) returns a u for which u + OffsetFromUTC(u) is indeed t. Note, however, that OffsetFromUTC(u) need not be equal to OffsetFromUTC(t), so t - OffsetFromUTC(t) is not an adequately reliable answer, although it may be the best you can do, unless you arrange for your local-time values to come with an extra bit (or trit, to allow "unknown" as an option) of data, indicating their (presumed) DST-ness or some equivalent truth (such as python's "fold").
Time software would all be so much simpler if we all just stuck to UTC; I recommend doing so for all internal types and only meddling with the political mess of time zone variation at the very surface of software, where users are exposed to the data. Local time may seem like a convenience, but avoiding bugs at transitions annihilates what little convenience it actually confers.
I may be reached as edward.welbourne@qt.io (and shall now fix Qt's V4 engine to be less broken than it presently is - only as broken as the spec requires it to be).
The text was updated successfully, but these errors were encountered: