Search code examples
objective-ccocoa-touchcocoansdatenscalendar

Objective-C: Getting the number of days in a calendar year


I am using the following NSCalendar method to get the number of days in a calendar year:

NSRange range = [gregorian rangeOfUnit:NSDayCalendarUnit 
                                inUnit:NSYearCalendarUnit 
                               forDate:date];

I am expecting a range.length return value of type NSRange which is 365 or 366 (days).

However, this code returns a range.length of 31 for a date value of 2005-06-06.

What is wrong with my code?

This is the full code snippet:

NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];

[subArray enumerateObjectsUsingBlock:
   ^(NSDate *date, NSUInteger idx, BOOL *stop) {
   NSUInteger numberOfDays = [gregorian ordinalityOfUnit:NSDayCalendarUnit
   inUnit:NSYearCalendarUnit forDate:date];
}];

Solution

  • This calculates the number of days of a year of a given date:

    NSDate *someDate = [NSDate date];
    
    NSDate *beginningOfYear;
    NSTimeInterval lengthOfYear;
    NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    [gregorian rangeOfUnit:NSYearCalendarUnit
                 startDate:&beginningOfYear
                  interval:&lengthOfYear
                   forDate:someDate];
    NSDate *nextYear = [beginningOfYear dateByAddingTimeInterval:lengthOfYear];
    NSInteger startDay = [gregorian ordinalityOfUnit:NSDayCalendarUnit
                                              inUnit:NSEraCalendarUnit
                                             forDate:beginningOfYear];
    NSInteger endDay = [gregorian ordinalityOfUnit:NSDayCalendarUnit
                                            inUnit:NSEraCalendarUnit
                                           forDate:nextYear];
    NSInteger daysInYear = endDay - startDay;
    

    Sad caveat: This does not work correctly for year 1582.

    The year 1582, the year when Gregor introduced the currently widespread used calendar, needed a fix to align solar with calendar years. So they went with the pragmatic solution: They just dropped October 5-14. (They were not crazy enough to change weekdays, too). As a result the year 1582 only has 355 days.

    Addendum: The code above only works correctly for years after 1582. It returns 365 days for the year 1500, for example, even though this year was a leap year in the then used julian calendar. The gregorian calendar starts at October 15, 1582. Computations made on the gregorian calendar are just not defined before that date. So in this way Apple's implementation is correct. I'm not aware of a correct implementation for years before 1583 on Cocoa.