Reputation: 3238
I'm getting unexpected results when attempting to enumerate days for a range of months. For example, I want to determine the 29th day for every month in 2018. As February does not have a day 29, I expect 11 dates: Jan 29, Mar 29, April 29, etc... Instead, I only get Jan 29 returned.
Paste into a playground:
var calendar = Calendar(identifier: .gregorian)
calendar.locale = Locale(identifier: "en_US")
calendar.timeZone = TimeZone(identifier: "America/Chicago")!
let firstOfYear = Date(timeIntervalSince1970: 1514786400)
let endDate = calendar.date(byAdding: .year,
value: 1,
to: firstOfYear,
wrappingComponents: false)!
var components = DateComponents()
components.day = 29 // unexpected results
// components.day = 5 // correct results
var dates = [Date]()
calendar.enumerateDates(startingAfter: firstOfYear,
matching: components,
matchingPolicy: .strict,
using: { (nextDate: Date?, exactMatch: Bool, stop: inout Bool) in
if nextDate?.compare(endDate) == .orderedDescending {
stop = true
return
}
dates.append(nextDate!)
})
dates
Note that I have tried all 4 matchingPolicy
types with the same results. Anyone able to shed light on what is happening? It seems that the enumeration stops after it cannot find a date in a month. Is it best practice to create your own loop to determine dates?
Upvotes: 0
Views: 429
Reputation: 318824
From the documentation for Calendar enumerateDates
:
If an exact match is not possible, and requested with the strict option, nil is passed to the closure and the enumeration ends
This is why you only get the date for January 29. Using other matching modes doesn't work well when asking for a non-existent date such as February 29 on a non-leap year.
The following code gives you the results you want:
func datesFor(day: Int, year: Int) -> [Date] {
var res = [Date]()
var components = DateComponents(year: year, day: day)
for month in 1...12 {
components.month = month
if let date = Calendar.current.date(from: components) {
// Feb 29, 2018 results in Mar 1, 2018. This check skips such dates
if Calendar.current.date(date, matchesComponents: components) {
res.append(date)
}
}
}
return res
}
print(datesFor(day: 29, year: 2018))
Upvotes: 4