158

How to check if an NSDate belongs to today?

I used to check it using first 10 characters from [aDate description]. [[aDate description] substringToIndex:10] returns string like "YYYY-MM-DD" so I compared the string with the string returned by [[[NSDate date] description] substringToIndex:10].

Is there more fast and/or neat way to check?

Thanks.

vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
Seunghoon
  • 5,632
  • 5
  • 35
  • 41
  • I believe my method is the only one (as of my writing this) that actually refers to Apple's documentation and does what they suggest within it to determine if two days are the same. Although other methods may work, I believe my method is most future proof. http://stackoverflow.com/a/17641925/901641 – ArtOfWarfare Jul 14 '13 at 17:43
  • @ArtOfWarfare: I disagree. Your code still needs to deal with time intervals, but apple provides methods that already factor this away — see: http://stackoverflow.com/a/17642033/106435 – vikingosegundo Aug 05 '13 at 20:13
  • 1
    The NSDate "description" method displays the date in UTC format, that is Greenwich Mean Time, with no correction for daylight savings time. If you're late in the evening in the USA, or early in the morning in India, it will not display what is "today" according to your calendar. You need an NSCalendar object to translate the NSDate into a day, and to convert the current date into a day, then compare them. – gnasher729 Mar 24 '14 at 00:53

20 Answers20

314

In macOS 10.9+ & iOS 8+, there's a method on NSCalendar/Calendar that does exactly this!

- (BOOL)isDateInToday:(NSDate *)date 

So you'd simply do

Objective-C:

BOOL today = [[NSCalendar currentCalendar] isDateInToday:date];

Swift 3:

let today = Calendar.current.isDateInToday(date)
Catfish_Man
  • 41,261
  • 11
  • 67
  • 84
192

You can compare date components:

NSDateComponents *otherDay = [[NSCalendar currentCalendar] components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:aDate];
NSDateComponents *today = [[NSCalendar currentCalendar] components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:[NSDate date]];
if([today day] == [otherDay day] &&
   [today month] == [otherDay month] &&
   [today year] == [otherDay year] &&
   [today era] == [otherDay era]) {
    //do stuff
}

Edit:

I like stefan's method more, I think it makes for a cleaner and more understandable if statement:

NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:(NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]];
NSDate *today = [cal dateFromComponents:components];
components = [cal components:(NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:aDate];
NSDate *otherDate = [cal dateFromComponents:components];

if([today isEqualToDate:otherDate]) {
    //do stuff
}

Chris, I've incorporated your suggestion. I had to look up what era was, so for anyone else who doesn't know, it distinguishes between BC and AD. This is probably unnecessary for most people, but it's easy to check and adds some certainty, so I've included it. If you're going for speed, this probably isn't a good method anyway.


NOTE as with many answers on SO, after 7 years this is totally out of date. In Swift now just use .isDateInToday

Fattie
  • 27,874
  • 70
  • 431
  • 719
David Kanarek
  • 12,611
  • 5
  • 45
  • 62
  • 1
    Thanks. I think it is more neat and future-safe solution to my question. – Seunghoon Feb 25 '10 at 03:12
  • 1
    I think you need to include the era component, too. I think it's a coin toss between the two approaches; isEqualToDate seems nicer, but it seems a little wasteful and takes about the same number of lines to reconstruct a date from the components. – Chris Page Oct 23 '11 at 09:33
  • 1
    Since both "otherDay" and "today" only have those four fields filled anyway (that's all you requested), you can just use [today equalTo:otherDay], without jumping the hoops in the second one. – Glenn Maynard Dec 05 '12 at 01:23
  • @Glenn Are you suggesting I compare NSDateComponents in the first one? I hadn't really thought about that, but it also seems like a good idea. – David Kanarek Dec 05 '12 at 11:59
  • 1
    @DavidKanarek Digging up old posts...I wouldn't recommend comparing date components directly, if you get into a situation where you need to offset one of the components, you may get something like month == 3, day == 34. Converting to dates will correctly interpret this as April 3, comparing date components will not see this as the same as month == 4, day == 3. – Sean Kladek Mar 26 '13 at 19:22
28

This is an offshoot to your question, but if you want to print an NSDate with "Today" or "Yesterday", use the function

- (void)setDoesRelativeDateFormatting:(BOOL)b

for NSDateFormatter

Jon
  • 7,848
  • 1
  • 40
  • 41
16

I would try to get today's date normalized to midnight and the second date, normalize to midnight then compare if it is the same NSDate.

From an Apple example here's how you normalize to midnight today's date, do the same for the second date and compare:

NSCalendar * gregorian = [[NSCalendar alloc]
                               initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents * components =
    [gregorian components:
                 (NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit)
                 fromDate:[NSDate date]];
NSDate * today = [gregorian dateFromComponents:components];
stefanB
  • 77,323
  • 27
  • 116
  • 141
  • I think you need to include the era, too. – Chris Page Oct 23 '11 at 09:34
  • Shouldn't a date be normalised by setting time to 12, i.e. midday, not midnight? Since date is in GMT, setting it to midday ensures that time zone variations both ways (and they go up to 12h only both ways) does not 'jump' into the day before or after. – artooras Jun 06 '14 at 15:54
15

Working Swift extension of the suggestion by Catfish_Man:

extension Date {

    var isToday: Bool {
        Calendar.current.isDateInToday(self)
    }
    
}
Benno Kress
  • 2,637
  • 27
  • 39
12

No need to juggle with components, eras and stuff.

NSCalendar provides an method to get the beginning of a certain time unit for an existing date.

This code will get the begin of today and another date and compare that. If it evaluates to NSOrderedSame, both dates are during the same day — so today.

NSDate *today = nil;
NSDate *beginningOfOtherDate = nil;

NSDate *now = [NSDate date];
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&today interval:NULL forDate:now];
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&beginningOfOtherDate interval:NULL forDate:beginningOfOtherDate];

if([today compare:beginningOfOtherDate] == NSOrderedSame) {
    //otherDate is a date in the current day
}
Community
  • 1
  • 1
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
9
extension NSDate {
  func isToday() -> Bool {
    let cal = NSCalendar.currentCalendar()
    var components = cal.components([.Era, .Year, .Month, .Day], fromDate:NSDate())
    let today = cal.dateFromComponents(components)!

    components = cal.components([.Era, .Year, .Month, .Day], fromDate:self)
    let otherDate = cal.dateFromComponents(components)!

    return today.isEqualToDate(otherDate)
}

Worked for me on Swift 2.0

slxl
  • 635
  • 1
  • 10
  • 23
Omar Albeik
  • 1,332
  • 13
  • 17
6

Swift version of the best answer:

let cal = NSCalendar.currentCalendar()
var components = cal.components([.Era, .Year, .Month, .Day], fromDate:NSDate())
let today = cal.dateFromComponents(components)!

components = cal.components([.Era, .Year, .Month, .Day], fromDate:aDate);
let otherDate = cal.dateFromComponents(components)!

if(today.isEqualToDate(otherDate)) {
    //do stuff
}
Vojtech Vrbka
  • 5,342
  • 6
  • 44
  • 63
5

Refer to Apple's documentation entry entitled "Performing Calendar Calculations" [link].

Listing 13 on that page suggests that to determine the number of midnights between days, you use:

- (NSInteger)midnightsFromDate:(NSDate *)startDate toDate:(NSDate *)endDate
{
    NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
    NSInteger startDay = [calendar ordinalityOfUnit:NSDayCalendarUnit
                                             inUnit:NSEraCalendarUnit
                                            forDate:startDate];
    NSInteger endDay = [calendar ordinalityOfUnit:NSDayCalendarUnit
                                           inUnit:NSEraCalendarUnit
                                          forDate:endDate];
    return endDay - startDay;
}

You may then determine if two days are the same by using that method and seeing if it returns 0 or not.

ArtOfWarfare
  • 20,617
  • 19
  • 137
  • 193
  • 2
    Bear in mind that an Era does start at 00:00 GMT! This method might not work correctly if one of the dates (or both) are not in the GMT timezone. – Andreas Ley Aug 06 '13 at 11:43
5

You could also check the time interval between the date you have, and the current date:

[myDate timeIntervalSinceNow]

This will give you the time interval, in seconds, between myDate and the current date/time.

Link.

Edit: Note to everyone: I'm well aware that [myDate timeIntervalSinceNow] does not unambiguously determine whether myDate is today.

I am leaving this answer as is so that if someone is looking for something similar and [myDate timeIntervalSinceNow] is useful, they may find it here.

alesplin
  • 1,332
  • 14
  • 23
  • 1
    What you say is correct, but the interval doesn't really help to check if date is today. – Jaanus Feb 25 '10 at 02:33
  • 3
    Just keep in mind that this is it tells you if you are within 24 hours since now, but not if the dates are the same. A one second difference can be different dates and a 86399 second difference can be the same date. – David Kanarek Feb 25 '10 at 02:34
  • @David: True. Didn't think about that as I was doing 3 other things while answering the question, and your answer wasn't posted when I loaded the page. – alesplin Feb 25 '10 at 02:37
  • 1
    This does not answer the question at all. You should delete it. – vikingosegundo Nov 07 '13 at 13:50
  • I'm not inclined to do so, given that at the very least it doesn't contain anything that is going to cause harm (unlike some of the other 'magic number' answers below). It's obviously not correct or complete, but it could be an approach that may prove useful to someone doing something similar that happens across it. – alesplin Nov 12 '13 at 22:05
  • Actually, two NSDates that are 86399 + 3600 seconds apart can be on the same day, but only once a year. – gnasher729 Mar 24 '14 at 00:55
  • 1
    This answer is nothing but noise. It does neither attempt to answer the question nor is it necessary to refer to `timeIntervalSinceNow` as it is covered in many other posts. Also handling day comparison with checking for seconds encourage error-prone division by 86400. – vikingosegundo Jun 16 '14 at 20:45
4

Swift Extension based on the best answers:

extension NSDate {
    func isToday() -> Bool {
        let cal = NSCalendar.currentCalendar()
        if cal.respondsToSelector("isDateInToday:") {
            return cal.isDateInToday(self)
        }
        var components = cal.components((.CalendarUnitEra | .CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay), fromDate:NSDate())
        let today = cal.dateFromComponents(components)!

        components = cal.components((.CalendarUnitEra | .CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay), fromDate:self);
        let otherDate = cal.dateFromComponents(components)!
        return today.isEqualToDate(otherDate)
    }
}
ZiggyST
  • 587
  • 6
  • 11
2

There is an easier way than many of the above answers!

NSDate *date = ... // The date you wish to test
NSCalendar *calendar = [NSCalendar currentCalendar];

if([calendar isDateInToday:date]) {
    //do stuff
}
aryland
  • 133
  • 1
  • 8
2

If you have a lot of these date comparisons, then the calls to calendar:components:fromDate start to take up a lot of time. According to some profiling I have done, they seem to be quite expensive.

Say you are trying to determine which from some array of dates, say NSArray *datesToCompare, are the same day as some given day, say NSDate *baseDate, then you can use something like the following (partly adapted from an answer above):

NSDate *baseDate = [NSDate date];

NSArray *datesToCompare = [NSArray arrayWithObjects:[NSDate date], 
                           [NSDate dateWithTimeIntervalSinceNow:100],
                           [NSDate dateWithTimeIntervalSinceNow:1000],
                           [NSDate dateWithTimeIntervalSinceNow:-10000],
                           [NSDate dateWithTimeIntervalSinceNow:100000],
                           [NSDate dateWithTimeIntervalSinceNow:1000000],
                           [NSDate dateWithTimeIntervalSinceNow:50],
                           nil];

// determine the NSDate for midnight of the base date:
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* comps = [calendar components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) 
                                       fromDate:baseDate];
NSDate* theMidnightHour = [calendar dateFromComponents:comps];

// set up a localized date formatter so we can see the answers are right!
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];

// determine which dates in an array are on the same day as the base date:
for (NSDate *date in datesToCompare) {
    NSTimeInterval interval = [date timeIntervalSinceDate:theMidnightHour];
    if (interval >= 0 && interval < 60*60*24) {
        NSLog(@"%@ is on the same day as %@", [dateFormatter stringFromDate:date], [dateFormatter stringFromDate:baseDate]);
    }
    else {
        NSLog(@"%@ is NOT on the same day as %@", [dateFormatter stringFromDate:date], [dateFormatter stringFromDate:baseDate]);
    }
}

Output:

Nov 23, 2011 1:32:00 PM is on the same day as Nov 23, 2011 1:32:00 PM
Nov 23, 2011 1:33:40 PM is on the same day as Nov 23, 2011 1:32:00 PM
Nov 23, 2011 1:48:40 PM is on the same day as Nov 23, 2011 1:32:00 PM
Nov 23, 2011 10:45:20 AM is on the same day as Nov 23, 2011 1:32:00 PM
Nov 24, 2011 5:18:40 PM is NOT on the same day as Nov 23, 2011 1:32:00 PM
Dec 5, 2011 3:18:40 AM is NOT on the same day as Nov 23, 2011 1:32:00 PM
Nov 23, 2011 1:32:50 PM is on the same day as Nov 23, 2011 1:32:00 PM
  • Thanks for this. The accepted way was way too CPU intensive for our app so we needed to optimize this. – Accatyyc Jul 09 '12 at 14:05
  • 1
    This code will fail for daylight saving times. Everybody using code with the infamous number 86,400 deserve the headache it may cause. – vikingosegundo Nov 08 '13 at 14:41
0

This could probably be reworked as an NSDate category, but i used:

// Seconds per day (24h * 60m * 60s)
#define kSecondsPerDay 86400.0f

+ (BOOL) dateIsToday:(NSDate*)dateToCheck
{
    // Split today into components
    NSCalendar* gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents* comps = [gregorian components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit) 
                                        fromDate:[NSDate date]];

    // Set to this morning 00:00:00
    [comps setHour:0];
    [comps setMinute:0];
    [comps setSecond:0];
    NSDate* theMidnightHour = [gregorian dateFromComponents:comps];
    [gregorian release];

    // Get time difference (in seconds) between date and then
    NSTimeInterval diff = [dateToCheck timeIntervalSinceDate:theMidnightHour];
    return ( diff>=0.0f && diff<kSecondsPerDay );
}

(However, comparing the two date strings as in the original question almost feels 'cleaner'..)

LeoN
  • 59
  • 1
  • 2
  • Why include hours, minutes and seconds when creating `comps` when you're immediately setting them to zero? Also, I think you need to include the era. – Chris Page Oct 23 '11 at 09:30
  • Please note that while this will work in almost every case, there are such things as leap seconds. I don't know if or how Apple's NSDate class handles them, but there is some chance this won't work. This is more a "did you know" rather than a criticism of your method as they're exceedingly rare (24 in history). – David Kanarek Nov 23 '11 at 13:36
  • what about dates that are not 24 hours long, but 23 or 25 due to Daylight Saving Times? – vikingosegundo Oct 04 '13 at 12:11
0

for iOS7 and earlier:

//this is now => need that for the current date
NSDate * now = [NSDate date];

NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[calendar setTimeZone:[NSTimeZone systemTimeZone]];

NSDateComponents * components = [calendar components:( NSYearCalendarUnit|    NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate: now];

[components setMinute:0];
[components setHour:0];
[components setSecond:0];

//this is Today's Midnight
NSDate *todaysMidnight = [calendar dateFromComponents: components];



//now timeIntervals since Midnight => in seconds
NSTimeInterval todayTimeInterval = [now timeIntervalSinceDate: todaysMidnight];

//now timeIntervals since OtherDate => in seconds
NSTimeInterval otherDateTimeInterval = [now timeIntervalSinceDate: otherDate];

if(otherDateTimeInterval > todayTimeInterval) //otherDate is not in today
{
    if((otherDateTimeInterval - todayTimeInterval) <= 86400) //86400 == a day total seconds
    {
        @"yesterday";
    }
    else
    {
        @"earlier";
    }
}
else
{
    @"today";
}


now = nil;
calendar = nil;
components = nil;
todaysMidnight = nil;

NSLog("Thank you :-)");
0

Check our Erica Sadun's great NSDate extension. Very simple to use. Fine it here:

http://github.com/erica/NSDate-Extensions

It's already there in this post: https://stackoverflow.com/a/4052798/362310

Community
  • 1
  • 1
Vaibhav Saran
  • 12,848
  • 3
  • 65
  • 75
0

The correct and safe solution without force-unwrapping, working on Swift 2.2 and before iOS 8:

func isToday() -> Bool {
    let calendar = NSCalendar.currentCalendar()
    if #available(iOS 8.0, *) {
        return calendar.isDateInToday(self)
    }

    let todayComponents = calendar.components([.Era, .Year, .Month, .Day], fromDate:NSDate())
    let dayComponents = calendar.components([.Era, .Year, .Month, .Day], fromDate:self)

    guard let today = calendar.dateFromComponents(todayComponents),
        day = calendar.dateFromComponents(dayComponents) else {
        return false
    }

    return today.compare(day) == .OrderedSame
}
fpg1503
  • 7,492
  • 6
  • 29
  • 49
-1

Here's my 2 cent answer building on the accepted answer but supporting the newer API as well. Note: I use the Gregorian calendar as most time stamps are GMT but change yours as you see fit

func isDateToday(date: NSDate) -> Bool {
    let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
    if calendar.respondsToSelector("isDateInToday:") {
        return calendar.isDateInToday(date)
    }
    let dateComponents = NSCalendarUnit.CalendarUnitYear | NSCalendarUnit.CalendarUnitMonth | NSCalendarUnit.CalendarUnitDay
    let today = calendar.dateFromComponents(calendar.components(dateComponents, fromDate: NSDate()))!
    let dateToCompare = calendar.dateFromComponents(calendar.components(dateComponents, fromDate: date))!

    return dateToCompare == today
}
Daniel Galasko
  • 23,617
  • 8
  • 77
  • 97
-3

My solution is calculate how much days passed since 1970 by division and compare the integer part

#define kOneDay (60*60*24)
- (BOOL)isToday {
  NSInteger offset = [[NSTimeZone defaultTimeZone] secondsFromGMT];

  NSInteger days =[self timeIntervalSince1970] + offset;
  NSInteger currentDays = [[NSDate date] timeIntervalSince1970] + offset;
  return (days / kOneDay == currentDays / kOneDay);
}
stcui
  • 106
  • 4
  • That's actually not a bad solution. Not sure why it was so downvoted. Using integer division will cut off the remainder and compare just the "days" value of the unix long time, after correcting for GMT. I think this is the cleanest solution of them all to be quite frank. – devios1 Aug 05 '13 at 18:38
  • 1
    @chaiguy: this is a bad solution as it assumes, that each day is 24 hours long — what is not the case. Due to Daylight Saving Times days can be 23, 24 or 25 hours long. – vikingosegundo Oct 04 '13 at 12:14
  • Hmm that's true. However the average per year is exactly 24 hours, so it will only potentially be off by one hour worst case, as I see it. Perhaps that could be checked for explicitly. – devios1 Oct 04 '13 at 16:40
  • 2
    @chaiguy: no. just never use a code that contains 60*60*24. or live with your headache. – vikingosegundo Oct 08 '13 at 05:36
  • "secondsFromGMT" changes abruptly twice a year in most places in the world. Much safer to use the NSCalendar methods. – gnasher729 Mar 24 '14 at 00:59
-4
NSDate *dateOne = yourDate;
NSDate *dateTwo = [NSDate date];  

switch ([dateOne compare:dateTwo])
{  
    case NSOrderedAscending:  
        NSLog(@”NSOrderedAscending”);  
        break;  

    case NSOrderedSame: 
        NSLog(@”NSOrderedSame”);  
        break;

    case NSOrderedDescending:  
        NSLog(@”NSOrderedDescending”);  
        break;  
}  
rajesh
  • 592
  • 4
  • 13