I’ve got a little timer functionality in my app. When the timer starts I create a date object and keep it as a property.
I then have a timer set in the code that repeatedly ticks every second and compares the stored property against Date() using timeIntervalSince. However, it looks like all positive values are off by 1 until they reach zero. The code remains on zero for two ticks and then starts calculating the results correctly from there on.
Is there any explanation for this discrepancy? More importantly, is there a guarantee that this behaviour will remain consistent so that I can adjust my own logic to handle it correctly? Unfortunately, I couldn’t find anything that could explain the behaviour in documentation.
Here is a minimum reproducible example of what I’m trying to achieve
import Combine
import Foundation
var cancellables = Set<AnyCancellable>()
let endDate = Date().addingTimeInterval(5)
Timer.publish(every: 1, on: .main, in: .common)
.autoconnect()
.sink { _ in
let now = Date()
print("End date: \(endDate)")
print("Now: \(now)")
print("Distance: \(Int(endDate.timeIntervalSince(now)))")
}
.store(in: &cancellables)
And the example output:
End date: 2023-03-14 07:11:28 +0000
Now: 2023-03-14 07:11:23 +0000
Distance: 4
End date: 2023-03-14 07:11:28 +0000
Now: 2023-03-14 07:11:24 +0000
Distance: 3
End date: 2023-03-14 07:11:28 +0000
Now: 2023-03-14 07:11:25 +0000
Distance: 2
End date: 2023-03-14 07:11:28 +0000
Now: 2023-03-14 07:11:26 +0000
Distance: 1
End date: 2023-03-14 07:11:28 +0000
Now: 2023-03-14 07:11:27 +0000
Distance: 0
End date: 2023-03-14 07:11:28 +0000
Now: 2023-03-14 07:11:28 +0000
Distance: 0
End date: 2023-03-14 07:11:28 +0000
Now: 2023-03-14 07:11:29 +0000
Distance: -1
End date: 2023-03-14 07:11:28 +0000
Now: 2023-03-14 07:11:30 +0000
Distance: -2
End date: 2023-03-14 07:11:28 +0000
Now: 2023-03-14 07:11:31 +0000
Distance: -3
>Solution :
TimeInterval is a floating point type with all its inadequacies. And the conversion to Int rounds always down by stripping off the fractional part.
A more reliable way is to let Calendar do the date math
print("Distance: \(Calendar.current.dateComponents([.second], from: now, to: endDate))")