Календарь: получить компоненты даты, странный случай

Я пытаюсь выяснить, почему этот кусок кода работает так странно. Вы можете найти детали ниже:

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 

Я не понимаю, почему я получаю такие разные результаты, в то время как даты (threeDayBeforeNow...) отличаются менее чем на секунду.

2 ответа

Решение

Как уже объяснил Давид, проблема в том, что различные даты рассчитываются в разные моменты времени, поэтому разница не составляет ровно 4 дня. В частности, разница междуthreeDayBeforeNowDate_t2 а также oneDayAfterNowDate менее 4 дней, и поэтому .day Компонент разницы 3,

Вот упрощенный пример, демонстрирующий проблему (на игровой площадке):

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 а также nowDate отличаются ровно на 4 дня, но date1 а также nowDate Разница будет немного меньше, чем 4 дня (при условии, что в этот промежуток времени переход на летнее время отсутствует).

Проблема в том, что между созданием nowDate и ваши другие переменные, где вы используете Date(timeIntervalSinceNow:)инициализатор.

И то и другое Date() а также Date(timeIntervalSinceNow:) используйте системное время, чтобы получить текущее время, когда выполнение достигает этой конкретной переменной. Независимо от того, насколько малым, всегда будет небольшая задержка между различными вызовами системного времени и, следовательно, даже если вы создадите два Date объекты, использующие Date() в двух последовательных строках кода они не будут представлять один и тот же момент времени.

Работая на игровой площадке, следующий фрагмент кода демонстрирует это поведение:

let now1 = Date()
let now2 = Date()
now1.timeIntervalSince(now2) //-0.0002049803733825684

Если вы хотите последовательный Date объекты, создать единственную переменную, в которой вы храните дату создания, используя Date() как вы делаете в данный момент для nowDateзатем используйте эту же переменную для создания другого Date объекты, использующие nowDate.addingTimeInterval() как вы в настоящее время делаете для threeDayBeforeNowDate,

Другие вопросы по тегам