what I am trying to do is make a if statement with dates using greater than less than signs. For some reason only the greater than sign works. Here is my code:
NSDate *currDate = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"HHmm"];
NSString *dateString = [dateFormatter stringFromDate:currDate];
NSLog(#"%#",dateString);
if (dateString < #"0810" && dateString > #"0800") {
NSLog(#"Homeroom");
}
else {
NSLog(#"no");
}
The output for this code would be if the time was 8:03:
2013-04-08 08:03:47.956 Schedule2.0[13200:c07] 0803
2013-04-08 08:03:47.957 Schedule2.0[13200:c07] no
If I were to make is so where it is only the greater then sign like this:
if (dateString > #"0800") {
NSLog(#"Homeroom");
}
else {
NSLog(#"no");
}
The output would be this:
2013-04-08 08:03:29.748 Schedule2.0[14994:c07] 0803
2013-04-08 08:03:29.749 Schedule2.0[14994:c07] Homeroom
create a NSDate object with the time 8:10 and one with 8:00. Now you can compare the given date with both these dates
if(([date0800 compare:date] == NSOrderingAscending) && [date0810 compare:date] == NSOrderingDescending) )
{
// date is between the other
}
to create the boundaries dates you can do this
NSDate *date = [NSDate date]; // now
NSDateComponents *components = [[NSCalendar currentCalendar] components:( NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit ) fromDate:date];
components.hour = 8;
components.minute = 0;
NSDate *date0800 = [[NSCalendar currentCalendar] dateFromComponents: components];
components.minute = 10;
NSDate *date0810 = [[NSCalendar currentCalendar] dateFromComponents: components];
if you insist of using operators like < and >, you can use the timeinterval of the date objects.
if(([date0800 timeIntervalSince1970] < [date timeIntervalSince1970]) && ([date0810 timeIntervalSince1970] > [date timeIntervalSince1970]))
{
// date lays between the other two
}
but beware of checking == on it, as it could be faulty due to rounding errors.
Here you are comparing string objects, with < and >, which does not do what you are expecting. You can use NSDateComponents to get the hour and minute to compare those:
NSDate *today = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components =
[gregorian components:(NSHourCalendarUnit | NSMinuteCalendarUnit ) fromDate:today];
NSInteger hour = [weekdayComponents hour];
NSInteger minutes = [weekdayComponents minute];
BOOL homeroom = (hour == 8) && (minute < 10);
Or you can create a specific NSDate for 8:10 and 8:00 using NSDateFormater and using the compare: function.
NSString objects are objects, and when you compare objects with C comparison operators (==, >, <, etc.) you are comparing their addresses, not their values. You need to use compare, such as:
if ([dateString compare:#"0810"] == NSOrderedAscending &&
[dateString compare:#"0800"] == NSOrderedDescending) { ...
Though I'd recommend converting to NSDate objects in most cases if you want to compare dates and times.
You can't use > or < to compare string objects. That actually compares pointers so we won't get into why > 'works' and < 'doesn't'.
For this kind of date comparison use NSDateComponents
NSDateComponents Reference
Here's the gist of a category I wrote on NSDate. I found it made my code more readable.
https://gist.github.com/nall/5341477
#interface NSDate(SZRelationalOperators)
-(BOOL)isLessThan:(NSDate*)theDate;
-(BOOL)isLessThanOrEqualTo:(NSDate*)theDate;
-(BOOL)isGreaterThan:(NSDate*)theDate;
-(BOOL)isGreaterThanOrEqualTo:(NSDate*)theDate;
#end
Related
I am trying to create a catch on an iOS app to keep people from accessing things outside of a small window.
Basically, I need the action to only fire if it is between 12-1:30PM on Sundays in London (BST Time Zone). How would I check the current time, convert it to that time zone, and then see if it matches up?
I have tried the following, but it always shows it is between that range:
- (NSDate *)dateByNeutralizingDateComponentsOfDate:(NSDate *)originalDate {
NSCalendar *gregorian = [[[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
// Get the components for this date
NSDateComponents *components = [gregorian components: (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate: originalDate];
// Set the year, month and day to some values (the values are arbitrary)
[components setYear:2000];
[components setMonth:1];
[components setDay:1];
return [gregorian dateFromComponents:components];
}
- (BOOL)isTimeOfDate:(NSDate *)targetDate betweenStartDate:(NSDate *)startDate andEndDate:(NSDate *)endDate {
if (!targetDate || !startDate || !endDate) {
return NO;
}
// Make sure all the dates have the same date component.
NSDate *newStartDate = [self dateByNeutralizingDateComponentsOfDate:startDate];
NSDate *newEndDate = [self dateByNeutralizingDateComponentsOfDate:endDate];
NSDate *newTargetDate = [self dateByNeutralizingDateComponentsOfDate:targetDate];
// Compare the target with the start and end dates
NSComparisonResult compareTargetToStart = [newTargetDate compare:newStartDate];
NSComparisonResult compareTargetToEnd = [newTargetDate compare:newEndDate];
return (compareTargetToStart == NSOrderedDescending && compareTargetToEnd == NSOrderedAscending);
}
-(void)checkDate {
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateFormat:#"EEEE HH:mm"];
NSTimeZone *timeZone = [NSTimeZone timeZoneWithAbbreviation:#"BST"];
[dateFormatter setTimeZone:timeZone];
NSDate *openingDate = [dateFormatter dateFromString:#"Sunday 12:00"];
NSDate *closingDate = [dateFormatter dateFromString:#"Sunday 1:30"];
NSDate *targetDate = [NSDate date];
if ([self isTimeOfDate:targetDate betweenStartDate:openingDate andEndDate:closingDate]) {
NSLog(#"TARGET IS INSIDE!");
}else {
NSLog(#"TARGET IS NOT INSIDE!");
}
}
I assume you want to use the current time as observed in England, rather than BST specifically, as BST is British Summer Time. My understanding is that, during the winter, they use UTC (formerly known as GMT). Thus we should specify the time zone in a way that will select the proper offset from UTC based on the time of year.
static BOOL dateIsAcceptable(NSDate *date) {
NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
calendar.timeZone = [NSTimeZone timeZoneWithName:#"Europe/London"];
NSDateComponents *components = [calendar
components:NSCalendarUnitWeekday | NSCalendarUnitHour | NSCalendarUnitMinute
fromDate:date];
if (components.weekday != 1) {
return NO;
}
double hour = components.hour + components.minute / 60.0;
return hour >= 12 && hour < 13.5;
}
Im trying to check if the time of an NSDate has passed. Ive used the following but that obviously has the year so therefore it has passed. How would I check just the time of the NSDate object, only Hour and minutes are important:
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"HH:mm:ss"];
[dateFormat setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"]];
NSDate *date = [dateFormat dateFromString:openingDay.endTime];
if ([date timeIntervalSinceNow] < 0.0) {
}
But the date is:
2000-01-01 5:30:00 p.m. +0000
The time string has the format 'HH:mm:ss'
I tried looking for the answer as I thought it would be common but no luck.
Edit:
I should have elaborated some more, I get a time from a server which is a string. I want to then check if this time is past. The string is an end time of a shops opening hours, I therefore want to check if the shop has closed.
Edit2:
the string I get from the Json object is end_time:17:30:00. I then want to check if the current time is after this time, if so, then show a closed UIAlertView. My approach was to take this string and turn it into a date object and compare it to the current time. However when I convert it to an NSDate object the year is 2000, which is obviously in the past. I hope I have provided enough.
To get the hour and minutes component, you do this:
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:date];
NSInteger hour = [components hour];
NSInteger minute = [components minute];
You can then create helper methods to keep the code organized:
- (NSInteger)hourFromDate:(NSDate *)date {
return [[NSCalendar currentCalendar] component:(NSCalendarUnitHour) fromDate:date];
}
- (NSInteger)minuteFromDate:(NSDate *)date {
return [[NSCalendar currentCalendar] component:(NSCalendarUnitMinute) fromDate:date];
}
note that component:fromDate:returns directly that component (as NSInteger), since it can only take one component type per parameter, while components:fromDate: returns a NSDateComponents, which then you can grab multiple components.
and then just:
NSDate *endDate = [dateFormat dateFromString:openingDay.endTime];
NSDate *today = [NSDate date];
NSInteger endDateHour = [self hourFromDate:endDate];
NSInteger endDateMinute = [self minuteFromDate:endDate];
NSInteger todayHour = [self hourFromDate:date];
NSInteger todayMinute = [self minuteFromDate:date];
BOOL hasEndMinutePassed = endDateMinute > todayMinute;
BOOL hasEndHourPassed = endDateHour > todayHour;
if ((hasEndHourPassed) || (endDateHour == todayHour && hasEndMinutePassed)) {
//Yep, it passed
} else {
//Nope, it didn't
}
I wrote it like this to keep things organized.
You could also write a category:
Header file:
#interface NSDate (Components)
- (NSInteger)hour;
- (NSInteger)minutes;
#end
Implementation file:
#import "NSDate+Components.h"
#implementation NSDate (Components)
- (NSInteger)hour {
return [[NSCalendar currentCalendar] component:NSCalendarUnitHour fromDate:self];
}
- (NSInteger)minute {
return [[NSCalendar currentCalendar] component:NSCalendarUnitMinute fromDate:self];
}
You can go a bit further than that and add the comparation logic inside the Category itself:
Adding this to the header:
- (BOOL)hourAndMinutesPassedFromDate:(NSDate *)date;
and then the implementation:
- (BOOL)hourAndMinutesPassedFromDate:(NSDate *)date {
BOOL hasEndMinutePassed = [self minute] > [date minute];
BOOL hasEndHourPassed = [self hour] > [date hour];
return ((hasEndHourPassed) || ([self hour] == [date hour] && hasEndMinutePassed));
}
Thats it. I didn't test the logic itself (but it should be accurate, i used something like this before), and of course you are free to modify this to fit your needs.
Before giving downvote, comment the reason
I have created a UIDatePicker with minimum & maximum date values (i.e. 6 months of date picker) Now, i need to get 7 days from selected date. There i need to check the conditions,
If date is today date i need to get 7 days from today onwards
If date is last date (i.e. last date of picker) need to get last 7 days including last day
If date is middle of today's date & last date i need to get last 3 days, next 3 days including today date. And, also while getting last & next 3 days it shouldn't get exceed with picker's date limit.
Here's my code snippet:
- (void)addDays:(NSInteger)range {
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MM-yyyy"];
NSDate *startDate = self.selectedDate;
for (int x = 0; x <= range; x++) {
NSLog(#"%#", [dateFormat stringFromDate:startDate]);
startDate = [startDate dateByAddingTimeInterval:(60 * 60 * 24)];
}
}
- (void)minusDays:(NSInteger)range {
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MM-yyyy"];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *comps = [NSDateComponents new];
for (NSInteger i=0; i<range; i++) {
comps.day += -1;
NSDate *date = [calendar dateByAddingComponents:comps toDate:self.selectedDate options:0];
NSDateComponents *components = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:date];
NSLog(#"%#", [dateFormat stringFromDate:[calendar dateFromComponents:components]]);
}
}
- (void)calculateDateRange {
if ([dateArray count] > 0) {
[dateArray removeAllObjects];
}
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *lastcomponents = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:[NSDate date]];
lastcomponents.month += 6;
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:#"yyyy/MM/dd"];
NSDate *currentDate = [NSDate date];
NSDate *selectedD = self.selectedDate;
NSDate *endDate = [calendar dateFromComponents:lastcomponents];
NSDate *fromDate;
NSDate *toDate;
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate interval:NULL forDate:currentDate];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate interval:NULL forDate:selectedD];
NSDateComponents *difference = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
NSInteger first = [difference day];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate interval:NULL forDate:selectedD];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate interval:NULL forDate:endDate];
NSDateComponents *difference2 = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
NSInteger second = [difference2 day];
if ((first == 0 || first < 3) && second > 7) {
[self addDays:7];
} else if (first >= 3 && second > 7) {
[self minusDays:3];
[self addDays:3];
}else if (second == 7 || second < 7) {
[self minusDays:7];
}
}
This is working fine. But, can't get exact last & previous days.
Anyone has idea on this?
My interpretation of your needs
You have a date picker. When a date is picked you need to create a 7 day range around that selected date.
So, if the selected date is 15/11/2014 then you want 3 days either side so...
12/11/2014 - 18/11/2014.
However, the date range cannot exceed the limits of the date picker. So if the minimum date on the date picker is set to 14/11/2014 then (in the above example) the date range would be...
14/11/2014 - 21/11/2014
Even after your additional explanation this is still my interpretation. And my code does exactly this.
Solution
You CANNOT use 60*60*24 to mean one day. This is just wrong. When dealing with dates you should always be using NSDateComponents and NSCalendar.
Also, break down your problem into small steps. There is no reason to do everything in one giant function.
OK I guess you have a datePicker action somewhere so I'd code it like this...
- (void)datePickerDateChanged
{
NSDate *minimumDate = self.datePicker.minimumDate;
NSDate *maximumDate = self.datePicker.maximumDate;
NSDate *selectedDate = self.datePicker.date;
NSDate *startDate;
NSDate *endDate;
if ([self numberOfDaysFromDate:minimumDate toDate:selectedDate] < 3) {
// get 7 days after minimumDate
startDate = minimumDate;
endDate = [self dateByAddingDays:6 toDate:minimumDate];
} else if ([self numberOfDaysFromDate:selectedDate toDate:maximumDate] < 3) {
// get 7 days before maximumDate
startDate = [self dateByAddingDays:-6 toDate:maximumDate];
endDate = maximumDate;
} else {
// get 3 days before and 3 days after selectedDate
startDate = [self dateByAddingDays:-3 toDate:selectedDate];
endDate = [self dateByAddingDays:3 toDate:selectedDate];
}
// Here startDate and endDate define your date range.
}
- (NSDate *)dateByAddingDays:(NSInteger)days toDate:(NSDate *)date
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [NSDateComponents new];
components.day = days;
return [calendar dateByAddingComponents:components toDate:date options:0];
}
- (NSInteger)numberOfDaysFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
// always return positive. We just want magnitude of days.
return components.day > 0 ? components.day : -components.day;
}
This is untested and just a first attempt.
I have a UIViewController with 2 buttons: today and yesterday. Every time the client clicked on today it needs to set the property _date for today and the same with yesterday.
The method which I set the property value is the following:
-(IBAction)dateButtonTouched:(id)sender
{
if(![sender isSelected])
{
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:[[NSDate alloc] init]];
[components setMinute:-[components minute]];
[components setSecond:-[components second]];
if([sender tag] == YESTERDAY)
{
[components setHour:-24];
_date= [cal dateByAddingComponents:components toDate: [NSDate date] options:0]; //YESTERDAY
}
else
{
NSDate *today = [NSDate date];
[components setHour:0];
_date= [cal dateByAddingComponents:components toDate: today options:0]; //TODAY
}
NSLog(#"user selected date %#",_date);
}
}
in the following method I'm trying to check if _date is today or yesterday:
-(void) viewWillLayoutSubviews
{
NSDateFormatter *dateFormat = [NSDateFormatter new];
[dateFormat setDateFormat:#"MM-dd-YY"];
self.dateLabel.text = [dateFormat stringFromDate:_date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:[[NSDate alloc] init]];
[components setMinute:-[components minute]];
[components setSecond:-[components second]];
[components setHour:0];
NSDate *today = [cal dateByAddingComponents:components toDate: [NSDate date] options:0]; //TODAY
[components setHour:-24];
NSDate *yesterday = [cal dateByAddingComponents:components toDate: [NSDate date] options:0]; //YESTERDAY
if([_date isEqualToDate:yesterday])
{
//do something
}
else if( [_date isEqualToDate:today])
{
//do something
}
}
The _date is never today or yesterday although if I debug it, print it or compare the description is the same.
An NSDate instance represents a specific moment in time. It is much more specific than "yesterday" or "today". Instead of trying to reset the hours, minutes, and seconds via date components, Maybe you should compare the components you're interested in: day, month, and year. Or you can use a calendar to compare at the component level:
NSDate *today = [NSDate date];
NSDate *someDay; // the date to compare
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *difference = [cal components:NSDayCalendarUnit fromDate:today toDate:someDay options:nil];
if (difference.day == -1) {
// yesterday
} else if (difference.day == 0) {
// today
}
Without knowing exactly what you want to do, it's harder to get more specific.
An NSDate object contains both date and time, encoded as a value that is essentially the number of microseconds since Jan 1 2000.
When you do two successive [NSDate date] operations you will get two different values, if not because the time has actually changed that much between the two then because most computer clock implementations assure that you get distinct values with each access.
NSDate's isEqualToDate function compares all of the bits of the two NSDate objects to see if they are exactly equal. This means that if the two are even a microsecond apart they will not appear to be equal.
If you want to see if two NSDate values are the same day you should either use NSDateFormatter to format them into strings and compare the strings, or use one of the NSDateComponent functions to calculate the number of days between them (several ways to do this).
Hello I have seen many NSDate comparisons on this site
but they all seems to be very complicated , since I need only to know
if the date now is past 4pm or before 4pm
maybe there is some easy way to achieve this goal ?
[link] (Check if the time and date is between a particular date and time)
but it seems very long and complicated I just need simple bool answer past 4pm or not
- (BOOL)checkTime
{
NSDate* now = [NSDate date];
NSDate *endDate;
[formatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
endDate = [formatter dateFromString:#"2012-12-07 16:00:00"];//well here I have a problem how do I set end day to today 4pm ?
NSLog(#"%d",[now compare:endDate]);
if([now compare:endDate] < 0)
return YES;
else if([now compare:endDate] > 0)
return NO;
else
return NO;
}
EDIT: After the answers I came up with this code
- (BOOL)checkTime
{
NSDate *date = [NSDate date];
NSDateFormatter *df = [NSDateFormatter new];
[df setDateFormat:#"HH"];
int intS =9;
NSInteger HourStart = intS;
NSInteger hour = [[df stringFromDate:date] integerValue];
int hourE = 16;
NSInteger HourEnd = hourE;
if((hour >HourStart) && (hour < HourEnd))
return YES;
else
return NO;
}
For now it is works fine , but I am not sure it will work on another calendars set etc.
In general, I'd prefer to use NSDateComponents and NSCalendar for these sorts of calendrical calculations, since you don't know what calendar the user is using.
Here's a method for doing the comparison in a category on NSDate using date components:
#import <Foundation/Foundation.h>
#interface NSDate (Foo)
- (BOOL)isAfterFourPM;
#end
#implementation NSDate (Foo)
- (BOOL)isAfterFourPM {
unsigned int flags = NSHourCalendarUnit;
NSDateComponents *comps = [[NSCalendar currentCalendar] components:flags fromDate:self];
return (comps.hour >= 16);
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
NSDate *now = [NSDate date];
NSLog(#"is after 4 PM? - %#",([now isAfterFourPM]?#"YES":#"NO"));
// let's try with a different time (17h)
unsigned int flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
flags |= NSHourCalendarUnit;
NSDateComponents *currentComps = [[NSCalendar currentCalendar] components:flags fromDate:now];
currentComps.hour = 17;
NSDate *afterFourDate = [[NSCalendar currentCalendar] dateFromComponents:currentComps];
NSLog(#"is after 4 PM? - %#",([afterFourDate isAfterFourPM]?#"YES":#"NO"));
}
}
This prints:
2013-12-10 05:16:05.921 Untitled 2[43453:507] is after 4 PM? - NO
2013-12-10 05:16:05.921 Untitled 2[43453:507] is after 4 PM? - YES
(at ~ 5 AM Central time)
In this case, all you need to do, is get the NSHourCalendarUnit component from the date using the current calendar and compare the hour property on NSDateComponents to 16.
The following will give you hour:
NSDate *now = [NSDate date];
NSDateFormatter *df = [NSDateFormatter new];
[df setDateFormat:#"hh"];
NSInteger hour = [[df stringFromDate:now] integerValue];
NOTE:
If you want to check only for 4 then use hh, if for 16 then use HH.
Or in simple words HH for 24 hour format.