Search code examples
objective-ciosnsdatenscalendarnstimezone

Storing an absolute NSDate, not relative to a timezone


I am creating an iOS app to track attendance. Each attendance entry is stored in an object which has a status attribute (e.g. present, absent) and an NSDate attribute called date which denotes the day which that attendance record was taken. When I select a particular date (using a UIDatePickerView or alike) I want all the attendance records (objects) for that date to appear in a table view.

While this sounds simple in principle, I am running into an issue relating to timezones. I am aware that NSDates are stored independent of timezones (i.e. they are stored relative to UTC/GMT +0000). This means that if I am in Sydney and take attendance on, for example, Sunday 4 November 2012 because the date is stored as timezone independent, if I take my iPhone/iPad to a different time zone (such as San Francisco) all the attendance records would shift one day back, in this case to Saturday 3 November 2012, because that was the moment in time when the attendance was taken in San Francisco local time (which was actually the next day, in Sydney local time).

I don't want this to happen - I want the date to be absolute. In other words, if the attendance is taken on Sunday 4 November 2012 then it needs to stay on that date, no matter where in the world (and whichever timezone) I may go. As you can see, this is quite in contrast to, say, a calendar application where it is desirable for the timing of appointments to change depending on the timezone.

Any suggestions on a better way to approach this problem would be appreciated. Please keep in mind that I am selecting the date to display using a UIDatePickerView which returns the current NSDate in the timezone independent format, so I also need a way to do an easy comparison (preferably in an NSPredicate since the attendance objects are stored in Core Data) to get all the attendance objects for that particular day.


Solution

  • Have you tried converting the time to it's NSDateComponents? you can then recreate an NSDate from it regardless of the time zone.

    Edited to add

    // This is just so I can create a date from a string.
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss z"];
    
    
    // Create a date as recorded in a timezone that isn't mine.
    NSDate *localDate = [formatter dateFromString:@"2012-10-30 10:30:00 +0200"];
    NSLog(@"Initial Date: %@", localDate);
    // this logs 2012-10-30 08:30:00 +0000
    // Which is what you would expect, as the original time was 2 hours ahead
    
    NSDateComponents *components = [[NSDateComponents alloc] init];
    NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    components = [gregorian components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit fromDate:localDate];
    
    NSLog(@"Components: %@", components);
    
    
    // Create a date from these time components in some other time zone
    [gregorian setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"EST"]];
    NSDate *newDate = [gregorian dateFromComponents:components];
    
    NSLog(@"New Date: %@", newDate);
    // This logs 2012-10-30 12:30:00 +0000
    // Which is the local EST of 8:30 am expressed in UTC
    

    Which demonstrates how I can turn make 8:30 am in +2 time zone look the same as for a -4 timezone.