Календарь.nextDate() ведет себя очень странно при использовании.назад для направления. Только на последний день месяца.


В моем приложении мне нужно получить предыдущие 4 утра, а не 4 утра текущей даты.
Например:

  • если это March 05, 10:00 am , то я должен ожидать, что вернусь: March 05, 4:00 am
  • если это March 05, 02:00 am , то я должен ожидать, что вернусь: March 04, 4:00 am

Для достижения этого я использую Calendar.nextDate. Он хорошо работает для каждого дня месяца, за исключением последнего дня каждого месяца.

Это пример счастливого пути, где он работает, как и ожидалось:
let components = DateComponents(hour: 4, minute: 0, second: 0)

let date =  Calendar.current.date(from: DateComponents(year: 2018,
                                                       month: 03, 
                                                       day: 05, 
                                                       hour: 13, 
                                                       minute: 25, 
                                                       second: 0))!
let result = Calendar.current.nextDate(after: date, 
                                       matching: components,
                                       matchingPolicy: .nextTime,
                                       direction: .backward)

Вывод:

date = "Mar 5, 2018 at 1:25 PM"
result = "Mar 5, 2018 at 4:00 AM"

Это пример темного печального пути, где он не действует так, как ожидалось:

let components = DateComponents(hour: 4, minute: 0, second: 0)

let date =  Calendar.current.date(from: DateComponents(year: 2018,
                                                       month: 03, 
                                                       day: 31, 
                                                       hour: 13, 
                                                       minute: 25, 
                                                       second: 0))!
let result = Calendar.current.nextDate(after: date, 
                                       matching: components,
                                       matchingPolicy: .nextTime,
                                       direction: .backward)

Вывод:

date = "Mar 31, 2018 at 1:25 PM"
result = "Mar 30, 2018 at 4:00 AM"
// it should give me this instead: "Mar 31, 2018 at 4:00 AM"

Любые идеи, почему это происходит? это ошибка в коде Apple, должен ли я сообщить об этом? или я использую это неправильно?
Если это ошибка Apple, есть ли другой способ достичь того, что мне нужно в то же время?

Обновление:

Просмотрев исходный код Swift, я заметил, что проблема не в функции Calendar.nextDate() конкретно. nextDate() использует Calendar.enumerateDates() и вот где настоящая проблема. Однако я не смог найти исходный код для этой функции. Это та же ошибка с использованием Calendar.enumerateDates():

let components = DateComponents(hour: 4, minute: 0, second: 0)

let date =  Calendar.current.date(from: DateComponents(year: 2018,
                                                       month: 03, 
                                                       day: 31, 
                                                       hour: 13, 
                                                       minute: 25, 
                                                       second: 0))!

Calendar.current.enumerateDates(startingAfter: inputDate,
                                matching: components, 
                                matchingPolicy: .nextTime, 
                                direction: .backward) { (date, exactMatch, stop) in
    let result = date
    // result = "Mar 30, 2018 at 4:00 AM"
    stop = true
}

Кроме того, это похожий вопрос: календарь Свифта.enumerateDates дает неверный результат, начиная с февраля

1 7

1 ответ:

Похоже, что это ошибка, о которой следует сообщить Apple. Убедитесь, что вы включили запускаемое тестовое приложение, демонстрирующее проблему. Обратите внимание, что эта ошибка появляется только в iOS. В macOS (по крайней мере, на игровой площадке macOS) код работает, как и ожидалось.

В то же время существует довольно простой обходной путь.

Измените код, чтобы использовать .forward вместо .backward. Это даст следующую дату вместо предыдущей даты. Там, кажется, нет никаких ошибок, идущих в этом направлении. Затем используйте:

Calendar.current.date(byAdding: .day, value: -1, date: result)

Чтобы получить желаемый результат, вернитесь на один день назад.