Leaping Leap Seconds, Batman... 😔

When I create a Date value from this string "2005-12-31T23:59:59Z" and then check its timeIntervalSinceReferenceDate value I find 157766399. The same process for "2006-01-01T00:00:00Z" yields 157766400. If I create a Date value directly from the number 157766400 and then check it's description I find "2006-01-01T00:00:00Z".

This would all be fine, except that as far as I've understood from what I've read there was a leap second inserted at the end of 2005, which would mean that exactly 157766400 seconds after the reference date (the beginning of 2001) the officially accepted UTC timestamp was either "2005-12-31T23:59:60Z" or "2005-12-31T23:59:59Z" repeated, but definitely not "2006-01-01T00:00:00Z". It was not until 157766401 seconds after the reference date that the worldwide UTC timestamp was "2006-01-01T00:00:00Z".

Does this mean that Date.timeIntervalSinceReferenceDate does not return the actual number of physically elapsed seconds since the reference date? Is the Date type incapable of referring to a leap second? "2005-12-31T23:59:60Z", for example?

3 Likes

After some digging, I found this documentation which indicates that the Foundation date-related types and methods do not account for leap seconds. Not sure how the official story has changed since that was updated in 2013, but there's only been two additional leap seconds since then so I doubt it's changed much.

4 Likes

Thanks! Nice to at least see "leap seconds" mentioned somewhere in the official documentation.

An important follow-up to call out:

Since Cocoa implements time according to the NTP standard, these methods ignore leap seconds in the calculation.

The implication here isn't clear, but Apple platforms do adjust for leap seconds using NTP: when leap seconds occur, the system clock will correctly update to leap forward or backward, and the effective meaning of existing timestamps shifts forward or backward by 1 second. Just to be clear that leap seconds aren't ignored on these systems; they are just not differentiated by the APIs when searching backward in time, nor predicted when searching forward in time.

8 Likes

I think it's worth mentioning that some other systems, instead of leaping, may leap smear (or slew) and spread out that adjustment over a longer period (hrs - days).

I don't know about that one in particular, but I believe the seconds in POSIX time are never the SI seconds. It is implementation-defined even:

The relationship between the actual time of day and the current value for seconds since the Epoch is unspecified.

How any changes to the value of seconds since the Epoch are made to align to a desired relationship with the current actual time is implementation-defined. As represented in seconds since the Epoch, each and every day shall be accounted for by exactly 86400 seconds.

If Date uses POSIX time in under the hood, it might have similar "inaccuracy".

4 Likes

That's a feature of particular NTP servers (rather than clients), right? I.e., if you pointed iOS/macOS to a Google NTP server (which implements smearing), then you'd get the smear behavior?

For now then I'm conceptualizing it as such:

Date.timeIntervalSinceReferenceDate returns the number of Apple-defined POSIX seconds since the reference date, where the duration of an Apple-defined POSIX second is almost always exactly one SI second, with the exception of the handful of leap POSIX seconds which have a duration of exactly two SI seconds. This means, for example, that POSIX second 157766399.5 occurred one SI second after 157766399. Good thing that all I needed was a clear and comprehensible grid of time. If I were creating some predictive physics engine that depends on knowing exactly how many SI seconds elapsed between two dates then I think that would require manually maintaining the log of leap seconds as they are issued.

Kind of, it's still technically possible that the clients would leap smear themselves when a leap second is expected. Though it's more common to add the 23:59:60, double the duration of 23:59:59, or even ignore it entirely and re-sync the clock after the fact.

Hmm, from what I read above, it's probably more of a replay, where 157766399-157766400 is played twice. In which case, 157766399.5 is ambiguously either 0.5 or 1.5 SI seconds after 157766399. It probably won't matter much if you're using a second granularity (157766399 - 157766400 spans two SI seconds regardless).

Ain't clock synchronization fun? :smirk:

1 Like

I remember some twenty years ago, when I moved to Trondheim, Norway. City regulations forbid serving of alcohol after 02:30 at night. However, during the yearly daylight saving adjustments, the hour 02-03 is played twice. So, serving stopped from 02:30:00-02:59:59, and re-opened again when the interval 02:00:00 to 02:29:59 replayed.

I was stunned. But can now clearly visualise what @Lantua is talking about.

1 Like
Terms of Service

Privacy Policy

Cookie Policy