In iOS, Xcode9, and Obj-C, I am trying to find out how much time has elapsed in days between two dates. I know the NSTimeIntervalSince method yields a result in seconds. But it seems that when I calculate the seconds between consecutive days (like 11-2-2016 and 11-3-2016), I am 1 second short, yielding 86,399. If I calculate seconds between larger intervals, I am always a second short.
11-2-2016 to 11-4-2016: 172,799 (two 24-hour days but missing a second)
11-2-2016 to 11-5-2016: 259,199 (three 24-hour days but missing a second)
Here's my code:
#property (strong, nonatomic) NSDate *firstDate;
#property (strong, nonatomic) NSDate *secondDate;
#synthesize firstDate, secondDate;
int secondsIntervalFirstDatetoSecondDate = (fabs([firstDate timeIntervalSinceDate:secondDate]));
NSLog(#"seconds between firstDate and secondDate %i", secondsIntervalFirstDatetoSecondDate);
firstDate and secodDate are NSDate
They are entered by the user using a datePicker and date formatting is set as
[formatter setDateFormat:#"MM-dd-yyyy"];
I've searched for an answer but could find none.
First post for a newbie, sorry for any etiquette errors, I'll try to improve.
When you create a UIDatePicker and put it in "date" mode, the time is the time the date picker was created. If you're creating two pickers, they're going to be created at slightly different times. Even a very small difference in those times can lead to a 1-second difference.
You should never compute time-differences this way. There are not 86,400 seconds in a day. "How many days are between two points in time" is complicated, and you should use the tools designed for it: NSDateComponents and NSCalendar.
NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
NSInteger diffInDays = [[calendar components:NSCalendarUnitDay
fromDate:[calendar startOfDayForDate:date1]
toDate:[calendar startOfDayForDate:date2]
options:0] day];
Related
I have 2 UIDatePickers, one set in UIDatePickerModeDate and the other in UIDatePickerModeTime.
If the first one has its date set to today, then the second one must have a restriction such that the user cannot select a time that is before now. To do this I use the setMinimumDate function. However, if the user sets a date in the future, I need to reset the minimumDate property, meaning that there should be no minimumDate set on the picker.
I am able to achieve this by calling
[self.timePicker setMinimumDate:nil];
But this gives me an exception in the Xcode console output as below.
-[__NSCFCalendar components:fromDate:]: date cannot be nil
Future exception.
A few of these errors are going to be reported with this complaint, then further violations will simply be ignored.
How can I remove the minimumDate property of my timePicker after it has been set, without this exception being raised?
Thank you.
EDIT:
The reason I tried setting it to nil was because Apple's documentation says that the default value of the minimumDate property is nil (here).
Also, I did try to set the minimum date to the future date selected by the user, after making the time component zero. This causes the time to be selected as 12 AM in the picker which is acceptable. Now if the user selects today's date again, nothing happens when I set the minimumDate property, i.e. I cannot toggle the minimumDate using this. The code is as follows:
(void)setMinimumDateForTimePicker {
if([[NSCalendar currentCalendar] isDateInToday:self.datePicker.date]) {
NSLog(#"Min date for today");
[self.timePicker setMinimumDate:[self roundUpToNearestFifteenMinutes:[NSDate date]]];
} else {
// date is in the future so zero out time
NSLog(#"Min date for the future");
NSDateComponents* selectedDateWithZeroTime = [[NSCalendar currentCalendar] components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:self.datePicker.date];
[self.timePicker setMinimumDate:[[NSCalendar currentCalendar] dateFromComponents:selectedDateWithZeroTime]];
}
[self updateStartTimeField:nil];
}
This is the code that worked finally for what I was trying to do.
- (void)setMinimumDateForTimePicker {
if([[NSCalendar currentCalendar] isDateInToday:self.datePicker.date]) {
[self.timePicker setMinimumDate:[self roundUpToNearestFifteenMinutes:[NSDate date]]];
} else {
[self.timePicker setMinimumDate:[[NSCalendar currentCalendar] startOfDayForDate:[NSDate date]]];
}
[self updateStartTimeField:nil];
}
I have a text in either of below formats:
.03 hours
0.03 - 0.05 hours
In above cases accessibility should read in below format:
dot zero three hours
zero dot zero three (character name) zero dot zero five hours
But in real scenario it is reading like below:
three hours
zero dot zero three zero dot five hours
Like i mentioned above the number format is getting from json response as string type and it might be in any format like 0.03 hours, .03 hours, .03 - .05 hours, 4 - 5 hours
to fix the issue, I have tried many solutions like below but no luck
hoursLabel.accessibilityAttributedLabel = NSAttributedString(string: hoursLabel.text ?? "", attributes: [NSAttributedString.Key.accessibilitySpeechPunctuation: NSNumber(value: true)])
Can any one help me out, how to fix this scenario.
In may case it should work in all other units like .03 (unit)
the number format is getting from json response as string type and it might be in any format like 0.03 hours, .03 hours, .03 - .05 hours, 4 - 5 hours
First of all, you should rearrange the incoming data so as to have the same defined format. Then, you just have to set up this format to be read out as desired by VoiceOver.
Hereunder, an ObjC code snippet from this complete 'date, time and numbers' explanation:
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel * hourLabel;
#end
#implementation ViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSDateFormatter * dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"0.HH"];
NSDate * date = [dateFormatter dateFromString:#"0.05"];
_hourLabel.text = [NSDateFormatter localizedStringFromDate:date
dateStyle:NSDateFormatterNoStyle
timeStyle:NSDateFormatterShortStyle];
NSDateComponents * hourComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitHour
fromDate:date];
_hourLabel.accessibilityLabel = [NSDateComponentsFormatter localizedStringFromDateComponents:hourComponents
unitsStyle:NSDateComponentsFormatterUnitsStyleSpellOut];
}
#end
Unless I misunderstood your need, you should reach your purpose using this rationale.
I'm struggling to understand how to use NSDates properly.
I have an event that instantiates a timingDate = [NSDate date];
I then later on what to record the time intervals betweeen user's touches.
So I want to find the interval between the timingDate and the user touch in milliseconds.
Then I want to reset the timingDate to be equal to the touchTime so that the next time the screen is touched I can find the differnce between the previous touch and the present touch. I hope that makes sense. But I am going around in circles because I don't understand how to use NSDates or NSIntervals. The properties interval touchTime and timingDate are all currently NSDate types - Is this right?
So I've tried a lot of different things like
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
touchTime = timingDate;
interval = [[NSDate date] timeIntervalSinceDate:timingDate]; // should be the time difference from when the timingDate was first set and when the user touched the screen.
touchTime = [[[NSDate date]timeIntervalSinceDate:timingDate]doubleValue];
timingDate = [NSDate dateWithTimeIntervalSinceReferenceDate:touchTime];
NSLog(#"Time taken Later: %f", [[NSDate date]timeIntervalSinceDate:timingDate]);
}
Your code is a bit complex! You just need to calculate the difference between timingDate and the time that the touch occurred, and then set timingDate to the current time so that you can perform this calculation on every touch event.
To find the difference between timingDate and the first touch, you can use NSDate's timeIntervalSinceDate with the current time. This will return an NSTimeInterval value, which represents a time value in seconds with sub-millisecond precision. Here's an example:
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:timingDate];
NSLog(#"Time taken: %f seconds / %f milliseconds",timeInterval,timeInterval*1000);
Then, in order to set your timingDate to the current time, simply use timingDate = currentDate;. This will allow you to continuously measure the time difference between touches.
NSTimeInterval is a double which represents seconds.
NSDate is an object that holds the date/time.
Here is an example:
#property(nonatomic, strong) NSDate * lastTouchDate;
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSTimeInterval secondsSinceLastTouch = 0;
if(self.lastTouchDate){
secondsSinceLastTouch = [[NSDate date] timeIntervalSinceDate:self.lastTouchDate];
NSLog(#"It's been %.1f seconds since the user touched the screen", secondsSinceLastTouch);
}else{
NSLog(#"This is the first time the user touched the screen");
}
self.lastTouchDate = [NSDate date];
}
If you don't want the interval between the last time you touched it, do not update the self.lastTouchDate after initialization and it will be seconds since the date was initialized.
So, the first thing that you need to understand is that -[NSDate timeIntervalSinceDate:] returns an NSTimeInterval, which is really just a double value.
In your example above, you haven't declared your variables with types, but if you look at the value your variable interval it should be a decimal value of seconds since the time represented by timingDate.
Assuming that timingDate is an NSDate object, and it is set before this code is run, this code should print the time (seconds) to the debug console.
NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:timingDate];
NSLog(#"Time between touches %f", interval);
Here is the NSDate class documentation, in case you were having trouble finding it.
( https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSDate_Class/index.html#//apple_ref/doc/c_ref/NSDate )
I have rather unusual requirement, but anyway..
#property (weak, nonatomic) IBOutlet UIDatePicker *dateDatePicker;
When I get reading of dateDatePicker.date it returns NSDate set to current system's time zone. But I want it to be in different specific time zone.
So, pretend right now it's 3:50pm Central Time.
dateDatePicker.date returns 3:50pm CST
I want to have something like this:
[dateDatePicker setTimeZone:#"EST"]
NSDate *ESTDate = dateDatePicker.date;
... and then I'd like debugger to show "2:50pm CST" - because 3:50pm in EST IS 2:50CST
This may be simpler that you think. If your date is 3:50pm CST, and you want it to be 2:50pm CST, just subtract an hour:
date = [date dateByAddingTimeInterval:-3600];
Of course, the local time zone might not always be CST, so you can get the difference between the two time zones and use that instead of hard coding -3600:
NSString *targetTimeZoneName = #"US/Eastern";
NSInteger localOffset = [[NSTimeZone localTimeZone] secondsFromGMTForDate:date];
NSInteger targetOffset = [[NSTimeZone timeZoneWithName:targetTimeZoneName] secondsFromGMTForDate:date];
NSInteger timeZoneDelta = localOffset - targetOffset;
date = [date dateByAddingTimeInterval:timeZoneDelta];
UIDatePicker's timeZone property is not a string, as you are using it. You have to pass it an NSTimeZone object.
Also, NSDates do not have a timezone. Its just a moment in history and its string representation depends on the time zone. Use an NSDateFormatter to format the date object for the timezone you want.
Is it possible to have an action from a button a) do an NSDate store when tapped, and then b) for that method to do a date comparison with an if statement?
So, for instance, if the stored NSDate was less than 12 hours ago, the user could not press the button again?
Haven't come across a tip on this from my research.
Sure. You can get current date by code
[NSDate date]
Then save him into NSUserDefaults
And check a current date with saved date on button press.
How
if ( [[NSDate date] timeIntervalSinceDate: savedDate] >= 12*60*60) // 12 hours
{
// do your steps
}