So, I'm having a difficult time (no pun intended) finding if an NSDate is within a specific range. Consider this: a scheduled visit between 5:00 PM and 6:00 PM. I want to find if the scheduled visit is within +/- 15 minutes of the current time. Thus my safe range is 4:45 PM to 6:15 PM. How can I find out if the scheduled visit is in or out of the range? Here's the code I've got so far, which doesn't work at all...
- (BOOL)isInScheduledRange {
// NSLog(#"Start: %f", [self.start timeIntervalSinceNow]);
// NSLog(#"End: %f", [self.end timeIntervalSinceNow]);
//
// if (([self.start timeIntervalSinceNow] >= -900) && ([self.end timeIntervalSinceNow] <= 3600)) {
// NSLog(#"In Range");
//
// return YES;
// }
//
NSDate *now = [[NSDate alloc] init];
// if ((([self.start timeIntervalSinceDate:now] >= -900) && ([self.start timeIntervalSinceDate:now] <= 900)) && (([self.end timeIntervalSinceDate:now] <= -900) && ([self.end timeIntervalSinceDate:now] >= 900))) {
// NSLog(#"In Range");
// }
NSLog(#"%#; %#; %#", now, self.start, self.end);
// NSLog(#"%#; %#", [self.start timeIntervalSinceDate:now], [self.end timeIntervalSinceDate:now]);
if ((([self.start timeIntervalSinceDate:now] >= -900) && ([self.start timeIntervalSinceDate:now] <= 0)) && (([self.end timeIntervalSinceDate:now] >= 0) && ([self.end timeIntervalSinceDate:now] <= 900))) {
NSLog(#"In Range");
}
return NO;
[now release];
}
I'd appreciate some help. I'm having difficulties with this time calculation.
On a separate note, I hate dealing with time no matter what platform I'm working with...
You want to check if the start time is less than 900 seconds after the current time, and the end time is less than 900 seconds before the current time. timeIntervalSinceDate: returns a positive number if the object it is called on is after the argument, you need to check for start <= 900 and end >= −900. Also, you could simplify your code by using timeIntervalSinceNow instead of manually getting the current date and passing it to timeIntervalSinceDate:. Finally, if you can assume (or previously ensured) that the start is before the end, then you don't need the two middle tests, as they will both have to be true for both of the others to be true.
if([self.start timeIntervalSinceNow] <= 900 && [self.end timeIntervalSinceNow] >= −900) {
NSLog(#"In range")
}
Here's some verbose (and silly) code that explains my approach to this problem
NSTimeInterval secondsBeforeStart = [self.start timeIntervalSinceNow];
if ( secondsBeforeStart > (15 * 60))
{
// The result for '[self.start timeIntervalSinceNow]' is positive as long
// as 'self.start' remains in the future. We're not in range until there
// are 900 seconds to go or less.
NSLog( #"Still time to chill.");
// More than fifteen minutes to go so return NO.
return NO;
}
else
{
NSLog( #"OMG did I miss it!!!");
NSTimeInterval secondsBeforeEnd = [self.end timeIntervalSinceNow];
if ( secondsBeforeEnd < -( 15 * 60))
{
// The result for '[self.end timeIntervalSinceNow]' is negative when
// 'self.end' is in the past.
// It's been more than 900 seconds since the end of the appointment
// so we've missed it.
NSLog( #"Ahhhhh!!!");
// More than 900 seconds past the end of the event so return NO.
return NO;
}
else
{
// We are somewhere between 900 seconds before the start and
// 900 seconds before the end so we are golden.
NSLog( #"Whew, we made it.");
return YES;
}
}
The way I would have coded it would have been
BOOL inRange = NO; // Assume we are not in the proper time range.
if ( [self.start timeIntervalSinceNow] <= ( 15 * 60))
{
// 'Now' is at least 900 seconds before the start time but could be later.
if ( [self.end timeIntervalSinceNow] >= -( 15 * 60))
{
// 'Now' is not yet 900 seconds past the end time.
inRange = YES
}
}
return inRange;
Note: I haven't actually compiled this but I'm pretty sure the logic is correct.
Finally, you did notice that your last two lines
return NO;
[now release];
}
would have created a memory leak. (Release and then return ;^))
This is a distance problem - you want to know if the current time is within 900 seconds of the target time. Compute distance problems by taking the absolute value of the difference between the two points. By taking the absolute value of the difference, the order of the two samples doesn't matter.
/**
* #brief Return YES if the current time is within 900 seconds of the meeting time (NSDate* time).
*/
-(BOOL)currentTimeIsWithin900SecondsOf:(NSDate*)time
{
NSDate* now = [NSDate date];
return fabs( [now timeIntervalSinceReferenceDate] - [time timeIntervalSinceReferenceDate] ) < 900;
}
You need to create three NSDates. Then convert NSDates to time intervals using intervalSinceReferenceDate and compare those. You manually set up NSDates by using NSDateComponents.
Related
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I calculate the difference between two dates?
NSDate, comparing two dates
I have got two dates. How to check if theres a difference of 30 days in between them?
I actually have an In-App purchase which needs to be disabled after every 30-days of purchase.
When the user buys the feature, the Date is saved and so I need to check the dates. If 30-days have passed, I need to disable the feature again.
You can convert both dates to seconds with timeIntervalSince1970 and then check if difference is bigger than 2592000 (30*24*60*60 which is 30 days * 24 hours * 60 minutes * 60 seconds).
NSTimeInterval difference = [date1 timeIntervalSince1970] - [date2 timeIntervalSince1970];
if(difference >2592000)
{
//do your stuff here
}
EDIT:
For more compact version you can use - (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate
NSTimeInterval difference = [date1 timeIntervalSinceDate:date2];
if(difference >2592000)
{
//do your stuff here
}
Provide the start and end NSDate in following manner:
NSDate *date_Start;
NSDate *date_End;
NSCalendar *cal=[NSCalendar currentCalendar];
NSDateComponents *components=[cal components:NSDayCalendarUnit fromDate:date_Start toDate:date_End options:0];
int days=[components day];
if(days>30){
//Your code here
}
You could create a date which is +30days from now :
NSDate *thrityDaysPlus = [[NSDate date] dateByAddingTimeInterval:3600*24*30]
and then simply compare it to your saved date
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");
}
I am working on an iphone app for class and my group is having some time trouble.
The idea we are working with is that we have a scheduled event (say 2:00pm) and we want to know if we are within a certain amount of time from the scheduled time (say +/- 15 minutes). So for this example if the event was at 2pm, if this event was called from 1:45-2:15pm the method would return true/yes, and otherwise, it would return false/no.
I know how to current time/date, but how do I do the part with checking to see if it is within a certain range of time from the event time?
?
So what I am going for would be a method similar to the following with currentTime getting a NSDate object for the current time and classStartTime getting a NSDate object for the start time of the class:
- (BOOL)dateInRange:(NSDate*)currentTime inRangeOf:(NSDate*)classStartTime
{
if([currentTime <= [classStartTime dateByAddingTimeInterval:60*15]] && [currentTime >= [classStartTime dateByAddingTimeInterval:-(60*15)]])
{
return TRUE;
}
else{
return FALSE;
}
}
I know this doesn't work. How do I get the comparison part of the if statement to work properly
I guess, your time is a NSDate. You could use TimeIntervalSinceDate: with the start time of your interval and check if the result is positive and smaller than your interval length.
Use NSDate and NSDateComponents. I don't have a Mac nearby, so the following code will probably have errors in it.
NSCalendar *gregorian = [[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];
int comp = NSHourCalendarUnit | NSMinuteCalendarUnit;
NSDateComponents *theDateComponents = [gregorian components:comp fromDate:[NSDate date]];
int hour = [theDateComponents hour];
int minute = [theDateComponents minute];
if ((hour == 13 && minute >= 45) || (hour == 14 && minute <= 15)) {
//do your stuff
}
Hope it helps
Find a start date and and end date with
+ (id)dateWithTimeInterval:(NSTimeInterval)seconds sinceDate:(NSDate *)date
then use the compare method to check that the date you are checking falls within your dates...
Once a IBAction is pressed, I want a counter which counts up.
But in this form
00:00:00
Hours:minutes:seconds
This is the code so far:
-(void)countUp {
mainInt += 1;
seconds.text = [NSString stringWithFormat:#"%02d", mainInt];
}
This only counts up in 00 format
Thanks for the help!
Just do the appropriate math to break the count up into its constituent parts:
NSString *timeString = [NSString stringWithFormat:#"%02d:%02d:%02d",
totalSeconds/3600, // hours
(totalSeconds/60)%60, // minutes
totalSeconds%3600] // seconds
For the sake of readability, it'd be nice to replace that inline math with macros or functions, for example:
#define secondsPerMinute 60
#define minutesPerHour 60
int hours(int secs) {
return secs/(minutesPerHour * secondsPerMinute);
}
int minutes(int secs) {
return (secs/secondsPerMinute) % minutesPerHour;
}
int seconds(int secs) {
return secs % (minutesPerHour * secondsPerMinute);
}
// ...
NSString *timeString = [NSString stringWithFormat:#"%02d:%02d:%02d",
hours(totalSeconds),
minutes(totalSeconds),
seconds(totalSeconds)];
Often when implementing this sort of display, you don't want the colons to jump around as the elapsed time changes. Many fonts have fixed-width numerals, so it's not always a problem, but you might want to use three separate labels for the hours, minutes, and seconds, with unchanging labels in between for the colons.
Another approach to the math above is to store the seconds, minutes, and hours in three variables instead of one, and just be careful to increment minutes and reset seconds when seconds hit 60, and so on. To make this easier to use, encapsulate it in a class, like:
#interface Time : NSObject {
int seconds;
int minutes;
int hours;
}
- (void)countUp;
- (NSString*)timeString;
#end;
I must be missing something small her but can't figure it out. Trying to create a date for comparison, but I can't seem to offset currentDate from GMT to EST:
// current date (gmt) //
NSDate *currentDate = [NSDate date];
NSTimeZone *currentDateTimeZone = [NSTimeZone timeZoneWithAbbreviation:#"EST"];
NSDateFormatter *currentDateFormat = [[NSDateFormatter alloc]init];
[currentDateFormat setTimeZone:currentDateTimeZone];
[currentDateFormat setDateFormat:#"yyyy-MM-dd HH:mm:ss zzz"];
NSString *currentDateString = [currentDateFormat stringFromDate:currentDate];
NSLog(#"currentDateString: %#", currentDateString); // returns 2011-01-05 13:30:30 EST
NSDate *currentDateWithOffset = [currentDateFormat dateFromString:currentDateString];
NSLog(#"currentDateWithOffset: %#", currentDateWithOffset); // returns 2011-01-05 18:30:30 +0000
Thanks!
Edit:
I'm calling a method in a separate class (trying to make this portable) using the following line:
[Expiration expires:[[NSDate alloc] initWithString:#"2011-01-07 12:00:00 +0000"] within:1.0]
in the expires method, I have these lines:
NSComparisonResult comparison = [currentDateWithOffset compare:expires]; // check for a fixed date to disable the demo
double withinRange = [installDate timeIntervalSinceDate:currentDateWithOffset]; // check for number of seconds between "within" and the install date
I'm then comparing these two values like so:
if(withinRange >= within && withinRange > 0.0) {
// app is expired //
}
else {
// app is still enabled (so far...) //
if(comparison == NSOrderedDescending || comparison == NSOrderedSame) {
// app is expired //
}
else {
// app is still enabled //
}
}
Does this help? Thanks for your patience!
Edit:
Here's the entire expires:within method as it currently stands...
+(BOOL)expire:(NSDate*)expires within:(double)within {
// default expired value //
BOOL expired = NO;
// convert within value from days to seconds //
within *= 24.0 * 60.0 * 60.0;
// current date (gmt) //
NSDate *currentDate = [NSDate date];
// install date //
NSDate *installDate = [[NSUserDefaults standardUserDefaults]objectForKey:#"installDate"];
// check for a value in installDate //
if (nil == installDate) {
// app is running for the first time //
[[NSUserDefaults standardUserDefaults]setObject:currentDate forKey:#"installDate"];
[[NSUserDefaults standardUserDefaults]synchronize];
installDate = currentDate;
}
if([installDate timeIntervalSinceNow] < (-within)) {
expired = YES;
}
else {
if([expires timeIntervalSinceNow] < 0) {
expired = YES;
}
}
NSLog(#"installDate:%#", installDate);
NSLog(#"expires:%#", expires);
NSLog(#"currentDate:%#", currentDate);
return expired;
}
I'm then calling it from another class with
message.text = (YES == [Expiration expire:[[NSDate alloc] initWithString:#"2011-01-07 12:00:00 -0500"] within:(0.015625/2)]) ? #"This App is Expired" : #"This App is Active";
When running in the simulator (fresh app install), NSLog displayed this...
[Session started at 2011-01-06 10:43:46 -0500.]
2011-01-06 10:43:48.146 TimeBasedDemo[14717:207] installDate:2011-01-06 15:43:48 +0000
2011-01-06 10:43:48.147 TimeBasedDemo[14717:207] expires:2011-01-07 17:00:00 +0000
2011-01-06 10:43:48.147 TimeBasedDemo[14717:207] currentDate:2011-01-06 15:43:48 +0000
None of these answers gave me an NSDate object with the current, local date. So here's how I did it:
NSDate *currentDateWithOffset = [NSDate dateWithTimeIntervalSinceNow:[[NSTimeZone localTimeZone] secondsFromGMT]];
An NSDate object represents an instant in time irrespective of time zone and calendar considerations. Time zone info is relevant when you print or parse a date, but it is not stored within the NSDate.
Say you are creating your expiration date like this:
NSDate *exp=[[NSDate alloc] initWithString:#"2011-01-07 12:00:00 +0000"]
That says you want the expiration to occur at noon GMT, on the 7th Jan. If you want it to expire at noon EST, create it with -0500 instead. What you should not have to do is mess with the current time when you do a comparison.
An easy way just to see if the time has passed is then
if ([exp timeIntervalSinceNow]<0) { /* it's expired */ }
and you can see if within seconds have passed since the install date like this:
if ([installDate timeIntervalSinceNow]<(-within)]) { /* it's expired */}
In Cocoa, NSDate is an abstract representation of a date with no time zone information applied. Note that currentDateWithOffset is the same date as the date string, just in a different time zone (five hours ahead). This is expected behavior, as NSDate does not persist the time zone used to create it.
I tinkered around a bit more and found a way to 'cheat' the offset to suit my needs. From other reading, I'm guessing that NSCalendar might be a better long term-solution, but for now I ended up changing
NSDate *currentDateWithOffset = [currentDateFormat dateFromString:currentDateString];
to
NSDate *currentDateWithOffset = [[NSDate alloc] initWithString:[NSString stringWithFormat:#"%# +0000", currentDateString]];
That got me the results I needed and works in the later comparisons I'm using. Thanks for the background info all!
i need a little help i have a method which gets value such as 50, it then assigns that value to trackDuration, so NSNumber *trackDuration = 50, i want the method to every second minus 1 from the value of trackDuration and update a label, the label being called duration.
Here's what i have so far;
- (void) countDown {
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
NSNumber *trackDuration = [NSNumber numberWithDouble:[[iTunes currentTrack] duration]];
while (trackDuration > 0) {
trackDuration - 1;
int inputSeconds = [trackDuration intValue];
int hours = inputSeconds / 3600;
int minutes = ( inputSeconds - hours * 3600 ) / 60;
int seconds = inputSeconds - hours * 3600 - minutes * 60;
NSString *trackDurationString = [NSString stringWithFormat:#"%.2d:%.2d:%.2d", hours, minutes, seconds];
[duration setStringValue:trackDurationString];
sleep(1);
}}
Any help would be much appreciated, thanks in advanced, Sami.
This will block the main thread, and you are not assigning the value trackDuration, so it will always stay 50
trackDuration -1;
Should be:
trackDuration--; // or trackDuration -= 1;
Also I would do it like this:
- (void)startCountDown
{
[NSTimer scheduledTimerWithInterval:1.0f target:self selector:#selector(timerHit:) userInfo:nil repeats:YES];
}
- (void)timerHit:(NSTimer *)p_timer
{
if( trackDuration <= 1 && [p_timer isValid] )
[p_timer invalidate];
// track duration is an instance variable
trackDuration--;
// update LABEL
}
iOS 2.x or higher is required for NSTimer
Having this method run in a loop will make your app go unresponsive for the whole time it's running — you won't even see any UI updates. Instead, you should use an NSTimer with an interval of one second, and update the elapsed time when the timer fires.