In my app I need to get the previous 4am, not the 4am of the current date.
For example:
- if it's
March 05, 10:00 am
then I should expect to get back:March 05, 4:00 am
- if it's
March 05, 02:00 am
then I should expect to get back:March 04, 4:00 am
To achieve that I'm using Calendar.nextDate
. It works well for everyday of the month except the last day of each month.
This is an example of the happy path, where it works as expected:
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)
Output:
date = "Mar 5, 2018 at 1:25 PM"
result = "Mar 5, 2018 at 4:00 AM"
This is an example of the dark sad path, where it does NOT act as expected:
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)
Output:
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"
Any idea why this is happening? is it a bug in Apple's code, should I report it? or am I using this wrong?
If it's an Apple bug, is there another way of achieving what I need in the meantime?
Update:
After looking at some Swift source code, I noticed that the issue is not with the Calendar.nextDate()
function specifically. nextDate()
uses Calendar.enumerateDates()
and that's where the real issue is. I couldn't find the source code for that function though.
This is the same bug using 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
}
Also, this is a similar issue: Swift's Calendar.enumerateDates gives incorrect result when starting on February