My apps are time-zone agnostic: I want the dates and times saved and displayed in a manner that ignores changes in the user's time zone. Once I've recorded the local date/time of some user action in my app (typically by saving timeIntervalSince1970 in an sqlite DB), I want to show the same date/time string regardless if the user's time zone has changed or not, and regardless of the user's various time zone settings in his device.
For example, if a user's action occurs at 1:15 pm local time in San Francisco, and then he opens my app a few days later in New York, I want that prior action to still appear as 1:15 pm (not 4:15 pm), and even if he's manually set some other time zone in his device (e.g., Chicago). A second action in New York at 9:00 pm there, should forevermore display as 9:00 pm, even when back in California.
I could achieve my goal by saving every date/time as a string (based on the user's local time zone), and then never use the date methods again -- but then I couldn't do date/time arithmetic (e.g., sorting).
How can I be time-zone agnostic when saving and retrieving dates/times?
(Apple's Calendar app seems to behave this way when Time Zone Override is on, but only if I manually set the event time.)
Here's the working solution (#Hot Licks deserves the credit, but he didn't post an answer):
Subclass NSDate and add a gmtDate method that does the conversion from local TZ to GMT. gmtDate uses #"yyyy-MM-dd HH:mm" as the format string, which also drops seconds from the value. Like this:
+(NSDate *)gmtDate
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"yyyy-MM-dd HH:mm"; // drops the seconds
dateFormatter.timeZone = [NSTimeZone systemTimeZone]; // the local TZ
NSString *localTimeStamp = [dateFormatter stringFromDate:[NSDate date]];
// localTimeStamp is the current clock time in the current TZ
// adjust date so it'll be the same clock time in GMT
dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
NSDate *gmtDate = [dateFormatter dateFromString:localTimeStamp];
return gmtDate;
}
In didFinishLaunchingWithOptions: add [NSTimeZone setDefaultTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"GMT"]] so all date formatters make date strings in GMT by default (but never include the timezone in the format string).
When the date/time of a user action needs to be saved, get the date via [NSDate gmtDate] and save that value in your database.
Record the time zone along with the timestamp. Use that time zone when formatting the timestamp later. You'll need to add another column to your database table.
Either save the time zone, as Rob suggested, or save the time as both an NSDate and a string. Use the string for the UI and the NSDate for calculations.
Or hey, do all three, if you need them.
Related
I have a UIDatePicker and a button. When I press the button I want to display in the log the message "Setting a reminder for x" where x is the time displayed by the date picker.
Everytime I press the button, the time logged is precisely 3 hours behind the time displayed by the picker (the time displayed by the picker is my current time). I suspect it has something to do with the time zone. I live in GMT +2 time zone (I guess it's +3 since we are in daylight saving time).
Do you have any idea how I could make the time logged to be the same as the time displayed?
Below is the method that gets executed when I press the button. Thanks.
- (IBAction)addReminder:(id)sender {
[self.datePicker setTimeZone:[NSTimeZone systemTimeZone]];
NSLog(#"Setting a reminder for %#", self.datePicker.date);
}
NSDate is simple representation of some moment in time, it knows nothing about your local time. When you log it (actually, when description) message is sent, it uses GMT to display itself.
UIDatePicker uses your timezone by default (see https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIDatePicker_Class/#//apple_ref/occ/instp/UIDatePicker/timeZone).
So, here is how it works:
You choose some time in DatePicker, but time is meaningless unless you specify timezone.
DatePicker uses your local (GMT+2) timezone and creates NSDate for it.
You display NSDate and since it knows nothing about timezone, it simply displays itself as GMT.
To display your date, your should use NSDateFormatter. It has Timezone property: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDateFormatter_Class/#//apple_ref/occ/instp/NSDateFormatter/timeZone
You can also use NSCalendar and its components.
You should read this guide: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/DatesAndTimes/DatesAndTimes.html since dates, formatters and calendars are not very clear in Cocoa unless you understand its concepts.
Try this...
NSDate *now = [[NSDate alloc] init];
[self.datePicker setDate:now];
[self.picker setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:2*60*60]]; // GMT+2 Your timeZone
NSLog(#"Setting a reminder for %#", self.datePicker.date);
I am new to IOS development. I am receiving a date string from an API and would like to set the time to the user's current time zone. The dateformatter looks like:
[dateFormatter setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'"];
I know I can set the timezone like so
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
But is there a way to set it depending on the user's current time zone?
So there is a section on web application that users can enter events into and the web service sends those events to the mobile app in the following format:
"yyyy-MM-dd'T'HH:mm:ssZZZZZ"
I'm having issues trying to convert the string into a date so I can get just the time from the event (formatted in the correct timezone as well), So for example here's one that comes over "2015-03-20T20:00:00-07:00", which when I pull the time should be 1PM Pacific Time. But instead I either get 8PM or 3AM (depending on whether I add UTC abbreviation to the date formatter).
Here's what I have so far, I know I'm missing something here & maybe there's another date formatter that needs to be used but so far I can't figure out where I'm going wrong.
NSString *datePattern = #"yyyy-MM-dd'T'HH:mm:ssZZZZZ";
NSDateFormatter *dateFormatter = [NSDateFormatter new];
[dateFormatter setDateFormat:datePattern];
NSString *sString = [valueDict valueForKey:#"start_date"];
NSDate *startDate = [dateFormatter dateFromString:sString];
NSDateFormatter *timeFormatter = [NSDateFormatter new];
[timeFormatter setDateFormat:#"hh:mm a"];
[timeFormatter setLocale:[NSLocale systemLocale]];
NSString *timeString = [timeFormatter stringFromDate:startDate];
2015-03-20T20:00:00-07:00 is 8pm Pacific Daylight Time.
If you're representing 1pm PDT, that's either
2015-03-20T13:00:00-07:00
or represent that in "Zulu" (i.e. GMT/UTC)
2015-03-20T20:00:00Z
When working with a web service, the latter is the common convention for ISO 8601 dates. Then, when you present it to the user, you present it to them in their local timezone (using a NSDateFormatter with its default timeZone setting.
Note, when using NSDateFormatter to prepare ISO 8601 dates, you will want to ensure that you specify a locale of en_US_POSIX as outlined in Technical Q&A QA1480. When designing app for US audience this isn't critical, but it's best practice in case the user is not using a gregorian calendar on their device.
I know that NSDate doesn't have timezone information.
However, I'm trying to understand how to manipulate them properly.
At the moment I'm passing a date into an object. That date is the user selected date at time 00:00:00.
i.e. if the user hits October 21st then the NSDate passed in should be. 21/10/2013 00:00:00.
However, it isn't it's 20/10/2013 23:00:00. (One hour before).
Now, this is nothing about formatting them or displaying them. I'm just using the NSDates.
I'm creating the date using NSDateComponents and NSCalendar.
I guess my question is...
How can I tell what date an NSDate is actually referring to in my local time zone?
I need to send a UNIX time stamp for 00:00:00 and 23:59:59 for a given date. However, at the moment when I set the hour, minute and second to 0, 0 and 0 then I'm not getting midnight in the current time zone I'm getting midnight in GMT.
This isn't what I want.
Fixed?
OK, I've fixed it... I think. At least, it's doing what I want it to do.
The trick is...
NSTimeZone *timeZone = [NSTimeZone localTimeZone];
[dateComponents setSecond:timeZone.secondsFromGMT];
I've been confused by this many times. When you NSLog an NSDate, you'll always get the output in GMT. So the 20/10/2013 23:00:00 (GMT) you're seeing is the same as your expected 21/10/2013 00:00:00 (BST). The UNIX timestamp for both of these dates would be the same because it doesn't take into account timezone - it's always UTC.
If you want to output in a user-readable format, an NSDateFormatter will format the date using your current timezone and locale.
When I am printing the date
//getting the current date and time
self.date = [NSDate date];
NSLog(#"%#",date);
The date which I am getting is correct, but there is a delay in time by 6 hrs. My system time is correct.
try this
NSLocale* currentLoc = [NSLocale currentLocale];
NSLog(#"%#",[[NSDate date] descriptionWithLocale:currentLoc]);
Make use of NSDateFormatter
NSDate *today = [NSDate date];
//Create the dateformatter object
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
//Set the required date format
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
//Get the string date
NSString *dateString = [dateFormatter stringFromDate:today];
//Display on the console
NSLog(dateString);
Logging an NSDate in the debugger is somewhat misleading as it gives you a calendar day and time for a particular time zone - UTC / GMT. However, NSDate has no inherent time zone or any inherent relationship to how humans perceive and think about dates at all. Instead, it is a timestamp. Classes like NSDateComponents, NSTimeZone, NSDateFormatter, and so on all exist to provide human context and formatting.
So what you see is the timestamp formatted to that particular format and UTC time zone, which is how NSDate will always appear when printed in the debugger or the console. If you were to calculate the time zone offset between UTC and your own time zone, you'd find that the date represents the time stamp you gave it, and not one however many hours off.
you can set current time zone for customizing your date format.
This link can help:
https://stackoverflow.com/a/7213629/456471
The default date string representation is probably formatting the date as UTC, rather than your local time zone (the exact format that it will use is not defined, and may change from release to release, so you shouldn't rely on it). You should use the NSDateFormatter class if you need to format a date in a particular format (or with a particular time zone, including the local time zone); see the Data Formatting Guide and the NSDateFormatter Class Reference for more information.