How can I save a duration in a plist so that I can load up some sample data in Core Data?
By duration I mean a task has a time duration. Could be 1h12m. Could be 15m.
Using this category
+(NSDate *)dateWithHour:(NSInteger)hour minute:(NSInteger)minute
{
NSDateComponents *components = [[NSDateComponents alloc]init];
components.hour = hour;
components.minute = minute;
NSCalendar *calender = [NSCalendar currentCalendar];
NSDate *date = [calender dateFromComponents:components];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"HH:mm"];
NSLog(#"**Date Utils** %#",[dateFormatter stringFromDate:date]);
return date;
}
I can create a duration of 15minutes
task.duration = [NSDate dateWithHour:0 minute:15];
It outputs to the console as:
0001-01-01 04:29:24 +0000
Which doesn't appear to be 15minutes. It looks like 4h29m24s. If I run the date back through a dateFormatter sure enough it prints out 15m.
How can I input a time duration in the plist as shown below?
What am I missing?
Dates are terribly complicated because of time zones and leap years and leap seconds and so on. They're not suitable for this use case and you will have all kinds of bugs trying to use them.
The correct data type for durations is NSTimeInterval, which is a 64 bit floating point number, in seconds. NSDate uses this data type internally as well.
The easiest way to create a time interval is:
NSDate *aDate = ...
NSDate *anotherDate = ...
NSTimeInterval duration = [aDate timeIntervalSinceDate:anotherDate];
And you'd save it to a plist with NSNumber:
NSNumber *durationNumber = [NSNumber numberWithDouble:duration];
Note that NSTimeInterval is actually a double.
Related
Background: I have dates stored in files.
What I would like to do: I would like to take the difference between two dates in seconds. I can't find any way to do it. My date format looks like that:
2015-23-02-12-23-43
Try this out:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-mm-dd-hh-mm-ss"];
NSDate *date = [dateFormatter dateFromString:string1];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:date];
NSInteger hour = [components hour];
NSInteger minute = [components minute];
Try this:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *date = [dateFormatter dateFromString:EnterYourStringHere];
NSString *str = [dateFormatter stringFromDate:date];
If all you need is seconds then use
NSTimeInterval dateInterval = [date timeIntervalSince1970];
Then
NSString secondsString = [NSString stringWithFormat: #"%.0f", dateInterval];
EDIT:
If you have a file full of date strings that look like your example, 2015-23-02-12-23, then you could use code like this:
NSDateFormatter *myFormatter = [[NSDateFormatter alloc] init];
[myFormatter setDateFormat:#"yyyy-mm-dd-hh-mm-ss"];
//an example - you'll read from your file
NSString *aDateString = #"2015-23-02-12-23";
NSDate aDate = [myFormatter dateFromString: aDateString];
NSTimeInterval dateSeconds = [aDate timeIntervalSince1970];
That will give you dateSeconds as a double precision floating point number, which is the norm for numeric date calculations since it deals with fractions of seconds. You can then do numeric comparisons of date's time intervals.
Note that most UNIX systems use the numeric values returned by the timeIntervalSince1970 method, but Mac and iOS uses numeric date values returned by the method timeIntervalSinceReferenceDate. The two methods have different "epoch dates", or dates where they start counting from zero.
timeIntervalSince1970 uses midnight on Jan 1, 1970 (GMT)
timeIntervalSinceReferenceDate uses midnight on Jan 1, 2001 (GMT)
Which you use is up to you, just be sure to use the same method in all cases.
By the way, your date string format is horrible. having all those 2 digit numbers separated by dashes doesn't give the reader any way to tell apart month/day/hours/minutes/seconds. It's all a jumble. You'd be much better off using a standard date format.
In the US it's common to display dates in the form mm/dd/yyyyy (or yyyy/mm/dd, or even yyyy/dd/mm), and times as hh:mm:ss, so in "mm/dd/yyyyy hh:mm:ss" format you'd get:"09/02/2015 13:09:39" (I'm using human-readable date format strings for discussion, not those intended to set up a date formatter.)
I am making an app to use at school and I want to make a countdown timer to countdown the amount of time between now and the end of school, which for me is 3:00. For example, at 11:15, it will read 3:45.
So far, I have figured out how to make the countdown timer and I have the following code: countdownTimer.text = [Formatter stringFromDate: [NSDate date]];
This code doesn't actually work yet, but I think it will work if I figure out how to subtract the current time from a set time and then use that value where date is, however I am open to other suggestions on how to approach the problem.
You can use NSCalendar method dateBySettingHour:minute:second: to get the NSDate object associated with 3pm today:
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDate *schoolOut = [calendar dateBySettingHour:15 minute:0 second:0 ofDate:now options:0];
There are lots of different ways to get the NSDate object associated with 3pm today, but the above is just one example. You could also use components:fromDate of NSCalendar to extract the NSDateComponents of now, then adjust the hour, minute and second and then create a new NSDate object using dateFromComponents (also a NSCalendar method).
Anyway, once you have a NSDate object that represents your target date/time, you can then use NSDateComponentsFormatter to display the time interval between two dates in a nice format.
NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
formatter.allowedUnits = NSCalendarUnitHour | NSCalendarUnitMinute;
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull;
formatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad;
NSString *string = [formatter stringFromDate:now toDate:schoolOut];
You can adjust the unitsStyle and zeroFormattingBehavior to adjust the format of the string.
In my iPhone / iPad app I am showing a UIDatePicker for time. It will display time in this format, 11:00 AM. When User clicks on the time row we expand the time row to display this datePicker row.
I have a time stamp in string "starts": "11:00", // time of day in ISO-8601 format
I need to show this on the picker wheel as selected time when it gets opened up. For this, first of all I get the date at 12 AM using https://stackoverflow.com/a/9040554/4082792. Then I convert the given time (11:00) to number of seconds and add it to the midnight time to get the current time. This time is in local timezone (as I specify the timezone while using NSDateFormatter). When I try to setDate to UIDatePicker with this date, It gives me incorrect time, even though the time is correct in the NSDate variable. For 11:00 AM, it gives me 6:40 while the local time is 4:30.
So, I have two questions :
1) Why is the time wrong on wheel.
2) How can I convert the NSDate from one timezone to another, I need to show it in the local time format.
Snippet :
NSString *strDate = #"11:00";
NSDate *date = [NSDate date];
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
NSUInteger preservedComponents = (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit);
date = [calendar dateFromComponents:[calendar components:preservedComponents fromDate:date]];
///Start time
NSString *startTime = #"11:00";
NSArray *startTimeSeparatedByColon = [startTime componentsSeparatedByString:#":"]; /// From 22:10 to [22, 10];
NSInteger hourPartOfStart = startTimeSeparatedByColon[0] ? [startTimeSeparatedByColon[0] integerValue] : 0;
NSInteger minutePartOfStart = startTimeSeparatedByColon[1] ? [startTimeSeparatedByColon[1] integerValue] : 0;
NSTimeInterval totalTime = (hourPartOfStart*60*60+minutePartOfStart*60);
NSDate *finalDate = [NSDate dateWithTimeInterval:totalTime sinceDate:date];
NSDate *dt = [NSDate dateWithTimeInterval:[NSTimeZone localTimeZone].secondsFromGMT sinceDate:finalDate];
self.datePicker.date = dt;
By default, iOS converts date into the device's time zone.
But if you want to convert date into another time zone, here is the code for that:
NSTimeZone *currentDateTimeZone = [NSTimeZone timeZoneWithAbbreviation:#"EST"];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeZone:currentDateTimeZone];
You can get the date in "EST" time zone from this dateFormatter object.
Convert "EDT" TimeZone
NSString *format = #"EEEE dd-MMMM-yyyy HH:mm:ss z";
NSDateFormatter *date_EDTDateFormate = [[NSDateFormatter alloc] init];
date_EDTDateFormate setDateFormat:format];
date_EDTDateFormate setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"EDT"]];
NSString *stringEDT = [dateFormatter_EDT stringFromDate:date_System];
I'm looking to compare NSDate objects based on the day only (ignoring time). Instead of converting the time to 0:00:00, or using NSDateComponent like most solutions (ex. Comparing two NSDates and ignoring the time component)
Does anyone see an issue with converting the date to an int representing the number of days since 1970 with the timeIntervalSince1970 method?
return (int)([date timeIntervalSince1970]/(SECONDS_PER_DAY));
Yes, absolutely. There are an endless number of pitfalls with date math. Use NSDateComponents; they’re not hard.
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *date1Components = [cal components:NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date1];
NSDateComponents *date2Components = [cal components:NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date2];
NSComparisonResult comparison = [[cal dateFromComponents:date1Components] compare:[cal dateFromComponents:date2Components];
Here's a way to convert an NSDate to an NSTimeInterval that represents midnight of the original date without using NSCalendar. Doing this with two NSDate objects would let you compare the two dates without regard to time.
NSDate *now = [NSDate date]; // Your original date with time
NSTimeInterval interval = [now timeIntervalSince1970]; // the full interval
NSDateFormatter *form = [[NSDateFormatter alloc] init];
[form setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; // Zulu time
[form setDateFormat:#"A"]; // milliseconds since midnight
NSString *secondsStr = [form stringFromDate:now];
NSTimeInterval seconds = [secondsStr integerValue] / 1000.0; // seconds since midnight
NSTimeInterval justDate = interval - seconds; // interval for date at midnight (Zulu time)
// For testing purposes
NSDate *nowDate = [NSDate dateWithTimeIntervalSince1970:justDate];
NSLog(#"now = %#, interval = %f", now, interval);
NSLog(#"seconds = %#", secondsStr);
NSLog(#"justDate = %f, nowDate = %#", justDate, nowDate);
This may or may not be better than using NSCalendar as shown in Noah's answer.
You must definitely not simply divide by SECONDS_PER_DAY. That will simply be wrong.
NSCalendar.currentCalendar().compareDate(date1, toDate: date2, toUnitGranularity: .Day)
Returns NSComparisonResult
Here is what apple talks about it
friends,I am getting a date based on the calculation I have done below
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *expectedDate = [gregorian dateByAddingComponents:components toDate:startDate options:0];
NSTimeInterval timeZoneOffset = -[[NSTimeZone systemTimeZone] secondsFromGMTForDate:expectedDate];
NSDate *localDate = [expectedDate dateByAddingTimeInterval:(timeZoneOffset)];
NSString *date = [dateFormatter stringFromDate:localDate];
But the date goes wrong when the daylightsaving is in effect,and also the timeZoneOffset changes when the daylightsaving is in effect, but I want the same date irrespective of whether the daylight saving is in effect or no..
So friends,how shall I handle this situation,please help.
Regards
Ranjit
You don't need to take care of daylight saving time yourself, the "dateFormatter" does that automatically for you. Usually you only need a NSDate object in UTC (GMT+0) time and "dateFormatter", which also has a time zone, will display that time in its own time zone.
NSCalendar and NSDateFormatter have time zone settings. NSDate is just a point in time relative to GMT+0.
Example:
"expectedDate" is January 1st 4am (GMT+0)
"dateFormatter" has time zone GMT+2 (e.g. Europe/Berlin) set, then it will output "January 1st 6am" because of its own time zone when converting "expectedDate" into a string.
So basically you just need to ensure that "startDate" is correct and that "gregorian" and "dateFormatter" use the correct time zone. By default they use the system time zone, which seems to be the one you want to use. So you need only these lines (and startDate has to be correct):
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *expectedDate = [gregorian dateByAddingComponents:components toDate:startDate options:0];
NSString *date = [dateFormatter stringFromDate:expectedDate];
If that doesn't work, please post more code about how startDate and dateFormatter are generated.
James, try this
//To Fix DaylightSaving, 1 hr added to startDate.
NSTimeInterval secondsInOneHour = 1 * 60 * 60;
NSDate *expectedDST = [startDate dateByAddingTimeInterval:secondsInOneHour];
NSDate *expectedDate = [gregorian dateByAddingComponents:components toDate:expectedDST options:0];
NSTimeInterval timeZoneOffset = -[[NSTimeZone systemTimeZone] secondsFromGMTForDate:expectedDate];
NSDate *localDate = [expectedDate dateByAddingTimeInterval:timeZoneOffset];