iPhone4 iOS5 [[NSTimeZone localTimeZone] daylightSavingTimeOffset] wrong for daylight savings time?

2.4k Views Asked by At

I live in the US eastern time zone (EST), and a couple weeks ago the daylight savings began. I was testing some of my earlier code, which was working(supposedly) fine and noticed that the day second calculations are running 1 hour earlier.

here's the code that I use for testing:

 int  gmtOffset =  [[NSTimeZone localTimeZone] secondsFromGMT];
 int daylightOffset =  [[NSTimeZone localTimeZone] daylightSavingTimeOffset];

  int daySeconds =  ((int)([date timeIntervalSinceReferenceDate]+gmtOffset+daylightOffset))%(86400+1);

 NSLog(@"Day seconds: %i", daySeconds);

The result is 3600 seconds less than the expected value. The daylightOffset is 0. How can I correctly get the daylight offset for the timezone that the user is in? the NSDateFormatter parses the date correctly, so it must know something that NSTimeZone is not telling me.

Thank you!

3

There are 3 best solutions below

0
On

If you want to determine the number of seconds since midnight, you can use date components. This code adjusts appropriately for daylight savings.

NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[calendar setTimeZone:[NSTimeZone localTimeZone]];

NSDateComponents *comps = [calendar components:NSHourCalendarUnit|NSMinuteCalendarUnit| NSSecondCalendarUnit
                                      fromDate:[NSDate date]];
NSInteger time = [comps hour] * 3600 + [comps minute] * 60 + [comps second];

NSLog(@"Day seconds: %i", time);
0
On

The [localTime secondsFromGMT] return the true GMT including the daylight saving time, you can get the offset itself by [localTime daylightSavingTimeOffset]

0
On

This seems to be a recurring problem for many (me included) and after having pulled my hair way too long I have finally solved the issue.

The assumptions this code relies on is that:

  1. you are interested in dates, not in time
  2. you want your date calculations to be oblivious to any Daylight Saving Time
  3. you want an easy enough string representation of the date such that you can persist it.

In order to achive 1. I use unique NSCalendar instance that is initialized to Gregorian, GMT This also serves 2.

For 3. the NSDateFormatter is initialized with both the unique calendar and its time zone. Setting the timezone of the formatter to that of the calendar otherwise used for calculation was the key to all the issues I had!

This should always be true (assume the code below is part of a DateModel class):

NSDate * today = [DateModel normalize: [NSDate date]] ;
NSString*todayS= [DateModel dateToString: today] ;
BOOL alwaysTrue=[[DateModel normalize: [NSDate date]] isEqual: [DateModel stringToDate: todayS]] ; 

Here's the code:

+ (NSDate *) normalize: (NSDate *) date {
    NSDateComponents * comps = [[DateModel calendar] components:NSYearCalendarUnit
                                                            |   NSMonthCalendarUnit
                                                            |   NSDayCalendarUnit
                                                            |   NSHourCalendarUnit
                                                            |   NSMinuteCalendarUnit
                                                            |   NSSecondCalendarUnit
                                                       fromDate:date] ;

    [comps setHour:0] ;
    [comps setMinute:0] ;
    [comps setSecond:0] ;

    NSDate * dateOnly = [[DateModel calendar] dateFromComponents:comps] ;

    return dateOnly ;
}

+ (NSCalendar *) calendar {
    static NSCalendar * calendar = nil ;
    if (calendar == nil) {
        calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] ;
        calendar.timeZone = [NSTimeZone timeZoneWithAbbreviation: @"GMT"] ;
    }
    return calendar ;
}

+ (NSDateFormatter *) dateFormatter {
    static NSDateFormatter * formatter = nil ;
    if (formatter == nil) {
        formatter = [[NSDateFormatter alloc] init] ;
        [formatter setCalendar:[self calendar]] ;          // <- This is
        [formatter setTimeZone:[self calendar].timeZone] ; // <- critical
        [formatter setDateFormat:@"yyyyMMdd"] ;
    }
    return formatter ;
}

+ (NSDate *) dateFromString: (NSString *) stringDate {
    return [DateModel normalize: [[DateModel dateFormatter] dateFromString:stringDate]] ;
}

+ (NSString *) stringFromDate: (NSDate *) date {
    return [[DateModel dateFormatter] stringFromDate:[DateModel normalize: date]] ;
}

Hope this helps someone!