Добавьте дни к дате, и вычисление разницы не приведет к ожидаемому результату. - PullRequest
5 голосов
/ 31 октября 2019

Я не уверен, что я делаю здесь не так. Если вы запустите этот код на Xcode Playground (я использую 11.1), вы увидите, что он не работает для случайных дат, которые в моей системе оказываются где-то после 6500 дней, добавленных к текущей дате. Однако эти даты не являются фиксированными.

(1...10000).forEach { days in
    let calendar = Calendar(identifier: .gregorian)
    var dayComponent = DateComponents()
    dayComponent.day = days
    let date = Date()
    let someFutureDate = calendar.date(byAdding: dayComponent, to: date)!

    let difference = calendar.dateComponents([.day], from: date, to: someFutureDate)

    if (days != difference.day!) {
        print("WAT \(date) - \(someFutureDate) \(days) \(difference.day!)")
    }
}

Некоторые выходные данные (в моих тестах произошел сбой от 130 до 160 раз, выходные данные различаются):

WAT 2019-10-31 14:01:47 +0000 - 2038-01-21 14:01:47 +0000 6657 6656
WAT 2019-10-31 14:01:47 +0000 - 2038-01-26 14:01:47 +0000 6662 6661
WAT 2019-10-31 14:01:47 +0000 - 2038-02-02 14:01:47 +0000 6669 6668
WAT 2019-10-31 14:01:47 +0000 - 2038-02-05 14:01:47 +0000 6672 6671
WAT 2019-10-31 14:01:47 +0000 - 2038-02-12 14:01:47 +0000 6679 6678
WAT 2019-10-31 14:01:47 +0000 - 2038-02-14 14:01:47 +0000 6681 6680
[snip]

1 Ответ

4 голосов
/ 31 октября 2019

Это связано с ошибками округления (обратите внимание, что Date внутренне представляется как число с плавающей запятой, содержащее количество секунд с 1 января 2001 года). Если вы получите больше компонентов разности

let difference = calendar.dateComponents([.day, .hour, .minute, .second, .nanosecond],
                                         from: date, to: someFutureDate)

, то вы заметите, что в первом примере это что-то вроде

day: 6656 hour: 23 minute: 59 second: 59 nanosecond: 999999755 

, что почти ожидается 6657 дней. Похоже, что возможно исправить эту проблему, удалив «дробную часть» даты с

let date = calendar.date(bySetting: .nanosecond, value: 0, of: Date())!

или

let date = Date(timeIntervalSinceReferenceDate:
                Date().timeIntervalSinceReferenceDate.rounded())

С этим изменением я больше не смогу наблюдать разницу.

...