FormatStyle or DateFormatter: which do you reach for?

2.2.7 · L2 · Interview

Predict first

What does this print?

let style = Date.FormatStyle(date: .abbreviated, time: .omitted, locale: Locale(identifier: "en_US"), timeZone: TimeZone(identifier: "UTC")!)
print(Date(timeIntervalSince1970: 0).formatted(style))
  • Jan 1, 1970
  • 1/1/70
  • January 1, 1970

Predict first

What does this print?

1234567.formatted(.number.notation(.compactName).locale(Locale(identifier: "en_US")))
  • 1.2M
  • 1,234,567
  • 1M

Mental model. A FormatStyle is a recipe you can pass around. A DateFormatter is a kitchen appliance you have to set up and keep clean.

FormatStyle (iOS 15) is the modern way. Styles are value types: composable (.dateTime.year().month(.abbreviated).day()), Sendable, Codable, and they take an explicit locale. You call value.formatted(style), and the same idea covers numbers, currency, percent, lists, durations and measurements, plus parsing through ParseStrategy.

DateFormatter is a mutable, reference-type class that is expensive to create. It still earns its keep for exotic custom patterns or old deployment targets. For wire formats prefer Date.ISO8601FormatStyle or ISO8601DateFormatter to a hand-written pattern.

.abbreviated gives the short month name. Pinning the locale and zone makes the output reproducible.

The compact notation is a style option, not string surgery. It adapts to the locale too.

FormatStyleDateFormatter
Kindvalue typeclass (mutable)
Cost to createcheapexpensive, cache it
Locale and zoneexplicit, per styleproperties you set
Composableyesformat string
Available sinceiOS 15always

Common traps

  • Creating a DateFormatter in a cell’s render path. It is costly. Cache one, or use a style.
  • Letting the user’s locale format a machine value. Styles default to the current locale; set .locale explicitly for fixed output.

They might follow up with

  • How do you localise a custom layout? Use a skeleton: .dateTime.month().day() adapts to each locale’s order.
  • Can styles parse? Yes, Date(string, strategy:), and the result is a throwing call rather than an optional.

Related questions

  • 2.2.8 What goes wrong with DateFormatter, and how do you parse a fixed format safely?
  • 2.2.6 What is a Locale, and how is it different from the user’s language?

← What is a Locale, and how is it different from the user’s language?   What goes wrong with DateFormatter, and how do you parse a fixed… →