views:

709

answers:

4

How can i calculate the number of days in a year for any calendar, not just gregorian. I have tried this

NSUInteger *days = [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit inUnit:NSYearCalendarUnit forDate:[NSDate date]];

but that gives me the number of days in the current month instead of the number of days in the current year.

+1  A: 

I don't know objective-c but it is a simple algorithm to determine if it is a leap year.

if ( year % 400 == 0 )
   then 366 // Leap Year
else if ( year % 100 == 0 )
   then 365 // Non-Leap Year
else if ( year % 4 == 0 )
   then 366 // Leap Year
else
   365 // Non-Leap Year

Or if you want a little less verbose version

if ((year % 4 is 0) && (year % 100 != 0) || (year % 400 == 0))
   then 366 // Leap Year
else
   365 // Non-Leap Year
William Clemens
Please be mindful that this algorithm will break for years before 1582, before leap years where introduced ;-)
notnoop
This is true. You would have to add a check to make sure the year is after 1582. If you need to take that into account.
William Clemens
This is not realy what I was asking for, because it will only work for the gregorian calendar.
Godisemo
Also this should be noted that it only works on the Gregorian calendar . So you may find it helpful but I don't think it 100% solves your problem.
William Clemens
+1  A: 

Maybe you can use the components:fromDate:toDate:options: selector which is meant to (and I quote official Apple docs): Returns, as an NSDateComponents object using specified components, the difference between two supplied dates. ?

Also read this post that clarifies the behaviour you are seeing.

Bloodsplatter
This could do, but how should I describe the date "last day of the year"
Godisemo
http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/DatesAndTimes/Articles/dtCalendars.html Check out the example at the bottom of that page, if you work with the NSDateComponents that might work for all calendars.
Bloodsplatter
I thought about this and I don't think it will work because it will take me back to square one and I'll have the same problem again.
Godisemo
I also updated my original answer with another link, that explains the odd behavior you are getting ;) It is something that only came into existence since Leopard.
Bloodsplatter
This helped me to understand why my original approach didn't work. Thanks.
Godisemo
Seems like Jason Coco's answer uses exactly this approach
Godisemo
+2  A: 

I finally came up with a solution that works. What I do is first calculate the number of months in the year and then for each month calculate the number of days for that month.

The code looks like this:

NSUInteger days = 0;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *today = [NSDate date];
NSDateComponents *components = [calendar components:NSYearCalendarUnit fromDate:today];
NSUInteger months = [calendar rangeOfUnit:NSMonthCalendarUnit
                                   inUnit:NSYearCalendarUnit
                                  forDate:today].length;
for (int i = 1; i <= months; i++) {
    components.month = i;
    NSDate *month = [calendar dateFromComponents:components];
    days += [calendar rangeOfUnit:NSDayCalendarUnit
                           inUnit:NSMonthCalendarUnit
                          forDate:month].length;
}

return days;

It is not as neat as I would have hoped for but it will work for any calendar such as the ordinary gregorian one or the islamic one.

Godisemo
+1  A: 

In order to do what you want you have to find the range of days in a month and months in a year for a specific calendar. This function will do what you need for any calendar that uses day/month/year units (or has some equivalent mapped):

NSInteger getDaysInYear(NSDate* date)
{
    // Get the current calendar
    NSCalendar* c = [NSCalendar currentCalendar];

    // Find the range for days and months in this calendar
    NSRange dayRange = [c rangeOfUnit:NSDayCalendarUnit inUnit:NSYearCalendarUnit forDate:date];
    NSRange monthRange = [c rangeOfUnit:NSMonthCalendarUnit inUnit:NSYearCalendarUnit forDate:date];

    // Get the year from the suppled date
    NSDateComponents* yearComps = [c components:NSYearCalendarUnit fromDate:date];
    NSInteger thisYear = [yearComps year];

    // Create the first day of the year in the current calendar
    NSUInteger firstDay = dayRange.location;
    NSUInteger firstMonth = monthRange.location;
    NSDateComponents* firstDayComps = [[[NSDateComponents alloc] init] autorelease];
    [firstDayComps setDay:firstDay];
    [firstDayComps setMonth:firstMonth];
    [firstDayComps setYear:thisYear];
    NSDate* firstDayDate = [c dateFromComponents:firstDayComps];

    // Create the last day of the year in the current calendar
    NSUInteger lastDay = dayRange.length;
    NSUInteger lastMonth = monthRange.length;
    NSDateComponents* lastDayComps = [[[NSDateComponents alloc] init] autorelease];
    [lastDayComps setDay:lastDay];
    [lastDayComps setMonth:lastMonth];
    [lastDayComps setYear:thisYear];
    NSDate* lastDayDate = [c dateFromComponents:lastDayComps];

    // Find the difference in days between the first and last days of the year
    NSDateComponents* diffComps = [c components:NSDayCalendarUnit 
                                       fromDate:firstDayDate
                                         toDate:lastDayDate
                                        options:0];

    // We have to add one since this was subtraction but we really want to
    // give the total days in the year
    return [diffComps day] + 1;
}

If you want to specify this year, you can call it simply as getDaysInYear([NSDate date]);, or you could create a date from specific year/other components and pass that. You could also very easily re-implement it as a method call.

Jason Coco