I would like to get the number of months in a year for non-gregorian calendars. For example, in the Hebrew calendar, year 5784 has 13 months, but year 5783 has 12 months.
I tried:
public extension Calendar {
/// The range of months for a given year in the calendar
func monthRange(year: Int) -> Range<Int> {
let components = DateComponents(calendar: self,
year: year,
month: 1, // I assume 1 is always valid
day: 1) // I assume 1 is always valid
let date = self.date(from: components)!
let monthRange = self.range(of: .month, in: .year, for: date)!
return monthRange
}
}
with no luck (result is always 1..<14).
Note that:
public extension Calendar {
func dayRange(year: Int, month: Int) -> Range<Int> {
let components = DateComponents(calendar: self,
year: year,
month: month,
day: 1) // I assume 1 is always valid
let date = self.date(from: components)!
let dayRange = self.range(of: .day, in: .month, for: date)!
return dayRange
}
}
Is working fine (at least for Gregorian calendar, it gives indeed 29 days for leap-year February, 28 otherwise, and 30/31 days for other months). It also works correctly for Chinese calendar (30 or 31 days).
This is because of the meaning of the month
component in the Hebrew calendar. A month
value of 13 means Elul, and 1 means Tishri. The months Elul and Tishri are in every year, so the range is always 1..<14
.
The difference between leap and non-leap years is whether the month represented by the value 6 exists or not. 6 means Adar I, and 7 is Adar or Adar II depending on whether it is a leap year.
If you just want to find out whether it is a year with a leap month, just check the number of days in a year.
let components = DateComponents(calendar: self, year: year)
guard let date = components.date,
let rangeOfDays = range(of: .day, in: .year, for: date) else {
// something went wrong...
}
if rangeOfDays.upperBound > 380 {
// leap year!
}