In my app I need to get the previous 4am, not the 4am of the current date.
For example:
March 05, 10:00 am
then I should expect to get back: March 05, 4:00 am
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.
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"
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?
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
This does appear to be a bug that should be reported to Apple. Be sure you include a runnable test app demonstrating the issue. Note that this bug only appears in iOS. In macOS (at least a macOS playground), the code works as expected.
In the meantime there is a fairly simple workaround.
Change your code to use .forward
instead of .backward
. This will give the next date instead of the previous date. There does not seem to be any bugs going in that direction. Then use:
Calendar.current.date(byAdding: .day, value: -1, date: result)
to get your desired result by going back one day.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With