Search code examples
iosnsdateformatter

iOS 11.2.6 DateFormatter.date returns nil for cities that observe Brasília Summer Time


I created a new Single View App project from scratch and added only the following code to the ViewController's viewDidLoad method:

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yy"
if let date = dateFormatter.date(from: "11/20") {
    print("got the date: \(date)")
} else {
    print("failed getting the date")
}

The above code works everywhere in the world except cities that observe Brasília Summer Time (BRST).

I tested cities in all 38 time zones listed here by changing the time zone on the device in Settings > General > Date & Time. I also confirmed that BRST cities work fine on an iOS 11.0 device, but not iOS 11.2.6.

Also note that even on iOS 11.2.6, almost every other month/year combination I've tried works fine. Only "11/20" and "11/26" seem to fail.

Why is this code returning nil for cities that observe BRST on iOS 11.2.6?


Solution

  • The reason it doesn't work for that one timezone (Sao Paulo) is because of considerations specific to that timezone, namely daylight saving. Daylight saving in Brazil begins on the first Sunday in November, which happens to be November 1 in 2020.

    The time that it would default to does not exist, so it returns nil.

    You can play around with it by changing the formatter to include time and seeing exactly when the date formatter starts returning nil.

    Have run into similar confusion before; Dates and time zones are tricky.

    If you're using the date formatter for displaying dates to a user, you should typically not override their device settings and you probably want to avoid using a fixed date format, and should be aware of the potential for the date formatter to return a nil value (Swift optionals to the rescue). But if you're using it for internal dates, such as for your API, you should consider setting an explicit locale/time zone on your date formatter so these kinds of things don't happen, for example:

    dateFormatter.locale = Locale(identifier: "en_US_POSIX")
    dateFormatter.timeZone = TimeZone(abbreviation: "UTC")