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?
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?
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];
}
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.
(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.
In Swift:
NSCalendar.currentCalendar().isDate(yourDate, equalToDate: yourOtherDate, toUnitGranularity: .Day)
It will return true if they are on the same day.
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.
@interface NSDate (SameDay)
- (BOOL)isSameDayAsDate:(NSDate*)otherDate;
@end
#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...
}
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.
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];
}
NSCalendar.currentCalendar().isDateInToday(yourNSDate)
Available in iOS 8.0 and later and OS X v10.9 and later.
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;
in Swift 3.0, iOS8+
if NSCalendar.current.isDate(Date(), equalTo: date, toGranularity: .day) {
}
or
if NSCalendar.current.isDateInToday(date) {
}
I create my own utility classes.
@interface ZYUtility : NSObject
+ (NSDate *)yesterday;
+ (NSDate *)tomorrow;
+ (NSDate *)endOfDay:(NSDate *)date;
+ (NSDate *)beginningOfDay:(NSDate *)date;
@end
+ (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;
}
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;
}