We're currently looking into moving our app to iOS 11 when we discovered a portion of our legacy code used to convert UTC date times to localized date time strings is returning differing values between iOS 10 and iOS 11. When running a fresh install of our app on simulators (after resetting content and settings) we see the following output:
10.3.1:
(lldb) po [NSTimeZone localTimeZone]
Local Time Zone (America/Toronto (EDT) offset -14400 (Daylight))
11.0:
(lldb) po [NSTimeZone localTimeZone]
Local Time Zone (America/Los_Angeles (PDT) offset -25200 (Daylight))
I created a fresh project and have the same results when I run the following snippit highlighting the issue:
- (void)logLocalizedDate {
// Setup: Create a UTC date time
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss zzz"];
NSDate *utcDate = [dateFormatter dateFromString: @"2017-08-18 00:00:00 UTC"];
[NSTimeZone setDefaultTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
// Now convert it to a local time zone string
NSString *localizedDateString = [self computeLocalTimeZoneDayStringFromDate:utcDate];
NSLog(@"Computed Date String: %@", localizedDateString);
}
-(NSString *)computeLocalTimeZoneDayStringFromDate:(NSDate *)dateToCompute
{
// Create a localized date formatter
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en"]];
[dateFormatter setDateFormat:@"yyyy-MM-dd"];
// This call differs between iOS10 and iOS 11
dateFormatter.timeZone = [NSTimeZone localTimeZone];
NSString *date = [dateFormatter stringFromDate:dateToCompute];
return date;
}
Anything wrong here? We've never had an issue in the past and similar code in our app has been ported from iOS 8 -> 9 -> 10 with no issues. In fact it hasn't changed since 2014.
Thanks
The various NSTimeZone properties (
localTimeZone
,
defaultTimeZone
and
systemTimeZone
) are kinda confusing. In general I recommend that folks use the system time zone because app-specific time zones don’t make much sense on Apple’s ‘shrink wrap’ platforms.
Notably, in Swift, these have all collapsed into one
current
property, which is a wrapper around
systemTimeZone
.
Having said that,
localTimeZone
should defer to
defaultTimeZone
which should defer to
systemTimeZone
as long as no one has overridden the default, so the difference in behaviour you’re seeing is a bit weird. I’m curious if you can replicate it on a real device. The simulator is a good general testing facility but real devices represent the acid test.
As to what you should do with your code, my recommendations are based on the assumption that
-logLocalizedDate
is just test code that you’ve included to illustrate the problem, and thus it’s not code that you expect to deploy. If that’s wrong, please let me know, ’cause that code is quite worrying (-:
So, focusing on
-computeLocalTimeZoneDayStringFromDate:
, I have a bunch of suggestions:
defaults to the system time zone, so you should just delete line 22.NSDateFormatter
Your code is relying on the fact that the
locale you set in line 18 gives you the Gregorian calendar. That’s true, but not something I’d want to rely on. I’d be happier useen
, as discussed in QA1480 NSDateFormatter and Internet Dates.en_US_POSIX
Better yet, use
for dealing with fixed-format dates (assuming your deployment target allows that).NSISO8601DateFormatter
Date formatters can be kinda slow, so it’s generally a good idea to cache them. This is especially true when dealing with fixed-format dates, where you don’t have to adapt to the user changing their locale settings.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"