247

I am trying to achieve a dropBox sync and need to compare the dates of two files. One is on my dropBox account and one is on my iPhone.

I came up with the following, but I get unexpected results. I guess I'm doing something fundamentally wrong when comparing the two dates. I simply used the > < operators, but I guess this is no good as I am comparing two NSDate strings. Here we go:

NSLog(@"dB...lastModified: %@", dbObject.lastModifiedDate); 
NSLog(@"iP...lastModified: %@", [self getDateOfLocalFile:@"NoteBook.txt"]);

if ([dbObject lastModifiedDate] < [self getDateOfLocalFile:@"NoteBook.txt"]) {
    NSLog(@"...db is more up-to-date. Download in progress...");
    [self DBdownload:@"NoteBook.txt"];
    NSLog(@"Download complete.");
} else {
    NSLog(@"...iP is more up-to-date. Upload in progress...");
    [self DBupload:@"NoteBook.txt"];
    NSLog(@"Upload complete.");
}

This gave me the following (random & wrong) output:

2011-05-11 14:20:54.413 NotePage[6918:207] dB...lastModified: 2011-05-11 13:18:25 +0000
2011-05-11 14:20:54.414 NotePage[6918:207] iP...lastModified: 2011-05-11 13:20:48 +0000
2011-05-11 14:20:54.415 NotePage[6918:207] ...db is more up-to-date.

or this one which happens to be correct:

2011-05-11 14:20:25.097 NotePage[6903:207] dB...lastModified: 2011-05-11 13:18:25 +0000
2011-05-11 14:20:25.098 NotePage[6903:207] iP...lastModified: 2011-05-11 13:19:45 +0000
2011-05-11 14:20:25.099 NotePage[6903:207] ...iP is more up-to-date.
Nick Weaver
  • 47,228
  • 12
  • 98
  • 108
n.evermind
  • 11,944
  • 19
  • 78
  • 122
  • 11
    Duplicates: [1](http://stackoverflow.com/questions/5629154/) [2](http://stackoverflow.com/questions/2258703/) [3](http://stackoverflow.com/questions/5429280/) [4](http://stackoverflow.com/questions/2199488/) [5](http://stackoverflow.com/questions/994855/) [6](http://stackoverflow.com/questions/5727821/) [&c.](http://stackoverflow.com/search?page=4&tab=relevance&q=compare%20nsdate) – jscs May 11 '11 at 17:47
  • 1
    @JoshCaswell if it's a real duplicate, why not merge them? You've done it before... – Dan Rosenstark May 07 '14 at 23:47
  • 1
    Only diamond moderators can perform a merge, @Yar. – jscs May 08 '14 at 00:27

13 Answers13

668

Let's assume two dates:

NSDate *date1;
NSDate *date2;

Then the following comparison will tell which is earlier/later/same:

if ([date1 compare:date2] == NSOrderedDescending) {
    NSLog(@"date1 is later than date2");
} else if ([date1 compare:date2] == NSOrderedAscending) {
    NSLog(@"date1 is earlier than date2");
} else {
    NSLog(@"dates are the same");
}

Please refer to the NSDate class documentation for more details.

Graham
  • 7,431
  • 18
  • 59
  • 84
Nick Weaver
  • 47,228
  • 12
  • 98
  • 108
  • Lovely! Beats messing about with [date1 earlierDate:date2] etc... Thanks - for some reason I'd never thought to use compare: before. – SomaMan Apr 20 '12 at 10:29
  • 11
    I like to rely on the fact that NSOrderedAscending < 0 and NSOrderedDescending > 0. That makes the comparison easier to read: [date1 compare:date2] < 0 /* date1 < date2 */ and avoids the (easy to make) mistake @albertamg pointed out. ;-) – jpap Apr 04 '13 at 10:23
  • Well - the compare method is as error-prone as off-by-one errors. Thus, you should use (NSDate *)laterDate:(NSDate *)anotherDate which will return the later date of both. so you just compare your expected result and you're done! No fiddling around with "Waaait descending / ascending ?!" – omni May 11 '14 at 08:03
  • @jpap That messed me up as well; it seems that Apple wants you to think of the result as `date1 -> date2` is ascending/descending (and therefore date1 is later or earlier respectively). – Ja͢ck Jul 04 '14 at 07:34
  • @Jack it's a more abstract way without magic numbers(-1,0,1) for sorting algorithms to put elements in order. You could also redefine those constants yourself with a more readable name. My answer is doing the job but is not the award winner of readable code. Scroll down, there are other good ones. – Nick Weaver Jul 04 '14 at 08:11
  • 1
    These is a serious issue with this one I found out today, if you compare two same dates but with different time the results will vary, e.g If I compare todays date '2017-03-27 14:26:38 +0000' with '2017-03-27 10:16:14 +0000' for `NSOrderedDescending` the result will be TRUE, whereas if I compare today's dates '2017-03-27 00:00:01 +0000' with '2017-03-27 10:07:29 +0000' the result will be FALSE. Not saying the answer is wrong but for someone who wants to compare just the dates, this shouldn't be used. –  Mar 27 '17 at 10:31
50

Late to the party, but another easy way of comparing NSDate objects is to convert them into primitive types which allows for easy use of '>' '<' '==' etc

eg.

if ([dateA timeIntervalSinceReferenceDate] > [dateB timeIntervalSinceReferenceDate]) {
    //do stuff
}

timeIntervalSinceReferenceDate converts the date into seconds since the reference date (1 January 2001, GMT). As timeIntervalSinceReferenceDate returns a NSTimeInterval (which is a double typedef), we can use primitive comparators.

So Over It
  • 3,668
  • 3
  • 35
  • 46
15

In Swift, you can overload existing operators:

func > (lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.timeIntervalSinceReferenceDate > rhs.timeIntervalSinceReferenceDate
}

func < (lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.timeIntervalSinceReferenceDate < rhs.timeIntervalSinceReferenceDate
}

Then, you can compare NSDates directly with <, >, and == (already supported).

Andrew
  • 4,145
  • 2
  • 37
  • 42
  • If I try to make an extension out of this, I get "Operators are only allowed at global scope", suggestions? – JohnVanDijk Nov 05 '15 at 19:10
  • @JohnVanDijk you can't put it inside an extension. I'd put it in the same file as the extension, but outside the `{ ... }` – Andrew Nov 05 '15 at 21:02
13

NSDate has a compare function.

compare: Returns an NSComparisonResult value that indicates the temporal ordering of the receiver and another given date.

(NSComparisonResult)compare:(NSDate *)anotherDate

Parameters: anotherDate The date with which to compare the receiver. This value must not be nil. If the value is nil, the behavior is undefined and may change in future versions of Mac OS X.

Return Value:

  • If the receiver and anotherDate are exactly equal to each other, NSOrderedSame
  • If the receiver is later in time than anotherDate, NSOrderedDescending
  • If the receiver is earlier in time than anotherDate, NSOrderedAscending.
phi
  • 10,634
  • 6
  • 53
  • 88
Gary
  • 4,198
  • 2
  • 21
  • 26
  • @Irene is there a way to compare two NSDate objects in which only the time component is different? For some reason the above method doesn't work. – ThE uSeFuL Nov 20 '12 at 06:08
12

You want to use the NSDate compare:, laterDate:, earlierDate:, or isEqualToDate: methods. Using the < and > operators in this situation is comparing the pointers, not the dates

Dan F
  • 17,654
  • 5
  • 72
  • 110
11
- (NSDate *)earlierDate:(NSDate *)anotherDate

This returns the earlier of the receiver and anotherDate. If both are same, the receiver is returned.

Maulik
  • 19,348
  • 14
  • 82
  • 137
  • 1
    Note that the backing object of NSDates may be optimized on 64-bit versions of your compiled code such that dates that represent the same time will have the same address. Thus if `cDate = [aDate earlierDate:bDate]` then `cDate == aDate` and `cDate == bDate` can both be true. Found this doing some date work on iOS 8. – Ben Flynn Jul 11 '15 at 04:36
  • 1
    Conversely, on 32 bit platforms, if the dates aren't the same, `-earlierDate:` (and `-laterDate:`) can return neither the receiver nor the argument. – Ben Lings Feb 28 '18 at 18:08
  • Actually - my earlier comment about 32 bit platforms isn't correct. `-earlierDate:` and `-laterDate:` to either return the parameter or the receiver. However, `NSDate` properties on `NSManagedObject`s will return different instances for each call. – Ben Lings Oct 27 '20 at 12:07
7

Some date utilities, including comparisons IN ENGLISH, which is nice:

#import <Foundation/Foundation.h>


@interface NSDate (Util)

-(BOOL) isLaterThanOrEqualTo:(NSDate*)date;
-(BOOL) isEarlierThanOrEqualTo:(NSDate*)date;
-(BOOL) isLaterThan:(NSDate*)date;
-(BOOL) isEarlierThan:(NSDate*)date;
- (NSDate*) dateByAddingDays:(int)days;

@end

The implementation:

#import "NSDate+Util.h"


@implementation NSDate (Util)

-(BOOL) isLaterThanOrEqualTo:(NSDate*)date {
    return !([self compare:date] == NSOrderedAscending);
}

-(BOOL) isEarlierThanOrEqualTo:(NSDate*)date {
    return !([self compare:date] == NSOrderedDescending);
}
-(BOOL) isLaterThan:(NSDate*)date {
    return ([self compare:date] == NSOrderedDescending);

}
-(BOOL) isEarlierThan:(NSDate*)date {
    return ([self compare:date] == NSOrderedAscending);
}

- (NSDate *) dateByAddingDays:(int)days {
    NSDate *retVal;
    NSDateComponents *components = [[NSDateComponents alloc] init];
    [components setDay:days];

    NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    retVal = [gregorian dateByAddingComponents:components toDate:self options:0];
    return retVal;
}

@end
Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
6

Why don't you guys use these NSDate compare methods:

- (NSDate *)earlierDate:(NSDate *)anotherDate;
- (NSDate *)laterDate:(NSDate *)anotherDate;
emotality
  • 12,795
  • 4
  • 39
  • 60
justicepenny
  • 2,004
  • 2
  • 24
  • 48
6

You should use :

- (NSComparisonResult)compare:(NSDate *)anotherDate

to compare dates. There is no operator overloading in objective C.

Shaik Riyaz
  • 11,204
  • 7
  • 53
  • 70
Joris Mans
  • 6,024
  • 6
  • 42
  • 69
4

I have encounter almost same situation, but in my case I'm checking if number of days difference

NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *compDate = [cal components:NSDayCalendarUnit fromDate:fDate toDate:tDate options:0];
int numbersOfDaysDiff = [compDate day]+1; // do what ever comparison logic with this int.

Useful when you need to compare NSDate in Days/Month/Year unit

Andy
  • 5,287
  • 2
  • 41
  • 45
2

You can compare two date by this method also

        switch ([currenttimestr  compare:endtimestr])
        {
            case NSOrderedAscending:

                // dateOne is earlier in time than dateTwo
                break;

            case NSOrderedSame:

                // The dates are the same
                break;
            case NSOrderedDescending:

                // dateOne is later in time than dateTwo


                break;

        }
kapil
  • 671
  • 6
  • 9
1

Use this simple function for date comparison

-(BOOL)dateComparision:(NSDate*)date1 andDate2:(NSDate*)date2{

BOOL isTokonValid;

if ([date1 compare:date2] == NSOrderedDescending) {
    NSLog(@"date1 is later than date2");
    isTokonValid = YES;
} else if ([date1 compare:date2] == NSOrderedAscending) {
    NSLog(@"date1 is earlier than date2");
    isTokonValid = NO;
} else {
    isTokonValid = NO;
    NSLog(@"dates are the same");
}

return isTokonValid;}
Akhtar
  • 3,172
  • 5
  • 19
  • 21
0

I have tried it hope it works for you

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];      
int unitFlags =NSDayCalendarUnit;      
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];     
NSDate *myDate; //= [[NSDate alloc] init];     
[dateFormatter setDateFormat:@"dd-MM-yyyy"];   
myDate = [dateFormatter dateFromString:self.strPrevioisDate];     
NSDateComponents *comps = [gregorian components:unitFlags fromDate:myDate toDate:[NSDate date] options:0];   
NSInteger day=[comps day];
Jon Egerton
  • 40,401
  • 11
  • 97
  • 129