iosswiftdatensdateformatternsdatecomponents

Swift date difference in nanoseconds is not working


I am implementing a timer that requires calculating the milliseconds of the user being inactive and calculate the difference and recover the timer. Since there is no milliseconds option in dateComponents, I used nanoseconds instead, however, when I try to calculate the nanoseconds interval between two dates, I get the same results every time (current date is changing, should get different result), if I change the nanosecond to second, it works. I executed it twice to experiment.

let d1 = Date()
let df = DateFormatter()
df.dateFormat = "y-MM-dd H:m:ss.SSSS"
let d2 = df.date(from: "2021-05-03 9:30:00.1234")!

print(df.string(from: d1)) 
print(df.string(from: d2)) 
print(Calendar.current.dateComponents([.second], from: d1, to: d2).second!) 
// result1: "85573"
// result2: "85067"

When I use nanosecond

let d1 = Date()
let df = DateFormatter()
df.dateFormat = "y-MM-dd H:m:ss.SSSS"
let d2 = df.date(from: "2021-05-03 9:30:00.1234")!

print(df.string(from: d1))
print(df.string(from: d2))
print(Calendar.current.dateComponents([.nanosecond], from: d1, to: d2).nanosecond!)
// result1: "2147483647"
// result2: "2147483647"

Solution

  • Although I cannot find it documented: It seems that the possible values of all date components are limited to those of a signed 32-bit integer, and values outside of that range are truncated to Int32.max = 2147483647 or Int32.min = -2147483648:

    let d1 = Date()
    let d2 = d1.addingTimeInterval(2)
    print(Calendar.current.dateComponents([.nanosecond], from: d1, to: d2).nanosecond!)
    // 2000000000
    let d3 = d1.addingTimeInterval(3)
    print(Calendar.current.dateComponents([.nanosecond], from: d1, to: d3).nanosecond!)
    // 2147483647
    let d4 = d1.addingTimeInterval(-3)
    print(Calendar.current.dateComponents([.nanosecond], from: d1, to: d4).nanosecond!)
    // -2147483648
    

    If you request both seconds and nanoseconds, then the nanosecond component holds only the fractional seconds, which makes your code produce correct results again:

    print(Calendar.current.dateComponents([.second, .nanosecond], from: d1, to: d2))
    // Example output:
    // second: 111403 nanosecond: 464950927 isLeapMonth: false
    

    If you just need the difference between the two points in time in milliseconds, then

    let elapsed = Int(d2.timeIntervalSince(d1) * 1000)
    

    is a simpler option.