This is my first time comparing dates in Objective-C. I've been searching the web for a while and all the examples I found involve building a NSDate from a string so I decided to ask a new question here...
My question is as follows:
I need to know if two NSDates are in the same day, ignoring the time. I got two NSArray's containing a set of dates and one by one I need to determine which one from the first NSArray is in the same day as in the second array.
- (void)setActiveDaysColor:(UIColor *)activeDaysColor
{
for (DayView *actualDay in _days)
{
NSDate *actualDayDate = [actualDay date];
for (NSDate *activeDayDate in self.dates)
{
// Comparison code
// If both dates are the same, tint actualDay with a different color
}
}
}
Thank you in advance and have a nice day.
Alex.
create new dates by omitting the time components. and use one of the compare methods
earlierDate:
laterDate:
compare:
isEqualToDate
example
NSCalendar *calendar = [NSCalendar currentCalendar];
NSInteger components = (NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit);
NSDateComponents *firstComponents = [calendar components:components fromDate:firstDate];
NSDateComponents *secondComponents = [calendar components:components fromDate:secondDate];
NSDate *date1 = [calendar dateFromComponents:firstComponents];
NSDate *date2 = [calendar dateFromComponents:secondComponents];
NSComparisonResult result = [date1 compare:date2];
if (result == NSOrderedAscending) {
} else if (result == NSOrderedDescending) {
} else {
}
-(BOOL)isSameDay:(NSDate*)date1 otherDay:(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];}
Check NSDate documentation, These are the methods to compare date
isEqualToDate
earlierDate
laterDate
compare
in your case
if([actualDayDate isEqualToDate:activeDayDate])
{
}
Thanks for all your answers.
I found a cleaner answer to my question answered in a totally unrelated post but that actually works perfectly.
if ((unsigned int)[actualDayDate timeIntervalSinceDate:activeDayDate] / 60 / 60 / 24 == 0)
{
// do Stuff
}
Instead of the loops that you have in your code you could use a predicate to filter out all the objects that are today. Filtering out what dates are today is done by comparing it to the beginning of today and the end of today.
You can set any NSDate to the beginning of that date like this (see this answer)
NSDate *beginDate = [NSDate date];
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&beginDate interval:NULL forDate:beginDate];
Then to get the end date you simply add one day to it. Don't add days by calculating the number of seconds. This won't work with daylight savings! Do it like this (also see this answer):
NSDateComponents *oneDay = [[NSDateComponents alloc] init];
[oneDay setDay:1];
// one day after begin date
NSDate *endDate = [[NSCalendar currentCalendar] dateByAddingComponents:oneDay toDate:beginDate options:0];
Now that you have the two dates that define the range for today you can filter all your DayViews using a NSPredicate to get a new array of all the DayViews who's day is today, like this (see this answer):
// filter DayViews to only include those where the day is today
NSArray *daysThatAreToday = [_days filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(date >= %#) AND (date <= %#)", beginDate, endDate]];
Now you can apply the tint color to all the DayViews by enumerating the new array (that contains todays DayViews)
[daysThatAreToday enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// Set the tint color here...
}];
This, in my opinion, is a clean but more importantly a correct way of solving your problem. It reads clearly and handles daylight savings and other calendars then gregorian. It can also easily be reused if you want to tint all the DayViews for a certain week (or any other time period).
Related
How to get this to work for the whole month. I am able to call the method for that day.
So, what I'm trying to do is - If this month has 31 days and the 1st of the month is on a Friday or Saturday then use certain constraints. I have been trying for several days now on how to get this to work for the whole month. I have looked at almost all the posts on NSDate and tried to find a similar question, to see if it has been asked before. I'm finding it difficult to wrap my head around this. What am I not doing? Thank you for your time in advance.
NSDate *now = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSRange currentRange = [gregorian rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:now];
NSInteger wantedWeekDay = 6; //for day of week
NSInteger numberOfDays = currentRange.length;
NSDateComponents *component = [[NSCalendar currentCalendar] components:NSCalendarUnitWeekday fromDate:now];
NSInteger firstDateWeekDay = component.weekday;
NSLog(#"First Date Week Day = %ld",(long)firstDateWeekDay);
NSLog(#"weekday : %li",(long)firstDateWeekDay);
if (wantedWeekDay == firstDateWeekDay) {
if (numberOfDays ==31) {
NSLog(#"Yes");
}
}else {
NSLog(#"no");
}
I am trying to compare two times in iOS. I have list of times in my array i just want to check current time is matching to the any one the array value. can any help how to do that. i searched whole internet i did't get any idea.
I saw this question answer but i can't get a exact result.
According to Apple documentation of NSDate 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.
Return Value
If:
The receiver and anotherDate are exactly equal to each other, NSOrderedSame
The receiver is later in time than anotherDate, NSOrderedDescending
The receiver is earlier in time than anotherDate, NSOrderedAscending
In other words:
if ([date1 compare:date2]==NSOrderedSame) ...
Note that it might be easier to read and write this :
if ([date2 isEqualToDate:date2]) ...
-(NSString *)determineDateFromstring:(long long)date
{
NSTimeInterval interval=date;
NSDate *currentTime=[NSDate dateWithTimeIntervalSince1970:interval/1000];
NSLocale *gbLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_GB"];
NSNumber *myDateInString=[NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970]*1000];
NSTimeInterval inte=[myDateInString longLongValue];
NSDate *todayTime=[NSDate dateWithTimeIntervalSince1970:inte/1000];
NSCalendar *currentcalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [currentcalendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:todayTime];
int currentday=[components day];
int currentyear=[components year];
NSCalendar *paramtercalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *parametercomponents = [paramtercalendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:currentTime];
int currentday1=[parametercomponents day];
int currentyear1=[parametercomponents year];
if (currentday==currentday1) {
Nslog(date are same);
}else if(currentyear==currentyear1) {
Nslog(year are same);
}else{
}
}
if u want to check two date are equal or not then u can compare NStimeIntervals(long long values). if same then time ,date and year are same otherwise different.
I hope this answer will be the right to your question.......
Why don't you check the interval since 1970 and compare the integer value..
long long int i = [[NSDate date] timeIntervalSince1970];
I hope this will help.Also if you have values in form of NSString then you first need to convert them to NSDate
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"yyyy-MM-dd HH:mm:ssSSSSSS";
NSDate *d = [dateFormatter dateFromString:#"your date string"];
Im trying to fetch all the objects in an entity matching a user selectedDate (it's an NSDate).
The Core Data code is fine but my predicate keeps returning 0 results, the dates are the same in the database as the user is selecting.
How should the selectedDate be compared with a date from an entity using a predicate?
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(eDate = %#)", selectedDate];
Your predicate looks to be fine.
The reason you're finding zero result are returned however may be that the dates aren't entirely the same.
For example:
05/04/2012 13:37:00 will not match with 05/04/2012 13:37:01 as these two values are not exactly the same.
Do you want to check the date (day, month, year) as well as the time?
If you only want to check the date, you should create a start date and end date and compare them using the user selected date as a frame of reference.
Something similar to this should create a date and time for 00:00:00.
//gather current calendar
NSCalendar *calendar = [NSCalendar currentCalendar];
//gather date components from date
NSDateComponents *dateComponents = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:[NSDate date]];
//set date components
[dateComponents setHour:0];
[dateComponents setMinute:0];
[dateComponents setSecond:0];
//return date relative from date
return [calendar dateFromComponents:dateComponents];
Create another date by setting the hours, minutes and seconds to 23:59:59 and check that your user selected date falls between these ranges.
[NSPredicate predicateWithFormat:#"date BETWEEN %#", [NSArray arrayWithObjects:startOfDay, endOfDay, nil]]
While in human communication date often is equal with day, it is not the same with NSDate objects: A NSDate represents a single moment in time. Dates that are different just in seconds aren't equal. And actually they dont represent days, month, year at all, as this is different from calendar system to calendar system
you must define for yourself if dates in the same minute, hour, day … are equal. So basically you must teach the program to allow some fuzziness.
here for minute resolution
NSDate *startDate = ....;
NSTimeInterval length;
[[NSCalendar currentCalendar] rangeOfUnit:NSMinuteCalendarUnit
startDate:&startDate
interval:&length
forDate:startDate];
NSDate *endDate = [startDate dateByAddingTimeInterval:length];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(eDate >= %#) AND (eDate < %#)", startDate, endDate];
For dates on the same day (aware of Timezones and Daylight Saving Times) you just would change this:
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit
startDate:&startDate
interval:&length
forDate:startDate];
I can get working with some modifications to the accepted answer and using the iOS 8 API for create dates with time offset. The code:
NSCalendar *calendar = [[FFSharedCalendar singleton] getGregorianCalendar];
NSDate *startOfDay = [calendar dateBySettingHour:0 minute:0 second:0 ofDate:[NSDate date] options:0];
NSDate *endOfDay = [calendar dateBySettingHour:23 minute:59 second:59 ofDate:[NSDate date] options:0];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"createdAt > %# AND createdAt < %#", startOfDay, endOfDay];
NSArray* plans = [Plan MR_findAllWithPredicate:predicate];
Hope it helps someone
I've recently spent some time attempting to solve this same problem and have resolved the following, now updated for iOS 8 and above...
NSDate *dateDay = nil;
NSDate *dateDayStart = nil;
NSDate *dateDayNext = nil;
dateDay = <<USER_INPUT>>; //for example - selectedDate
dateDayStart = [[NSCalendar currentCalendar] startOfDayForDate:dateDay];
// dateDayNext EITHER
dateDayNext = [dateDayStart dateByAddingTimeInterval:(24 * 60 * 60)];
// dateDayNext OR
NSDateComponents *dateComponentDay = nil;
dateComponentDay = [[NSDateComponents alloc] init];
[dateComponentDay setDay:1];
dateDayNext = [[NSCalendar currentCalendar] dateByAddingComponents:dateComponentDay
toDate:dateDayStart
options:NSCalendarMatchNextTime];
...and the NSPredicate for the Core Data NSFetchRequest (as already shown above in other answers)...
[NSPredicate predicateWithFormat:#"(dateAttribute >= %#) AND (dateAttribute < %#)", dateDayStart, dateDayNext]]
I query in between 1 second above and below my actual Date. In my case its ok to add and subtract 1 second as I query for the restaurant orders so I know that it takes some time to punch a new order, for safe side I have 1 more orderNo field too.
let fetchRequest: NSFetchRequest = Order.fetchRequest()
let date_formatter = DateFormatter()
date_formatter.dateFormat = "yyyy-MM-dd-HH-mm-ss"
date_formatter.timeZone = TimeZone(abbreviation: "UTC")
let createdDate = date_formatter.date(from: clientCreatedStr)
let calendar = NSCalendar.current
//as matching exact same datetime doesnt return anything
let onesecondafter = calendar.date(byAdding: .second, value: 1, to: createdDate!)
let onesecondbefore = calendar.date(byAdding: .second, value: -1, to: createdDate!)
let predicate = NSPredicate(format: "offlineUniqueId == %# AND (clientCreatedDate >= %# AND clientCreatedDate <= %#)", offlineUniqueId, onesecondbefore! as NSDate, onesecondafter! as NSDate)
fetchRequest.predicate = predicate
I am using the following NSCalendar method to get the number of days in a calendar year:
NSRange range = [gregorian rangeOfUnit:NSDayCalendarUnit
inUnit:NSYearCalendarUnit
forDate:date];
I am expecting a range.length return value of type NSRange which is 365 or 366 (days).
However, this code returns a range.length of 31 for a date value of 2005-06-06.
What is wrong with my code?
This is the full code snippet:
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
[subArray enumerateObjectsUsingBlock:
^(NSDate *date, NSUInteger idx, BOOL *stop) {
NSUInteger numberOfDays = [gregorian ordinalityOfUnit:NSDayCalendarUnit
inUnit:NSYearCalendarUnit forDate:date];
}];
This calculates the number of days of a year of a given date:
NSDate *someDate = [NSDate date];
NSDate *beginningOfYear;
NSTimeInterval lengthOfYear;
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian rangeOfUnit:NSYearCalendarUnit
startDate:&beginningOfYear
interval:&lengthOfYear
forDate:someDate];
NSDate *nextYear = [beginningOfYear dateByAddingTimeInterval:lengthOfYear];
NSInteger startDay = [gregorian ordinalityOfUnit:NSDayCalendarUnit
inUnit:NSEraCalendarUnit
forDate:beginningOfYear];
NSInteger endDay = [gregorian ordinalityOfUnit:NSDayCalendarUnit
inUnit:NSEraCalendarUnit
forDate:nextYear];
NSInteger daysInYear = endDay - startDay;
Sad caveat: This does not work correctly for year 1582.
The year 1582, the year when Gregor introduced the currently widespread used calendar, needed a fix to align solar with calendar years. So they went with the pragmatic solution: They just dropped October 5-14. (They were not crazy enough to change weekdays, too). As a result the year 1582 only has 355 days.
Addendum: The code above only works correctly for years after 1582. It returns 365 days for the year 1500, for example, even though this year was a leap year in the then used julian calendar. The gregorian calendar starts at October 15, 1582. Computations made on the gregorian calendar are just not defined before that date. So in this way Apple's implementation is correct. I'm not aware of a correct implementation for years before 1583 on Cocoa.
Here's a relatively clean version. It's a category on NSCalendar.
- (NSInteger) daysInYear:(NSInteger) year {
NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
dateComponents.year = year;
dateComponents.month = dateComponents.day = 1;
NSDate *firstOfYear = [self dateFromComponents:dateComponents];
dateComponents.year = year + 1;
NSDate *firstOfFollowingYear = [self dateFromComponents:dateComponents];
return [[self components:NSDayCalendarUnit
fromDate:firstOfYear
toDate:firstOfFollowingYear
options:0] day];
}
Thanks to AKV for pointing out that -components:fromDate:toDate:options will work in this case.
This doesn't seem to work for 1582 or prior, which per Nikolai Ruhe's answer is because Gregorian calendar calculations simply aren't defined prior to then.
What about finding number of days in a given year as: Although this is not the relative solution for the question.
-(NSInteger)daysBetweenTwoDates:(NSDate *)fromDateTime andDate:(NSDate *)toDateTime{
NSDate *fromDate;
NSDate *toDate;
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate interval:NULL forDate:fromDateTime];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate interval:NULL forDate:toDateTime];
NSDateComponents *difference = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
return [difference day];
}
-(void)someMethod {
NSString *yourYear=#"2012";
NSDate *date1 = [NSDate dateWithString:[NSString stringWithFormat:#"%#-01-01 00:00:00 +0000",yourYear]];
NSDate *date2 = [NSDate dateWithString:[NSString stringWithFormat:#"%#-12-31 00:00:00 +0000",yourYear]];
NSInteger numberOfDays=[self daysBetweenTwoDates:date1 andDate:date2];
NSLog(#"There are %ld days in between the two dates.", numberOfDays);
}
Edit:
As dateWithString: is deprecated, you can use
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter dateFormat]=#"dd-MM-yyyy";
NSDate *date=[dateFormatter dateFromString:dateString];
to form date1 and date2
You are getting the number of days in the month of your date.
You could use the same function and just pass in a date with February as the month. If range.length returns 28, the Total days in the year will be 365, if it returns 29, then the number of days will be 366.
I have a NSDate that I must compare with other two NSDate and I try with NSOrderAscending and NSOrderDescending but if my date is equal at other two dates?
Example: if I have a myDate = 24/05/2011 and other two that are one = 24/05/2011 and two 24/05/2011 what can I use?
According to Apple documentation of NSDate 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
The receiver is later in
time than anotherDate,
NSOrderedDescending
The receiver is
earlier in time than anotherDate,
NSOrderedAscending
In other words:
if ([date1 compare:date2] == NSOrderedSame) ...
Note that it might be easier in your particular case to read and write this :
if ([date2 isEqualToDate:date2]) ...
See Apple Documentation about this one.
After searching, I've got to conclusion that the best way of doing it is like this:
- (BOOL)isEndDateIsSmallerThanCurrent:(NSDate *)checkEndDate
{
NSDate* enddate = checkEndDate;
NSDate* currentdate = [NSDate date];
NSTimeInterval distanceBetweenDates = [enddate timeIntervalSinceDate:currentdate];
double secondsInMinute = 60;
NSInteger secondsBetweenDates = distanceBetweenDates / secondsInMinute;
if (secondsBetweenDates == 0)
return YES;
else if (secondsBetweenDates < 0)
return YES;
else
return NO;
}
You can change it to difference between hours also.
If you want to compare date with format of dd/MM/yyyy only, you need to add below lines between NSDate* currentdate = [NSDate date]; && NSTimeInterval distance
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd/MM/yyyy"];
[dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]
autorelease]];
NSString *stringDate = [dateFormatter stringFromDate:[NSDate date]];
currentdate = [dateFormatter dateFromString:stringDate];
I take it you are asking what the return value is in the comparison function.
If the dates are equal then returning NSOrderedSame
If ascending ( 2nd arg > 1st arg ) return NSOrderedAscending
If descending ( 2nd arg < 1st arg ) return NSOrderedDescending
I don't know exactly if you have asked this but if you only want to compare the date component of a NSDate you have to use NSCalendar and NSDateComponents to remove the time component.
Something like this should work as a category for NSDate:
- (NSComparisonResult)compareDateOnly:(NSDate *)otherDate {
NSUInteger dateFlags = NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit;
NSCalendar *gregorianCalendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDateComponents *selfComponents = [gregorianCalendar components:dateFlags fromDate:self];
NSDate *selfDateOnly = [gregorianCalendar dateFromComponents:selfComponents];
NSDateComponents *otherCompents = [gregorianCalendar components:dateFlags fromDate:otherDate];
NSDate *otherDateOnly = [gregorianCalendar dateFromComponents:otherCompents];
return [selfDateOnly compare:otherDateOnly];
}
NSDate actually represents a time interval in seconds since a reference date (1st Jan 2000 UTC I think). Internally, a double precision floating point number is used so two arbitrary dates are highly unlikely to compare equal even if they are on the same day. If you want to see if a particular date falls on a particular day, you probably need to use NSDateComponents. e.g.
NSDateComponents* dateComponents = [[NSDateComponents alloc] init];
[dateComponents setYear: 2011];
[dateComponents setMonth: 5];
[dateComponents setDay: 24];
/*
* Construct two dates that bracket the day you are checking.
* Use the user's current calendar. I think this takes care of things like daylight saving time.
*/
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDate* startOfDate = [calendar dateFromComponents: dateComponents];
NSDateComponents* oneDay = [[NSDateComponents alloc] init];
[oneDay setDay: 1];
NSDate* endOfDate = [calendar dateByAddingComponents: oneDay toDate: startOfDate options: 0];
/*
* Compare the date with the start of the day and the end of the day.
*/
NSComparisonResult startCompare = [startOfDate compare: myDate];
NSComparisonResult endCompare = [endOfDate compare: myDate];
if (startCompare != NSOrderedDescending && endCompare == NSOrderedDescending)
{
// we are on the right date
}
Check the following Function for date comparison first of all create two NSDate objects and pass to the function:
Add the bellow lines of code in viewDidload or according to your scenario.
-(void)testDateComaparFunc{
NSString *getTokon_Time1 = #"2016-05-31 03:19:05 +0000";
NSString *getTokon_Time2 = #"2016-05-31 03:18:05 +0000";
NSDateFormatter *dateFormatter=[NSDateFormatter new];
[dateFormatter setDateFormat:#"yyyy-MM-dd hh:mm:ss Z"];
NSDate *tokonExpireDate1=[dateFormatter dateFromString:getTokon_Time1];
NSDate *tokonExpireDate2=[dateFormatter dateFromString:getTokon_Time2];
BOOL isTokonValid = [self dateComparision:tokonExpireDate1 andDate2:tokonExpireDate2];}
here is the function
-(BOOL)dateComparision:(NSDate*)date1 andDate2:(NSDate*)date2{
BOOL isTokonValid;
if ([date1 compare:date2] == NSOrderedDescending) {
//"date1 is later than date2
isTokonValid = YES;
} else if ([date1 compare:date2] == NSOrderedAscending) {
//date1 is earlier than date2
isTokonValid = NO;
} else {
//dates are the same
isTokonValid = NO;
}
return isTokonValid;}
Simply change the date and test above function :)