What goes wrong with DateFormatter, and how do you parse a fixed format safely?

2.2.8 · L2 · Interview

Predict first

What does this print?

let f = DateFormatter()
f.locale = Locale(identifier: "en_US_POSIX")
f.timeZone = TimeZone(identifier: "UTC")
f.dateFormat = "yyyy-MM-dd"
print(f.date(from: "2024-02-30") as Any)
  • nil
  • 2024-03-01
  • Crash

Predict first

What does this print for 30 December 2024?

let f = DateFormatter()
f.locale = Locale(identifier: "en_US_POSIX")
f.timeZone = TimeZone(identifier: "UTC")
f.dateFormat = "YYYY-MM-dd"
print(f.string(from: Date(timeIntervalSince1970: 1735516800)))
  • 2024-12-30
  • 2025-12-30

Mental model. A fixed-format formatter is a barcode scanner: it only works if the label is printed the way the scanner expects, whatever the shop’s local habits.

To parse or print a fixed format (an API timestamp) set three things: locale = en_US_POSIX, an explicit timeZone, and a dateFormat. Without the POSIX locale, the user’s settings can override your pattern. The famous failure is a 12/24-hour setting that makes h:mm a stop parsing, so it returns nil on some phones only.

Use yyyy for the calendar year, never YYYY (week-based year). A formatter is not lenient by default, so “2024-02-30” returns nil. Creating formatters is slow, so reuse one. For display that should follow the locale, build the format from a template with setLocalizedDateFormatFromTemplate instead of a literal pattern.

Non-lenient parsing rejects a date that does not exist, giving nil.

YYYY is the week-based year, and 30 December 2024 is in week 1 of 2025.

Common traps

  • YYYY instead of yyyy. Correct all year until the last days of December.
  • No en_US_POSIX. Parsing works in your testing and fails for users with other 12/24-hour or calendar settings.

They might follow up with

  • Is DateFormatter thread-safe? Using one concurrently is supported, but mutating its properties from several threads is a race.
  • What about ISO 8601 with milliseconds? ISO8601DateFormatter with .withFractionalSeconds, or a custom Date.ISO8601FormatStyle.

Related questions

  • 2.2.7 FormatStyle or DateFormatter: which do you reach for?
  • 2.2.3 How do Calendar and DateComponents work, and why can YYYY give the wrong year?
  • 2.5.2 How are dates encoded and decoded in JSON, and why are they 31 years off?

← FormatStyle or DateFormatter: which do you reach for?   How do you measure elapsed time, and how do you show a duration? →