DateFormatter is giving wrong date with Locale ar_AE (iOS 16 and above OS)

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let localeIdentifier = "ar_AE"
dateFormatter.locale = Locale(identifier: localeIdentifier)
if let date = dateFormatter.date(from: "2022-12-26") {
  dateFormatter.dateFormat = "dd MMM YYYY"
  let displayDate = dateFormatter.string(from: date)
  print(displayDate) // "26 ديسمبر 2023"
}

Converted date is one year ahead of the actual date. This issue occurs only with specific dates ranging from 26-Dec-2022 to 31st-Dec-2022, when Arabic locale is selected and on IOS 16.0 and above devices.

Answered by DTS Engineer in 740988022

There are three problems here. The first is that you’re confusing yyyy with YYYY. The latter gives you the year in the week-of-year calendar, which can be out of sync with the standard calendar at the start and end of the year.

A much bigger problems is that you’re using DateFormatter to parse fixed-format dates without pinning the locale to en_US_POSIX. That can yield all sorts of weird issues. QA1480 NSDateFormatter and Internet Dates discusses that in detail.

Finally, parsing a date without a time is tricky. See Parsing Dates Without Times.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I did a little bit of research and found YYYY corresponds to week year and yyyy corresponds to calendar year. Both of them differs in a certain way towards the end of the year. Please read more on https://technology.blog.gov.uk/2021/03/24/how-we-fixed-a-strange-week-year-bug/

But still I am wondering, why do we have a different behavior in iOS 16 and iOS 15 and below.

Accepted Answer

There are three problems here. The first is that you’re confusing yyyy with YYYY. The latter gives you the year in the week-of-year calendar, which can be out of sync with the standard calendar at the start and end of the year.

A much bigger problems is that you’re using DateFormatter to parse fixed-format dates without pinning the locale to en_US_POSIX. That can yield all sorts of weird issues. QA1480 NSDateFormatter and Internet Dates discusses that in detail.

Finally, parsing a date without a time is tricky. See Parsing Dates Without Times.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I stumbled across this answer and while your answer is correct it still misses one point. The DateFormatter produces wrong string results.

Especially when using the Locale en_US_POSIX

Just run the following code in playground:




let dfWithLocale: DateFormatter = {

    let df = DateFormatter()

    df.locale = Locale(identifier: "en_US_POSIX")

    df.dateFormat = "YYYY-MM-dd'T'HH:mm:ss"

    df.timeZone = TimeZone(secondsFromGMT: 0)



    return df

}()



let dfWithoutLocale: DateFormatter = {

    let df = DateFormatter()

    df.dateFormat = "YYYY-MM-dd'T'HH:mm:ss"

    df.timeZone = TimeZone(secondsFromGMT: 0)

    return df

}()



var date = Date().addingTimeInterval(-60*60*24*365*3)

while date < Date() {

    let stringWithLocale = dfWithLocale.string(from: date)

    let stringWithoutLocale =  dfWithoutLocale.string(from: date)

    print("Date: \(date) as StringWithLocale \(stringWithLocale) as StringWithoutLocale \(stringWithoutLocale)")

    date = date.addingTimeInterval(60*60*24)

}

This produces wrong output exactly around December 26 to December 31!

Here is the relevant output. In the middle the date as string using the locale, on the right the string using no locale and on the left the date with string interpolation.

Date: 2021-12-24 08:18:57 +0000 as StringWithLocale 2021-12-24T08:18:57 as StringWithoutLocale 2021-12-24T08:18:57

Date: 2021-12-25 08:18:57 +0000 as StringWithLocale 2021-12-25T08:18:57 as StringWithoutLocale 2021-12-25T08:18:57

Date: 2021-12-26 08:18:57 +0000 as StringWithLocale 2022-12-26T08:18:57 as StringWithoutLocale 2021-12-26T08:18:57

Date: 2021-12-27 08:18:57 +0000 as StringWithLocale 2022-12-27T08:18:57 as StringWithoutLocale 2021-12-27T08:18:57

Date: 2021-12-28 08:18:57 +0000 as StringWithLocale 2022-12-28T08:18:57 as StringWithoutLocale 2021-12-28T08:18:57

Date: 2021-12-29 08:18:57 +0000 as StringWithLocale 2022-12-29T08:18:57 as StringWithoutLocale 2021-12-29T08:18:57

Date: 2021-12-30 08:18:57 +0000 as StringWithLocale 2022-12-30T08:18:57 as StringWithoutLocale 2021-12-30T08:18:57

Date: 2021-12-31 08:18:57 +0000 as StringWithLocale 2022-12-31T08:18:57 as StringWithoutLocale 2021-12-31T08:18:57

Date: 2022-01-01 08:18:57 +0000 as StringWithLocale 2022-01-01T08:18:57 as StringWithoutLocale 2021-01-01T08:18:57

I am not sure when this started to go wrong. I am running this with macOS 13.2.1. Running the same in the playground for iOS gives the same result.

By the way: Using ISO8601DateFormatter gives the right result for this. But the other side of the API does not accept the Z at the end of the output. This is why I am using a format string.

You’re using YYYY, which yields the year in the week-of-year calendar. You should be using yyyy, which is the normal year.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

DateFormatter is giving wrong date with Locale ar_AE (iOS 16 and above OS)
 
 
Q