DateFormatter vs leap seconds

is this a bug that NSDateFormatter knows about leap days but not about leap seconds?

    let f = DateFormatter()
    f.timeZone = TimeZone(identifier: "UTC")
    f.dateFormat = "yyyy/MM/dd HH:mm:ss"
    // last leap year
    let t1 = f.date(from: "2020/02/29 00:00:00") // 2020-02-29 00:00:00 UTC
    // last leap second
    let t2 = f.date(from: "2016/12/31 23:59:60") // nil

Where did you get the notion of leap second ?

"2016/12/31 23:59:60" is an invalid date format, hence the nil result.

Similarly, not leap year in 2021, hence

let t1 = f.date(from: "2021/02/29 00:00:00")

gives nil

But consider the following:

let f = DateFormatter()
f.timeZone = TimeZone(identifier: "UTC")
f.dateFormat = "yyyy/MM/dd HH:mm:ss"
// last leap year
let t1 = f.date(from: "2020/02/29 00:00:00") // 2020-02-29 00:00:00 UTC
// last leap second
let t2 = f.date(from: "2016/12/31 23:59:59")! // not nil
let t3 = t2.addingTimeInterval(TimeInterval(1.0))
let t3Str = f.string(from: t3)
print("Leaped 1 second: ", t3Str)

You correctly get:

  • Leaped 1 second:  2017/01/01 00:00:00

Leap seconds are a long-running saga on macOS (and all Unix OSes).

If you periodically sync your time with a server, then things are generally fine.
But if you need to-the-second accuracy, while a leap-second is added, and you are performing date maths, then maybe not so good!

If you have developed a workaround, please share it here!

Unix time / POSIX time and the time_t struct ignore leap seconds and as an extension, Date does so as well. As far as it is concerned, each day is exactly 86400 seconds long. This means that Date.timeIntervalSince1970 is actually a bit of a lie. These timestamps that you get are the number of seconds since 1970-01-01 minus the number of leap seconds that have occurred since then (which are 27 as of now).

Ignoring these leap seconds in the timestamps is useful because it allows to represent dates in the future without any problems (it is unknown when future leap seconds occur).
When they happen – as one did on 2016-12-31T23:59:60 – the system just repeats the previous timestamp for 23:59:59 and then continues to count up normally.

So to answer your question: no, it's not a bug that Date, DateFormatter, Calendar and all the other time&date components in Foundation ignore leap seconds, it's a deliberate design decision.

While I agree with you answer in general, this isn’t quite right:

As far as it is concerned, each day is exactly 86400 seconds long.

Date doesn’t have the concept of days; rather, Date is just a floating point offset from the reference date. To deal with days you need to involve a calendar, and in that context days aren’t always 86400 seconds long because of daylight saving time transitions.

If you’re interesting in leap seconds, there’s been a lot of discussion around it on Swift Forums recently, centred around SE-0329. Notably, that proposal specifically chose to not deal with it because it remains a tricky issue.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

DateFormatter vs leap seconds
 
 
Q