3

If I try and set the time zone arbitrarily from anywhere within the app using the following :

[[NSCalendar currentCalendar] setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"EST"]];
NSTimeZone *tz = [[NSCalendar currentCalendar] timeZone];
NSLog(@"%@", tz);

the result of the log statement is :

America/Los_Angeles (PDT) offset -25200 (Daylight)

(which is my local timezone, i.e. [NSTimeZone systemTimeZone])

However, functionally similar code within a category on NSCalendar works fine :

[self setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"EST"]];
NSTimeZone *tz = [self timeZone];
NSLog(@"%@", tz);

and the log yields :

America/New_York (EDT) offset -14400 (Daylight)

What is going with setting the time zone for [NSCalendar currentCalendar]? The behavior is counterintuitive.

Michael
  • 3,269
  • 2
  • 23
  • 16

1 Answers1

7

Simple answer: +[NSCalendar currentCalendar] isn't returning the same instance. It is supposed to return you an instance that reflects the currently selected locale and time zone at the time that +currentCalendar is called. There are three possible reasonable behaviours:

  • It returns a new value every time.
  • It returns a cached value unless it detects that it ought to return a new value.
  • It returns a cached value, clearing it when it notices a locale/calendar change.

Modifying the calendar is only sensible in the first case. (In the second case, previous calls to +currentCalendar will point to the same instance. In the third case, all calls to +currentCalendar will return the same instance until the user changes locale/time zone.)

The correct way to set the app's time zone is +[NSTimeZone setDefaultTimeZone:].

tc.
  • 33,468
  • 5
  • 78
  • 96
  • Ok, that makes sense. It seemed like that value was being lost, because I assumed the behavior was like a singleton. Instead, the method is returning a new calendar instance each time. I could try referencing a distinct [NSCalendar currentCalendar] instance and log the time zone to verify that it works, instead of calling for a new instance. I could take the same approach if I wanted to retain that calendar for any reason. – Michael Oct 30 '12 at 18:25
  • 1
    The answer depends on what you ultimately want to achieve by "setting the time zone". If you want to set an app-wide user-facing time zone, use `+[NSTimeZone setDefaultTimeZone:]` as I've already mentioned (it may also suffice for *basic* unit test needs); but this may confuse the user! However, an app-wide time zone is often not appropriate. iOS also supports Japanese and Buddhist year numbering; if you need to format/parse ISO 8601 dates (e.g. an iCalendar parser), use `[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]` and then set its time zone. – tc. Oct 30 '12 at 19:26