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];
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 have this date object from Parse, "2015-07-24 20:36:38 +0000" and I would like to compare it to today's date to see how much time has passed since the date on the date object. Any ideas how to do this?
I have this method I use to get the hour and minute, but I'm stuck on how to use this to compare dates outside of the current day.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"hh:mm"];
NSString *formattedTime = [dateFormatter stringFromDate: date];
You can use an NSTimeInterval for this, the code is:
NSTimeInterval timeInterval = [date timeIntervalSinceNow];
with date being the date from parse as an NSDate, not NSString. and there should be no need to format it before comparing. Hope this helps
If you want this formatted as a string, in iOS 8 and later you can use NSDateComponentsFormatter:
NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
formatter.allowedUnits = NSCalendarUnitMinute | NSCalendarUnitHour;
NSString *string = [formatter stringFromDate:date toDate:[NSDate date]];
Here's the code:
NSDate *dateLocalNow = [self getLocalDate:[NSDate date]];
NSLog(#"%#",dateLocalNow);
NSDateComponents *dateToCheckAgainst = [[NSCalendar currentCalendar] components:NSMinuteCalendarUnit | NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSMinuteCalendarUnit fromDate: dateLocalNow];
NSLog(#"%#", dateToCheckAgainst);
getLocalDate gives me the date in the local timezone. If the NSLog for dateLocalNow outputs:
2014-10-29 01:01:55 +0000
Here's getLocalDate source:
-(NSDate *)getLocalDate:(NSDate *)date {
NSDate* sourceDate = date;
NSTimeZone* sourceTimeZone = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
NSTimeZone* destinationTimeZone = [NSTimeZone systemTimeZone];
NSInteger sourceGMTOffset = [sourceTimeZone secondsFromGMTForDate:sourceDate];
NSInteger destinationGMTOffset = [destinationTimeZone secondsFromGMTForDate:sourceDate];
NSTimeInterval interval = destinationGMTOffset - sourceGMTOffset;
NSDate* destinationDate = [[NSDate alloc] initWithTimeInterval:interval sinceDate:sourceDate];
return destinationDate;
}
Why does NSLog for dateToCheck against give me the following?:
Calendar Year: 2014
Month: 10
Leap month: no
Day: 28
Minute: 1
An NSDate is independent of time zone. Internally, it stores the number of seconds that have elapsed since a reference point in time. That reference point has many human-readable labels. Here are a few human-readable labels for Cocoa's standard reference date:
1/1/01, 12:00:00 AM GMT
12/31/00, 6:00:00 PM CST
1/1/01, 4:00:00 AM GMT+4
These labels all represent the same instant in time, but that instant can be labeled in many different ways. There is only one way to represent that instant as an NSDate.
You've made a new NSDate that is the original NSDate, adjusted by some time zone offsets, but the iOS SDK doesn't know or care. It considers your new NSDate to be an instant in time based on an offset from its standard reference date. That new instant is different from your original instant (unless your system time zone happens to be GMT). You shouldn't expect it to produce the same result when converted to a human-readable string or an NSDateComponents, unless you set the time zone on your NSDateFormatter or NSCalendar just right—which you didn't do in your posted code.
So what do you do about this? You don't try to create an NSDate that is offset from another NSDate based on time zone offsets. Instead, you specify the time zone when you are converting an NSDate to a human-readable string, by setting the timeZone property of your NSDateFormatter. Or you set the timeZone property of your NSCalendar when you ask it for the components of the date. If you're constructing a date from components, you can also set the timeZone of the NSDateComponents before using the calendar to convert the components to an NSDate.
Thus:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSDate *now = [NSDate date];
NSDateFormatter *f = [[NSDateFormatter alloc] init];
f.dateStyle = NSDateFormatterShortStyle;
f.timeStyle = NSDateFormatterLongStyle;
f.timeZone = [NSTimeZone systemTimeZone];
NSLog(#"from formatter = %#", [f stringFromDate:now]);
NSCalendar *calendar = [NSCalendar currentCalendar];
calendar.timeZone = [NSTimeZone systemTimeZone];
NSDateComponents *components = [calendar components:NSMinuteCalendarUnit | NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:now];
NSLog(#"components = %#", components);
}
return 0;
}
Output:
2014-10-29 00:24:19.762 commandline[28543:303] from formatter = 10/29/14, 12:24:19 AM CDT
2014-10-29 00:24:19.763 commandline[28543:303] components = <NSDateComponents: 0x1001027a0>
Calendar Year: 2014
Month: 10
Leap month: no
Day: 29
Minute: 24
Can you step back and tell us what you're trying to do? This routine feels like you're trying to take [NSDate date] and "convert" it to local time. But that's not how dates work in Cocoa. NSDate doesn't have a concept of timezone. Only date formatters (and calendars) do.
So, when you get [NSDate date], it retrieves the current time. Yes, if you NSLog it, it may show it to you in GMT, but if you use a NSDateFormatter with the default timezone or grab components from a NSCalendar, it will always be in the local timezone (unless you override it as something else), with no adjustment needed.
+0000 means it is the GMT reperesentation of the local date.Use NSDateFormatter to log the display date and it will give the components displayed
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.
We are building an app which requires the storage of a date for an entry on the device, the app will be international so we hit two dilema's / challenges.
User Preferences
If the user chooses the 12 hour rahter than 24 hour format we are returned from [NSDate date] a date like this 2012-07-17 11:26:03 AM which for sorting in a SQLite database is less than optimal as we cannot store it as a date.
User Locale
Typically this is ok however here in blighty we have a wonderfult thing called british summertime. which adds one hour every October 25th - 30th in a cycle and removes one hour every March 25 - 31th in a cycle so if no adjustment is made for 8 months of the year the time is one hour behind.
What I need to achieve is a consistent date formatted like this: 2012-07-17 11:26:03 no matter where the device is located and also taking into account where GMT+1 comes into place.
Any help would be awesome.
EDIT*
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"yyyy-MM-dd'T'HH:mm";
NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:#"GMT+01:00"];
[dateFormatter setTimeZone:gmt];
NSString *timeStamp = [dateFormatter stringFromDate:[NSDate date]];
NSDate *localDate = [dateFormatter dateFromString:timeStamp];
NSLocale* currentLocale = [NSLocale currentLocale];
NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
NSLog(#"Country Code: %#", countryCode);
NSLog(#"Current Loacle: %#", currentLocale);
if(([countryCode isEqualToString: #"GB"])){
NSDate *mydate = [NSDate date];
NSDate *fiddlyFoo = [mydate dateByAddingTimeInterval:3600];
NSLog(#"Print GMT +1 %#",fiddlyFoo);
} else {
NSLog(#"Print GMT Local %#",localDate);
}
I'm doing something like this now. Note that NSDate "knows" about the current timezone and daylight savings time etc. So you just need to get the GMT version of the time in a sortable representation. I'd suggest RFC 3339 but you can use variations on it per your needs:
This code:
NSCalendar *gregorian = [[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];
// Create a local date for London, for testing purposes
NSDateComponents *comps = [NSDateComponents new];
[comps setTimeZone:[NSTimeZone timeZoneWithName:#"Europe/London"]];
[comps setDay:1];
[comps setMonth:7];
[comps setYear:2012];
[comps setHour:14];
NSDate *date = [gregorian dateFromComponents:comps];
// Just want to show this date is 2PM in London July 1st
NSDateFormatter *curFormat = [NSDateFormatter new];
[curFormat setDateStyle:NSDateFormatterFullStyle];
[curFormat setTimeStyle:NSDateFormatterFullStyle];
NSLog(#"Reference date is good no: %#", [curFormat stringFromDate:date]);
// So now we get the date as a rfc3339 string, referenced to GMT
NSString *timeString;
{
NSDateFormatter *rfc3339 = [NSDateFormatter new];
[rfc3339 setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
rfc3339.timeZone = [NSTimeZone timeZoneWithName:#"UTC"];
timeString = [rfc3339 stringFromDate:date];
}
// referenced to UTC (sortable with any other time), can save in SQL DB etc
NSLog(#"Date as rfc3339 string: %#", timeString);
// Now lets convert it back into a BST time
NSDate *newDate;
{
NSDateFormatter *rfc3339 = [NSDateFormatter new];
[rfc3339 setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
rfc3339.timeZone = [NSTimeZone timeZoneWithName:#"UTC"];
newDate = [rfc3339 dateFromString:timeString];
// we want to show this as a string 2012-07-17 11:26:03
NSDateFormatter *newFormatter = [NSDateFormatter new];
[newFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"]; // local time
[newFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"Europe/London"]];
NSLog(#"Local string using 24 hour clock: %#", [newFormatter stringFromDate:newDate]);
}
Generates this output, which I believe is what you want:
TimeTester[58558:f803] Reference date is good no: Sunday, July 1, 2012 9:00:00 AM Eastern Daylight Time
TimeTester[58558:f803] Date as rfc3339 string: 2012-07-01T13:00:00Z
TimeTester[58558:f803] Local string using 24 hour clock: 2012-07-01 14:00:00