Cocoa-Touch: How do I see if two NSDates are in the same day?

I need to know if two NSDate instances are both from the same day.

Is there an easier/better way to do it than getting the NSDateComponents and comparing day/month/year?

13.10.2009 16:59:12
you might want to revaluate the accepted answer for this. Things have changed.
philipp 7.07.2016 15:37:48
12 ОТВЕТОВ
РЕШЕНИЕ

NSDateComponents sounds like the best bet to me. Another tactic to try is toll-free-bridging it to a CFDate, then using CFDateGetAbsoluteTime and doing a subtraction to get the amount of time between the two dates. You'll have to do some additional math to figure out if the time difference lands the dates on the same day, however.

7
13.10.2009 17:04:33
ya, but then you'd have to take into account so many things with regards to processing the time that you end up writing more code than just using NSDateComponents.
pxl 13.10.2009 18:24:30
Right; I was suggesting an alternative to NSDateComponents, but still believe it is the best bet. If this becomes a performance-critical section of code the alternative may be slightly faster, but I'd avoid that optimization until proven necessary.
fbrereto 13.10.2009 18:57:35
NSDate has this nice method: timeIntervalSinceDate: Therefore you don't even need corefoundation.
Georg Schölly 14.10.2009 21:14:25
NSDateComponents is the best bet because then things like time zones and daylight savings are handled automatically.
JeremyP 5.02.2014 20:31:56
Time interval alone is not enough to determine if two dates are the same day. 11:59 PM and 12:01 AM is only a 2 minute interval, but different days. You could use time interval to quickly rule out intervals > 24 hours, then use NSDateComponents for the complete check.
Drew C 30.07.2014 16:10:30

(Note: Look at Joe's answer for a good way to do this on iOS 8+)

I just use a date formatter:

NSDateFormatter *dateComparisonFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateComparisonFormatter setDateFormat:@"yyyy-MM-dd"];

if( [[dateComparisonFormatter stringFromDate:firstDate] isEqualToString:[dateComparisonFormatter stringFromDate:secondDate]] ) {
    
}

HTH.

24
15.10.2015 20:42:20
that's even worse than using NSDateComponents, since the formatter has to first break the date into components anyway, and then there's creating two strings, and then comparing strings.
Prody 13.10.2009 17:46:02
What's your metric for "better"? I think this is "easier" than the comparable NSDateComponents code in that it is more concise and is easily maintainable.
Ben Lachman 13.10.2009 19:02:12
I've been using the dateComponents method, but this is much smarter. Shorter code is better, and if performance is a problem I can cache the NSDateFormatter.
Steven Fisher 13.10.2009 21:34:47
Worked for me, nice and clever. Saved me from all the NSCalendar overhead.
Lukasz 21.08.2011 08:08:59

I use NSDateComponents to strip out the time aspect and then compare. Something like:

if ([[self beginningOfDay:date1] isEqualToDate:[self beginningOfDay:date2]]) 
{
...
}

- (NSDate *)beginningOfDay:(NSDate *)date {
    NSCalendar *calendar = [NSCalendar currentCalendar];

    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit;
    NSDateComponents *comp = [calendar components:unitFlags fromDate:date];

    return [calendar dateFromComponents:comp];
}
5
13.10.2009 21:30:59
That uses two conversions for each date (components:fromDate and dateFromComponents). I use only the components:fromDate, extract the month/day/year from each and compare those 3 ints to see if they match.
progrmr 14.10.2009 01:45:00
You should not use the beginningOfDay, not all days starts at 12am. You should use noon (12pm) for a don't care time date comparison
Leo Dabus 14.01.2016 15:16:28

If you are targeting iOS 8 (and OS X 10.9) or later, then Joe's answer is a better solution using a new method in NSCalendar just for this purpose:

-[NSCalendar isDate:inSameDayAsDate:]

For iOS 7 or earlier: NSDateComponents is my preference. How about something like this:

- (BOOL)isSameDayWithDate1:(NSDate*)date1 date2:(NSDate*)date2 {
    NSCalendar* calendar = [NSCalendar currentCalendar];

    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit;
    NSDateComponents* comp1 = [calendar components:unitFlags fromDate:date1];
    NSDateComponents* comp2 = [calendar components:unitFlags fromDate:date2];

    return [comp1 day]   == [comp2 day] &&
           [comp1 month] == [comp2 month] &&
           [comp1 year]  == [comp2 year];
}
154
23.05.2017 12:34:37
signature should be: -(BOOL)isSameDayWithDate1:(NSDate *)date1 date2:(NSDate *)date2 {
John 19.12.2011 00:39:33
- (BOOL)date:(NSDate *)date1 isTheSameDayAsDate:(NSDate *)date2 reads much better :)
Javier Soto 6.11.2012 01:01:02
or yet another way: -(BOOL)isDate:(NSDate*)date1 sameDayAsDate:(NSDate*)date2
progrmr 7.11.2012 22:34:54
return [comp1 isEqual:comp2]; seems to give same result. Is there a difference? Why choosing to manualy compare day, month and year?
Alex 7.03.2013 08:42:06
@Alex: The isEqual function is not documented in the NSDateComponents class reference so its not certain that it would do exactly the right thing under all circumstances. Does it ignore components that are invalid? Probably, but it's not documented. This code is only slightly longer and gives the correct result, even if the units had contained other fields (like hour or minute) when calling components:fromDate:
progrmr 11.03.2013 16:08:14

I like progrmr's solution, but I would go even further and make a category of NSDate that provides this method. It will make your code slightly more readable, and you won't need to copy and paste the method into each new class that might need it – just import the header file.

NSDate+SameDay.h

@interface NSDate (SameDay)
- (BOOL)isSameDayAsDate:(NSDate*)otherDate;
@end

NSDate+SameDay.m

#import "NSDate+SameDay.h"

@implementation NSDate (SameDay)

- (BOOL)isSameDayAsDate:(NSDate*)otherDate {

    // From progrmr's answer...
    NSCalendar* calendar = [NSCalendar currentCalendar];

    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit;
    NSDateComponents* comp1 = [calendar components:unitFlags fromDate:self];
    NSDateComponents* comp2 = [calendar components:unitFlags fromDate:otherDate];

    return [comp1 day]   == [comp2 day] &&
           [comp1 month] == [comp2 month] &&
           [comp1 year]  == [comp2 year];
}

@end

Then, after importing NSDate+SameDay.h in your class, you can use it like so:

if ([myFirstDate isSameDayAsDate:mySecondDate]) {
    // The two dates are on the same day...
}
11
5.02.2014 20:22:58
I also think this is the better approach.
Matthew Quiros 16.02.2014 16:25:36

I create my own utility classes.

ZYUtility.h

@interface ZYUtility : NSObject
+ (NSDate *)yesterday;
+ (NSDate *)tomorrow;
+ (NSDate *)endOfDay:(NSDate *)date;
+ (NSDate *)beginningOfDay:(NSDate *)date;
@end

ZYUtility.m

+ (NSDate *)yesterday;
{
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *componets = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
                                              fromDate:[NSDate date]];
    componets.day -= 1;
    componets.hour = 24;
    componets.minute = 0;
    componets.second = 0;

    return [calendar dateFromComponents:componets];
}

+ (NSDate *)tomorrow;
{
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *componets = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
                                              fromDate:[NSDate date]];
    componets.day += 1;
    componets.hour = 0;
    componets.minute = 0;
    componets.second = 0;

    return [calendar dateFromComponents:componets];
}

+ (NSDate *)beginningOfDay:(NSDate *)date {
    NSCalendar *calendar = [NSCalendar currentCalendar];

    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit;
    NSDateComponents *comp = [calendar components:unitFlags fromDate:date];

    return [calendar dateFromComponents:comp];
}

+ (NSDate *)endOfDay:(NSDate *)date {
    NSCalendar *calendar = [NSCalendar currentCalendar];

    NSDateComponents *components = [NSDateComponents new];
    components.day = 1;

    NSDate *theDate = [calendar dateByAddingComponents:components
                                                toDate:[ZYUtility beginningOfDay:date]
                                               options:0];

    theDate = [theDate dateByAddingTimeInterval:-1];

    return theDate;
}
-1
17.05.2014 03:05:13

The following will test whether two dates represent the same day in a given era (which is sufficient in many cases):

- (BOOL)isDate:(NSDate *)date1 sameDayAsDate:(NSDate *)date2 {
    NSCalendar *calendar = [NSCalendar currentCalendar];
    int diff = [calendar ordinalityOfUnit:NSDayCalendarUnit 
                                   inUnit:NSEraCalendarUnit 
                                  forDate:date1] -
               [calendar ordinalityOfUnit:NSDayCalendarUnit 
                                   inUnit:NSEraCalendarUnit 
                                  forDate:date2];
    return 0 == diff;
}
-1
21.08.2014 23:03:23

If you are working with iOS 8 you can use -isDate:inSameDayAsDate:.

From NSCalendar.h:

/*
    This API compares the Days of the given dates, reporting them equal if they are in the same Day.
*/
- (BOOL)isDate:(NSDate *)date1 inSameDayAsDate:(NSDate *)date2 NS_AVAILABLE(10_9, 8_0);

Note that for some reason this didn't make it to the official documentation on Apple's developer site. But it is definitely part of the public API.

53
31.01.2015 02:40:18

Is there an easier/better way to do it than getting the NSDateComponents and comparing day/month/year?

Yes, there is an easier way

-[NSCalendar rangeForUnit:startDate:interval:forDate:] This methods makes it easy to set a date to a beginning of a unit (day, hour, month, year,…) and get the length (interval) of that unit.

As a category it might be used like

@interface NSDate (Comparison)
-(BOOL) isSameDay:(NSDate *)rhs;
@end

@implementation NSDate (Comparison)

-(BOOL)isSameDay:(NSDate *)rhs
{
    NSDate *lhs = self;
    NSDate *lhsStart;
    NSDate *rhsStart;

    NSCalendar *cal = [NSCalendar currentCalendar];
    [cal rangeOfUnit:NSCalendarUnitDay startDate:&lhsStart interval:NULL forDate:lhs];
    [cal rangeOfUnit:NSCalendarUnitDay startDate:&rhsStart interval:NULL forDate:rhs];
    return [lhsStart compare:rhsStart] == NSOrderedSame;
}

@end

This category should work for all iOS versions and Mac OS X 10.5+.

for iOS 8+ and Mac OS X 10.9+, you can use NSCalendars

- (BOOL)isDate:(NSDate *)date1 equalToDate:(NSDate *)date2 toUnitGranularity:(NSCalendarUnit)unit;
1
8.03.2015 00:19:30

In Swift:

NSCalendar.currentCalendar().isDate(yourDate, equalToDate: yourOtherDate, toUnitGranularity: .Day)

It will return true if they are on the same day.

13
27.01.2016 20:27:47
Avaliable from iOS 8.0
ZYiOS 7.03.2016 13:29:21
NSCalendar.currentCalendar().isDateInToday(yourNSDate)

Available in iOS 8.0 and later and OS X v10.9 and later.

4
19.06.2016 03:20:38

in Swift 3.0, iOS8+

if NSCalendar.current.isDate(Date(), equalTo: date, toGranularity: .day) {

}

or

if NSCalendar.current.isDateInToday(date) {

}
1
17.03.2017 20:05:13
There is also Calendar.current.isDate(_ date1: Date, inSameDayAs date2: Date)
shim 2.05.2017 18:02:01