当使用.backward作为方向时,Calendar.nextDate()表现得非常奇怪。仅适用于本月的最后一天

时间:2021-02-25 02:44:17

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

在我的应用程序中,我需要获取前一个凌晨4点,而不是当前日期的凌晨4点。例如:

  • if it's March 05, 10:00 am then I should expect to get back: March 05, 4:00 am
  • 如果它是3月5日上午10:00,那么我应该期待回来:3月5日凌晨4:00
  • if it's March 05, 02:00 am then I should expect to get back: March 04, 4:00 am
  • 如果它是3月05日凌晨02:00,那么我应该期待回来:3月4日凌晨4:00

To achieve that I'm using Calendar.nextDate. It works well for everyday of the month except the last day of each month.

为了实现这一点,我正在使用Calendar.nextDate。除了每个月的最后一天,它适用于本月的每日。

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?

知道为什么会这样吗?它是Apple的代码中的错误,我应该报告吗?或者我使用这个错误?如果它是Apple的bug,那么在此期间还有另一种方法可以实现我需要的东西吗?

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():

在查看了一些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
}

Also, this is a similar issue: Swift's Calendar.enumerateDates gives incorrect result when starting on February

此外,这是一个类似的问题:Swift的Calendar.enumerateDates在2月开始时给出不正确的结果

1 个解决方案

#1


3  

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.

这似乎是一个应该向Apple报告的错误。确保您包含一个可运行的测试应用程序来演示该问题。请注意,此错误仅出现在iOS中。在macOS(至少是一个macOS playground)中,代码按预期工作。

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:

将代码更改为使用.forward而不是.backward。这将给出下一个日期而不是上一个日期。似乎没有任何错误朝那个方向发展。然后使用:

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

to get your desired result by going back one day.

通过回去一天得到你想要的结果。

#1


3  

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.

这似乎是一个应该向Apple报告的错误。确保您包含一个可运行的测试应用程序来演示该问题。请注意,此错误仅出现在iOS中。在macOS(至少是一个macOS playground)中,代码按预期工作。

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:

将代码更改为使用.forward而不是.backward。这将给出下一个日期而不是上一个日期。似乎没有任何错误朝那个方向发展。然后使用:

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

to get your desired result by going back one day.

通过回去一天得到你想要的结果。