Search code examples
iosswiftdatecalendarfoundation

Calendar: get date components, weird case


I'm trying to find out why this piece of code works so strange. You can find details below:

let nowDate = Date()
let threeDayBeforeNowDate_t1 = Date(timeIntervalSinceNow: -60 * 60 * 24 * 3)
let oneDayAfterNowDate = Date(timeIntervalSinceNow: 60 * 60 * 24 * 1)
let threeDayBeforeNowDate_t2 = Date(timeIntervalSinceNow: -60 * 60 * 24 * 3)
let threeDayBeforeNowDate = nowDate.addingTimeInterval(-60 * 60 * 24 * 3)

let diff_1 = threeDayBeforeNowDate_t1.timeIntervalSince(nowDate) - threeDayBeforeNowDate.timeIntervalSince(nowDate) // about 0.009357
let diff_2 = threeDayBeforeNowDate_t2.timeIntervalSince(nowDate) - threeDayBeforeNowDate.timeIntervalSince(nowDate) // about 0.010063
let diff_3 = threeDayBeforeNowDate_t2.timeIntervalSince(nowDate) - threeDayBeforeNowDate_t1.timeIntervalSince(nowDate) // about 0.000416

let calendar = Calendar.current
calendar.dateComponents(Set([Calendar.Component.day]), from: threeDayBeforeNowDate, to: oneDayAfterNowDate) // result = "day: 4 isLeapMonth: false"
calendar.dateComponents(Set([Calendar.Component.day]), from: threeDayBeforeNowDate_t1, to: oneDayAfterNowDate) // day: 4 isLeapMonth: false
calendar.dateComponents(Set([Calendar.Component.day]), from: threeDayBeforeNowDate_t2, to: oneDayAfterNowDate) // day: 3 isLeapMonth: false 

I don't understand, why I get so different results while dates (threeDayBeforeNow...) differ by less than a second.


Solution

  • As Dávid already explained, the problem is that the various dates are computed at different points in time, therefore the difference is not exactly 4 days. In particular, the difference between threeDayBeforeNowDate_t2 and oneDayAfterNowDate is less than 4 days, and that's why the .day component of the difference is 3.

    Here is a simplified example demonstrating the problem (in a Playground):

    let nowDate = Date()
    let date1 = Date(timeIntervalSinceNow: -60 * 60 * 24 * 4)
    let date2 = nowDate.addingTimeInterval(-60 * 60 * 24 * 4)
    
    let calendar = Calendar.current
    calendar.dateComponents([.day, .hour, .minute, .second, .nanosecond], from: date1, to: nowDate)
    // day: 3 hour: 23 minute: 59 second: 59 nanosecond: 994143066 isLeapMonth: false
    
    calendar.dateComponents([.day, .hour, .minute, .second, .nanosecond], from: date2, to: nowDate)
    // day: 4 hour: 0 minute: 0 second: 0 nanosecond: 0 isLeapMonth: false
    

    date2 and nowDate differ by exactly 4 days, but date1 and nowDate differ by a tiny be less than 4 days (assuming that there is no daylight savings transition in this timespan).