views:

1270

answers:

5

What is the most efficient/recommended way of comparing two NSDates? I would like to be able to see if both dates are on the same day, irrespective of the time and have started writing some code that uses the timeIntervalSinceDate: method within the NSDate class and gets the integer of this value divided by the number of seconds in a day. This seems long winded and I feel like I am missing something obvious.

The code I am trying to fix is:

if (!([key compare:todaysDate] == NSOrderedDescending))
{
    todaysDateSection = [eventSectionsArray count] - 1;
}

where key and todaysDate are NSDate objects and todaysDate is creating using:

NSDate *todaysDate = [[NSDate alloc] init];

Regards

Dave

+4  A: 

You set the time in the date to 00:00:00 before doing the comparison:

unsigned int flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSCalendar* calendar = [NSCalendar currentCalendar];

NSDateComponents* components = [calendar components:flags fromDate:date];

NSDate* dateOnly = [calendar dateFromComponents:components];

// ... necessary cleanup

Then you can compare the date values. See the overview in reference documentation.

Gregory Pakosz
Thanks for the quick response Gregory, this is really helpful. I guess my next question is, as this is part of a tight loop, do you think it is more efficient to use the timeIntervalSinceDate: method and some integer arithmetic, rather than creating more objects, doing the necessary computation and then cleaning up?Thanks again for your help, Dave
Magic Bullet Dave
My advice is: correct implementation is gold; then profile. If this particular loop is really a bottleneck, then optimize
Gregory Pakosz
Since you're only pulling out the Year, Month, and Day units, the Hour, Minutes, and Seconds are automatically set to 0. Hence, you don't have to explicitly do it yourself.
Dave DeLong
@Dave > indeed, thank you I edited the answer
Gregory Pakosz
A: 

The documentation regarding NSDate indicates that the compare: and isEqual: methods will both perform a basic comparison and order the results, albeit they still factor in time.

Probably the simplest way to manage the task would be to create a new isToday method to the effect of the following:

    - (bool)isToday:(NSDate *)otherDate {
        currentTime = [however current time is retrieved]; // Pardon the bit of pseudo-code
        if (currentTime < [otherDate timeIntervalSinceNow]) {
         return YES;
        } else {
         return NO;
        }
    }
Kaji
A: 

What I was thinking was something like this:

if (([key timeIntervalSinceReferenceDate] - [todaysDate timeIntervalSinceReferenceDate]) < 86400)
{
    ....
}

Would be interested to know if this is efficient/recommended...

Magic Bullet Dave
Sounds kind of like my suggestion above. The only issue with this is then you're simply checking within the past 24 hours, and not based on calendar day. If that's acceptable, then by all means this should work fine.
Kaji
Doh, seen the error of my ways - only works if key's time is 00:00:00 and this is no different to timeIntervalSinceDate. Thanks for the pointers.
Magic Bullet Dave
No problem! Glad to be able to help.
Kaji
A: 

I use this little util method:

-(NSDate*)normalizedDateWithDate:(NSDate*)date
{
   NSDateComponents* components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
                                              fromDate: date];
   return [calendar_ dateFromComponents:components]; // NB calendar_ must be initialized
}

(You obviously need to have an ivar called calendar_ containing an NSCalendar.)

Using this, it is easy to check if a date is today like this:

[[self normalizeDate:aDate] isEqualToDate:[self normalizeDate:[NSDate date]]];

([NSDate date] returns current date and time.)

This is of course very similar to what Gregory suggests. The drawback of this approach is that it tends to create lots of temporary NSDate objects. If you're going to process a lot of dates, I would recommend using some other method, such as comparing the components directly, or working with NSDateComponents objects instead of NSDates.

Felixyz
+5  A: 

I'm surprised that no other answers have this option for getting the "beginning of day" date for the objects:

[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&date1 interval:NULL date:date1];
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&date2 interval:NULL date:date2];

Which sets date1 and date2 to the beginning of their respective days. If they are equal, they are on the same day.

Or this option:

int day1 = [[NSCalendar currentCalendar] ordinalityOfUnit:NSDayCalendarUnit inUnit:NSEraCalendarUnit forDate:date1];
int day2 = [[NSCalendar currentCalendar] ordinalityOfUnit:NSDayCalendarUnit inUnit:NSEraCalendarUnit forDate:date2];

Which sets day1 and day2 to somewhat arbitrary values that can be compared. If they are equal, they are on the same day.

Ed Marty
+1 I've never noticed those methods before! Very cool.
Dave DeLong