iPhone - Differences among time zone convenience methods

The language in the docs is a bit on the dry side, to be sure, and the similarity of the names is potentially confusing. I'll quote the NSTimeZone docs here and try to explain them:

systemTimeZone
The time zone currently used by the system. If the current time zone cannot be determined, returns the GMT time zone.

This is the time zone which the device believes it is in; it is often set automatically, and would then correspond to the device's physical location, but if the user has explicitly set a particular time zone in the Settings App, that's what you'll get.

defaultTimeZone
The default time zone for the current application. If no default time zone has been set, this method invokes systemTimeZone and returns the system time zone.

Your application is allowed to set its own time zone, so that you can perform actions as if the device were in another zone, but without affecting the system time zone (and thereby other apps). The setting is performed with a call to setDefaultTimeZone:. If you haven't done that, this call is identical to calling systemTimeZone.

localTimeZone
An object that forwards all messages to the default time zone for the current application. The local time zone represents the current state of the default time zone at all times.

This is where it gets a little bit tricky. localTimeZone gives you nearly the same result as defaultTimeZone. The difference is that the specific NSTimeZone instance you get from localTimeZone will always reflect the setting you've made to the time zone within your app. You can call it once, save the result, and always get the current simulated time zone through that object, no matter the changes made. It is as if, when you use this NSTimeZone instance, the framework is calling defaultTimeZone for you, to be sure that you always get the current value.

Here's a couple of brief illustrations of the above. The NSTimeZone object that you get back from systemTimeZone represents the system time zone at the time you make the call. If you call systemTimeZone again, even if the user has since changed the time zone, you will get the same one. Your app caches that value, and you have to ask the system to clear it with resetSystemTimeZone to get the update.

// Say that device is in GMT originally
NSLog(@"%@", [NSTimeZone systemTimeZone]);    // GMT
// User flies into Rome and iPhone changes the zone automatically
NSLog(@"%@", [NSTimeZone systemTimeZone]);    // Still GMT
[NSTimeZone resetSystemTimeZone];    // Clear app's cache
NSLog(@"%@", [NSTimeZone systemTimeZone]);    // Now GMT+2

A similar thing happens with defaultTimeZone. When you call that method, you get an object that will always represent the same time zone, even if you later call setDefaultTimeZone:. However, if you use the object you get from localTimeZone, it will follow the change you make to the default time zone*.

// Say that defaultTimeZone is originally GMT
NSTimeZone * myDefaultTZ = [NSTimeZone defaultTimeZone];
NSTimeZone * myLocalTZ = [NSTimeZone localTimeZone];
[NSTimeZone setDefaultTimeZone:[NSTimeZone timeZoneWithName:@"Etc/GMT-4"]];
NSLog(@"%@", myDefaultTZ);    // Still gives GMT
NSLog(@"%@", [NSTimeZone defaultTimeZone]);    // GMT-4, the new value
NSLog(@"%@", myLocalTZ);    // Also the new value!

Apple seems to recommend using localTimeZone:

with the localTimeZone class method, you can get a relative time zone object that decodes itself to become the default time zone on any computer on which it finds itself.


*Note that localTimeZone is still subject to the app-level cache of the system time zone. It only changes to follow your setting of the default time zone.