Question

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];
}];
Was it helpful?

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.

OTHER TIPS

Here's a relatively clean version. It's a category on NSCalendar.

- (NSInteger) daysInYear:(NSInteger) year {
    NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
    dateComponents.year = year;
    dateComponents.month = dateComponents.day = 1;
    NSDate *firstOfYear = [self dateFromComponents:dateComponents];
    dateComponents.year = year + 1;
    NSDate *firstOfFollowingYear = [self dateFromComponents:dateComponents];

    return [[self components:NSDayCalendarUnit
                   fromDate:firstOfYear
                     toDate:firstOfFollowingYear
                     options:0] day];    
}

Thanks to AKV for pointing out that -components:fromDate:toDate:options will work in this case.

This doesn't seem to work for 1582 or prior, which per Nikolai Ruhe's answer is because Gregorian calendar calculations simply aren't defined prior to then.

What about finding number of days in a given year as: Although this is not the relative solution for the question.

-(NSInteger)daysBetweenTwoDates:(NSDate *)fromDateTime andDate:(NSDate *)toDateTime{

    NSDate *fromDate;
    NSDate *toDate;

    NSCalendar *calendar = [NSCalendar currentCalendar];

    [calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate interval:NULL forDate:fromDateTime];
    [calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate interval:NULL forDate:toDateTime];

    NSDateComponents *difference = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];

    return [difference day]; 
}

-(void)someMethod {

    NSString *yourYear=@"2012";

    NSDate *date1 = [NSDate dateWithString:[NSString stringWithFormat:@"%@-01-01 00:00:00 +0000",yourYear]];
    NSDate *date2 = [NSDate dateWithString:[NSString stringWithFormat:@"%@-12-31 00:00:00 +0000",yourYear]];

    NSInteger numberOfDays=[self daysBetweenTwoDates:date1 andDate:date2];    

    NSLog(@"There are %ld days in between the two dates.", numberOfDays);
}            

Edit:

As dateWithString: is deprecated, you can use

NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter dateFormat]=@"dd-MM-yyyy";
NSDate *date=[dateFormatter dateFromString:dateString];

to form date1 and date2

You are getting the number of days in the month of your date.

You could use the same function and just pass in a date with February as the month. If range.length returns 28, the Total days in the year will be 365, if it returns 29, then the number of days will be 366.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top