Calendar.nextDate () acts very weird when using .backward for directions. Only for the last day of the month

In my application, I need to get the previous 4 hours, not 4 hours of the current date.
For example:

  • if he is March 05, 10:00 am , then I should expect to return: March 05, 4:00 am
  • if he is March 05, 02:00 am , then I should expect to return: March 04, 4:00 am

To achieve this, I use Calendar.nextDate . It works well for the everyday month, except for the last day of each month.

This is an example of a happy journey 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 a 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 this a bug in Apple code, should I report it? or am i using it wrong?
If this is Apple's error, is there another way to achieve what I need at the same time?

Update:

Looking at some Swift source code, I noticed that the problem was not with the Calendar.nextDate() function. nextDate() uses Calendar.enumerateDates() and one where the real problem exists. However, I could not find the source code for this function. This is a 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 Calendar.enumerateDates gives an incorrect result when launched in February

+7
ios swift calendar swift4 macos
source share
1 answer

This seems to be a bug Apple should report. Make sure you enable the running test application that demonstrates the problem. Note that this error only appears on iOS. On macOS (at least the macOS playground), the code works as expected.

At the same time, there is a fairly simple workaround.

Modify your code to use .forward instead of .backward . This will result in the next date instead of the previous date. There seems to be no error in this direction. Then use:

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

to get the desired result, returning one day.

+3
source share

All Articles