67

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?

progrmr
  • 71,681
  • 16
  • 107
  • 147
Prody
  • 5,042
  • 6
  • 41
  • 61

12 Answers12

154

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];
}
Community
  • 1
  • 1
progrmr
  • 71,681
  • 16
  • 107
  • 147
  • 5
    signature should be: -(BOOL)isSameDayWithDate1:(NSDate *)date1 date2:(NSDate *)date2 { – John Dec 19 '11 at 00:39
  • 3
    - (BOOL)date:(NSDate *)date1 isTheSameDayAsDate:(NSDate *)date2 reads much better :) – Javier Soto Nov 06 '12 at 01:01
  • 6
    or yet another way: `-(BOOL)isDate:(NSDate*)date1 sameDayAsDate:(NSDate*)date2` – progrmr Nov 07 '12 at 22:34
  • return [comp1 isEqual:comp2]; seems to give same result. Is there a difference? Why choosing to manualy compare day, month and year? – Alex Mar 07 '13 at 08:42
  • 1
    @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 Mar 11 '13 at 16:08
  • @progmr OK. Thanks for your answer. – Alex Mar 11 '13 at 17:02
  • damn, why this is not the accepted response? works perfectly, thanks – busta117 Nov 21 '13 at 14:37
  • @Heath I've updated to include the iOS 8 method also – progrmr Apr 23 '15 at 18:18
  • @progrmr could you update your answer to indicate that NSCalendar is available as of OS X 10.9 as well? Otherwise great answer. – Sam Ballantyne Apr 25 '15 at 10:17
  • @SamBallantyne added 10.9 – progrmr Apr 26 '15 at 00:48
  • speed calculate `if (([date1 timeIntervalSince1970] - [date2 timeIntervalSince1970]) > 86400) {return NO;}` – frank Jul 03 '15 at 11:58
  • Remember that NSDate is time zone naive. 2015-12-03 13:00 in New York is the same as 2015-12-04 2:00 in Singapore. – adamek Dec 03 '15 at 17:50
  • It's true that `NSDate` does not have time zone, `NSDate` values are stored in UTC. However, this code uses `NSCalendar currentCalendar` for the conversion which does have current timezone information. The `NSDateComponents` returned from `[currentCalendar components:fromDate:]` are in the timezone of that calendar. – progrmr Dec 04 '15 at 04:13
53

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.

Joe Masilotti
  • 16,422
  • 6
  • 73
  • 86
24

(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.

Ben Lachman
  • 2,897
  • 23
  • 40
  • 9
    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 Oct 13 '09 at 17:46
  • 9
    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 Oct 13 '09 at 19:02
  • 5
    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 Oct 13 '09 at 21:34
  • 2
    Worked for me, nice and clever. Saved me from all the NSCalendar overhead. – Lukasz Aug 21 '11 at 08:08
13

In Swift:

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

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

vikzilla
  • 3,656
  • 3
  • 29
  • 50
11

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...
}
aapierce
  • 887
  • 9
  • 14
7

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.

fbrereto
  • 34,250
  • 17
  • 118
  • 176
  • 1
    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 Oct 13 '09 at 18:24
  • 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 Oct 13 '09 at 18:57
  • how expensive is NSDateComponents? – pxl Oct 13 '09 at 19:34
  • Not sure; measurements would have to be done to see how much of an improvement it'd be to migrate away from it. – fbrereto Oct 13 '09 at 20:11
  • 4
    `NSDate` has this nice method: `timeIntervalSinceDate:` Therefore you don't even need corefoundation. – Georg Schölly Oct 14 '09 at 21:14
  • 2
    NSDateComponents is the best bet because then things like time zones and daylight savings are handled automatically. – JeremyP Feb 05 '14 at 20:31
  • 3
    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 Jul 30 '14 at 16:10
  • Could use an example. – Adama Feb 04 '15 at 17:11
5

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];
}
  • 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 Oct 14 '09 at 01:45
  • 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 Jan 14 '16 at 15:16
4
NSCalendar.currentCalendar().isDateInToday(yourNSDate)

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

philipp
  • 3,873
  • 1
  • 32
  • 32
1

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;
vikingosegundo
  • 51,126
  • 14
  • 131
  • 172
1

in Swift 3.0, iOS8+

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

}

or

if NSCalendar.current.isDateInToday(date) {

}
norbDEV
  • 3,737
  • 2
  • 27
  • 25
-1

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;
}
ZYiOS
  • 5,084
  • 3
  • 36
  • 43
-1

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;
}
Wayne
  • 56,476
  • 13
  • 125
  • 118