Search code examples
iosnsdatenstimezone

Ignoring time-zone changes after saving NSDate


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.)


Solution

  • Here's the working solution (@Hot Licks deserves the credit, but he didn't post an answer):

    1. 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;
    }
    
    1. 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).

    2. 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.