Gosh Darn Format Style!
GitHub Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Single Date Styles

Compositing Xcode 13+

A flexible way to composite the exact date string of your dreams.

Like lego blocks, this format style allows you to mix and match the date components you would like to use in your final string.

Symbol Description
.day() The numerical day relative to the month
.dayOfTheYear() The numerical day relative to the year
.era() The era of the date
.hour() The hour
.minute() The minute
.month() The month of the year
.quarter() The quarter
.second() The second
.timeZone The time zone
.week() The week
.weekday() The named day of the week
.year() The year
The Locale for the examples are all en_CA (English, Canada) and are using the Gregorian Calendar.

You can access the date format style in two ways:

  1. Instantiate a new instance of the Date.FormatStyle struct
  2. Use the .dateTime extension on FormatStyle

Either way, you can then use method chaining to customize the output.

twosday.formatted(.dateTime.day()) // "22"
twosday.formatted(.dateTime.dayOfYear()) // "53"
twosday.formatted(.dateTime.era()) // "AD"
twosday.formatted(.dateTime.hour()) // "2 AM"
twosday.formatted(.dateTime.minute()) // "22"
twosday.formatted(.dateTime.month()) // "Feb"
twosday.formatted(.dateTime.quarter()) // "Q1"
twosday.formatted(.dateTime.second()) // "22"
twosday.formatted(.dateTime.secondFraction(.fractional(2))) // "00"
twosday.formatted(.dateTime.secondFraction(.milliseconds(1))) // "8542000"
twosday.formatted(.dateTime.timeZone()) // "MST"
twosday.formatted(.dateTime.week()) // "9"
twosday.formatted(.dateTime.weekday()) // "Tue"
twosday.formatted(.dateTime.year()) // "2022"

twosday.formatted(Date.FormatStyle().day()) // "22"
twosday.formatted(Date.FormatStyle().dayOfYear()) // "53"
twosday.formatted(Date.FormatStyle().era()) // "AD"
twosday.formatted(Date.FormatStyle().hour()) // "2 AM"
twosday.formatted(Date.FormatStyle().minute()) // "22"
twosday.formatted(Date.FormatStyle().month()) // "Feb"
twosday.formatted(Date.FormatStyle().quarter()) // "Q1"
twosday.formatted(Date.FormatStyle().second()) // "22"
twosday.formatted(Date.FormatStyle().secondFraction(.fractional(2))) // "00"
twosday.formatted(Date.FormatStyle().secondFraction(.milliseconds(1))) // "8542000"
twosday.formatted(Date.FormatStyle().timeZone()) // "MST"
twosday.formatted(Date.FormatStyle().week()) // "9"
twosday.formatted(Date.FormatStyle().weekday()) // "Tue"
twosday.formatted(Date.FormatStyle().year()) // "2022"

The symbols can be chained together to mix and match your desired string.

twosday.formatted(
    Date.FormatStyle().year().month().day().hour().minute().second()
) // "Feb 22, 2022, 2:22:22 AM"

twosday.formatted(
    Date.FormatStyle().second().minute().hour().day().month().year()
) // "Feb 22, 2022, 2:22:22 AM"

twosday.formatted(
    .dateTime.year().month().day().hour().minute().second()
) // "Feb 22, 2022, 2:22:22 AM"
twosday.formatted(
    .dateTime.second().minute().hour().day().month().year()
) // "Feb 22, 2022, 2:22:22 AM"
The order of the symbols in the final string are controlled by the date’s Locale and not the order that they are called.

Customization

Each symbol has customization options.

Day

twosday.formatted(.dateTime.day(.twoDigits)) // "22"
twosday.formatted(.dateTime.day(.ordinalOfDayInMonth)) // "4"
twosday.formatted(.dateTime.day(.defaultDigits)) // "22"
twosday.formatted(.dateTime.day(.julianModified())) // "2459633"
twosday.formatted(.dateTime.day(.julianModified(minimumLength: 8))) // "02459633"

twosday.formatted(Date.FormatStyle().day(.twoDigits)) // "22"
twosday.formatted(Date.FormatStyle().day(.ordinalOfDayInMonth)) // "4"
twosday.formatted(Date.FormatStyle().day(.defaultDigits)) // "22"
twosday.formatted(Date.FormatStyle().day(.julianModified())) // "2459633"
twosday.formatted(Date.FormatStyle().day(.julianModified(minimumLength: 8))) // "02459633"

Day of Year

twosday.formatted(.dateTime.dayOfYear(.defaultDigits)) // "53"
twosday.formatted(.dateTime.dayOfYear(.threeDigits)) // "053"
twosday.formatted(.dateTime.dayOfYear(.twoDigits)) // "53"

twosday.formatted(Date.FormatStyle().dayOfYear(.defaultDigits)) // "53"
twosday.formatted(Date.FormatStyle().dayOfYear(.threeDigits)) // "053"
twosday.formatted(Date.FormatStyle().dayOfYear(.twoDigits)) // "53"

Era

twosday.formatted(.dateTime.era(.abbreviated)) // "AD"
twosday.formatted(.dateTime.era(.narrow)) // "A"
twosday.formatted(.dateTime.era(.wide)) // "Anno Domini"

twosday.formatted(Date.FormatStyle().era(.abbreviated)) // "AD"
twosday.formatted(Date.FormatStyle().era(.narrow)) // "A"
twosday.formatted(Date.FormatStyle().era(.wide)) // "Anno Domini"

Hour

Each of the following methods accepts an AMPMStyle.

AMPMStyle Description
omitted Hides the day period marker (AM/PM).
For example, 8 (for 8 in the morning), 1 (for 1 in the afternoon) if used with defaultDigits.
Or 08, 01 if used with twoDigits.
narrow Narrow day period if the locale prefers using day period with hour.
For example, 8, 8a, 13, 1p if used with defaultDigits.
Or 08, 08a, 13, 01p if used with twoDigits.
abbreviated Abbreviated day period if the locale prefers using day period with hour.
For example, 8, 8 AM, 13, 1 PM if used with defaultDigits.
Or 08, 08 AM, 13, 01 PM if used with twoDigits.
wide Wide day period if the locale prefers using day period with hour.
For example, 8, 8 A.M., 13, 1 P.M. if used with defaultDigits.
Or, 08, 08 A.M., 13, 01 P.M. if used with twoDigits.
Option Description
defaultDigits(amPM: Date.FormatStyle.Symbol.Hour.AMPMStyle) The preferred numeric hour format for the locale with minimum digits. Whether the period symbol (AM/PM) will be shown depends on the locale.
twoDigits(amPM: Date.FormatStyle.Symbol.Hour.AMPMStyle) The preferred two-digit hour format for the locale, zero padded if necessary. Whether the period symbol (AM/PM) will be shown depends on the locale.
conversationalDefaultDigits(amPM: Date.FormatStyle.Symbol.Hour.AMPMStyle) Behaves like defaultDigits: the preferred numeric hour format for the locale with minimum digits. May also use conversational period formats.
conversationalTwoDigits(amPM: Date.FormatStyle.Symbol.Hour.AMPMStyle) Behaves like twoDigits: two-digit hour format for the locale, zero padded if necessary. May also use conversational period formats.
twosday.formatted(.dateTime.hour(.conversationalDefaultDigits(amPM: .wide))) // "2 AM"
twosday.formatted(.dateTime.hour(.conversationalDefaultDigits(amPM: .narrow))) // "2 a"
twosday.formatted(.dateTime.hour(.conversationalDefaultDigits(amPM: .abbreviated))) // "2 AM"
twosday.formatted(.dateTime.hour(.conversationalDefaultDigits(amPM: .omitted))) // "02"
twosday.formatted(.dateTime.hour(.conversationalTwoDigits(amPM: .wide))) // "02 AM"
twosday.formatted(.dateTime.hour(.conversationalTwoDigits(amPM: .narrow))) // "02 a"
twosday.formatted(.dateTime.hour(.conversationalTwoDigits(amPM: .abbreviated))) // "02 AM"
twosday.formatted(.dateTime.hour(.conversationalTwoDigits(amPM: .omitted))) // "02"
twosday.formatted(.dateTime.hour(.defaultDigits(amPM: .wide))) // "2 AM"
twosday.formatted(.dateTime.hour(.defaultDigits(amPM: .narrow))) // "2 a"
twosday.formatted(.dateTime.hour(.defaultDigits(amPM: .abbreviated))) // "2 AM"
twosday.formatted(.dateTime.hour(.defaultDigits(amPM: .omitted))) // "02"
twosday.formatted(.dateTime.hour(.twoDigits(amPM: .wide))) // "02 AM"
twosday.formatted(.dateTime.hour(.twoDigits(amPM: .narrow))) // "02 a"
twosday.formatted(.dateTime.hour(.twoDigits(amPM: .abbreviated))) // "02 AM"
twosday.formatted(.dateTime.hour(.twoDigits(amPM: .omitted))) // "02"

twosday.formatted(Date.FormatStyle().hour(.conversationalDefaultDigits(amPM: .wide))) // "2 AM"
twosday.formatted(Date.FormatStyle().hour(.conversationalDefaultDigits(amPM: .narrow))) // "2 a"
twosday.formatted(Date.FormatStyle().hour(.conversationalDefaultDigits(amPM: .abbreviated))) // "2 AM"
twosday.formatted(Date.FormatStyle().hour(.conversationalDefaultDigits(amPM: .omitted))) // "02"
twosday.formatted(Date.FormatStyle().hour(.conversationalTwoDigits(amPM: .wide))) // "02 AM"
twosday.formatted(Date.FormatStyle().hour(.conversationalTwoDigits(amPM: .narrow))) // "02 a"
twosday.formatted(Date.FormatStyle().hour(.conversationalTwoDigits(amPM: .abbreviated))) // "02 AM"
twosday.formatted(Date.FormatStyle().hour(.conversationalTwoDigits(amPM: .omitted))) // "02"
twosday.formatted(Date.FormatStyle().hour(.defaultDigits(amPM: .wide))) // "2 AM"
twosday.formatted(Date.FormatStyle().hour(.defaultDigits(amPM: .narrow))) // "2 a"
twosday.formatted(Date.FormatStyle().hour(.defaultDigits(amPM: .abbreviated))) // "2 AM"
twosday.formatted(Date.FormatStyle().hour(.defaultDigits(amPM: .omitted))) // "02"
twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .wide))) // "02 AM"
twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .narrow))) // "02 a"
twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .abbreviated))) // "02 AM"
twosday.formatted(Date.FormatStyle().hour(.twoDigits(amPM: .omitted))) // "02"

Minute

twosday.formatted(.dateTime.minute(.twoDigits)) // "22"
twosday.formatted(.dateTime.minute(.defaultDigits)) // "22"

twosday.formatted(Date.FormatStyle().minute(.twoDigits)) // "22"
twosday.formatted(Date.FormatStyle().minute(.defaultDigits)) // "22"

Month

twosday.formatted(.dateTime.month(.defaultDigits)) // "2"
twosday.formatted(.dateTime.month(.twoDigits)) // "02"
twosday.formatted(.dateTime.month(.wide)) // "February"
twosday.formatted(.dateTime.month(.abbreviated)) // "Feb"
twosday.formatted(.dateTime.month(.narrow)) // "F"

twosday.formatted(Date.FormatStyle().month(.defaultDigits)) // "2"
twosday.formatted(Date.FormatStyle().month(.twoDigits)) // "02"
twosday.formatted(Date.FormatStyle().month(.wide)) // "February"
twosday.formatted(Date.FormatStyle().month(.abbreviated)) // "Feb"
twosday.formatted(Date.FormatStyle().month(.narrow)) // "F"

Quarter

twosday.formatted(.dateTime.quarter(.narrow)) // "1"
twosday.formatted(.dateTime.quarter(.abbreviated)) // "Q1"
twosday.formatted(.dateTime.quarter(.wide)) // "1st quarter"
twosday.formatted(.dateTime.quarter(.twoDigits)) // "01"
twosday.formatted(.dateTime.quarter(.oneDigit)) // "1"

twosday.formatted(Date.FormatStyle().quarter(.narrow)) // "1"
twosday.formatted(Date.FormatStyle().quarter(.abbreviated)) // "Q1"
twosday.formatted(Date.FormatStyle().quarter(.wide)) // "1st quarter"
twosday.formatted(Date.FormatStyle().quarter(.twoDigits)) // "01"
twosday.formatted(Date.FormatStyle().quarter(.oneDigit)) // "1"

Second

twosday.formatted(.dateTime.second(.twoDigits)) // "22"
twosday.formatted(.dateTime.second(.defaultDigits)) // "22"

twosday.formatted(Date.FormatStyle().second(.twoDigits)) // "22"
twosday.formatted(Date.FormatStyle().second(.defaultDigits)) // "22"

Fractional Second

twosday.formatted(Date.FormatStyle().secondFraction(.fractional(2))) // "00"
twosday.formatted(Date.FormatStyle().secondFraction(.milliseconds(1))) // "8542000"

twosday.formatted(.dateTime.secondFraction(.fractional(2))) // "00"
twosday.formatted(.dateTime.secondFraction(.milliseconds(1))) // "8542000"

Time Zone

twosday.formatted(.dateTime.timeZone(.exemplarLocation)) // "Edmonton"
twosday.formatted(.dateTime.timeZone(.genericLocation)) // "Edmonton Time"
twosday.formatted(.dateTime.timeZone(.genericName(.long))) // "Mountain Time"
twosday.formatted(.dateTime.timeZone(.genericName(.short))) // "MT"
twosday.formatted(.dateTime.timeZone(.identifier(.short))) // "caedm"
twosday.formatted(.dateTime.timeZone(.identifier(.long))) // "America/Edmonton"
twosday.formatted(.dateTime.timeZone(.iso8601(.long))) // "-07:00"
twosday.formatted(.dateTime.timeZone(.iso8601(.short))) // "-07:00"
twosday.formatted(.dateTime.timeZone(.specificName(.short))) // "MST"
twosday.formatted(.dateTime.timeZone(.specificName(.long))) // "Mountain Standard Time"
twosday.formatted(.dateTime.timeZone(.localizedGMT(.short))) // "GMT-7"
twosday.formatted(.dateTime.timeZone(.localizedGMT(.long))) // "GMT-07:00"

twosday.formatted(Date.FormatStyle().timeZone(.exemplarLocation)) // "Edmonton"
twosday.formatted(Date.FormatStyle().timeZone(.genericLocation)) // "Edmonton Time"
twosday.formatted(Date.FormatStyle().timeZone(.genericName(.long))) // "Mountain Time"
twosday.formatted(Date.FormatStyle().timeZone(.genericName(.short))) // "MT"
twosday.formatted(Date.FormatStyle().timeZone(.identifier(.short))) // "caedm"
twosday.formatted(Date.FormatStyle().timeZone(.identifier(.long))) // "America/Edmonton"
twosday.formatted(Date.FormatStyle().timeZone(.iso8601(.long))) // "-07:00"
twosday.formatted(Date.FormatStyle().timeZone(.iso8601(.short))) // "-07:00"
twosday.formatted(Date.FormatStyle().timeZone(.specificName(.short))) // "MST"
twosday.formatted(Date.FormatStyle().timeZone(.specificName(.long))) // "Mountain Standard Time"
twosday.formatted(Date.FormatStyle().timeZone(.localizedGMT(.short))) // "GMT-7"
twosday.formatted(Date.FormatStyle().timeZone(.localizedGMT(.long))) // "GMT-07:00"

Week

twosday.formatted(.dateTime.week(.defaultDigits)) // "9"
twosday.formatted(.dateTime.week(.twoDigits)) // "09"
twosday.formatted(.dateTime.week(.weekOfMonth)) // "9"

twosday.formatted(Date.FormatStyle().week(.defaultDigits)) // "9"
twosday.formatted(Date.FormatStyle().week(.twoDigits)) // "09"
twosday.formatted(Date.FormatStyle().week(.weekOfMonth)) // "9"

Weekday

twosday.formatted(.dateTime.weekday(.abbreviated)) // "Tue"
twosday.formatted(.dateTime.weekday(.twoDigits)) // "3"
twosday.formatted(.dateTime.weekday(.short)) // "Tu"
twosday.formatted(.dateTime.weekday(.oneDigit)) // "3"
twosday.formatted(.dateTime.weekday(.wide)) // "Tuesday"
twosday.formatted(.dateTime.weekday(.narrow)) // "T"

twosday.formatted(Date.FormatStyle().weekday(.abbreviated)) // "Tue"
twosday.formatted(Date.FormatStyle().weekday(.twoDigits)) // "3"
twosday.formatted(Date.FormatStyle().weekday(.short)) // "Tu"
twosday.formatted(Date.FormatStyle().weekday(.oneDigit)) // "3"
twosday.formatted(Date.FormatStyle().weekday(.wide)) // "Tuesday"
twosday.formatted(Date.FormatStyle().weekday(.narrow)) // "T"

Year

twosday.formatted(.dateTime.year(.twoDigits)) // "22"
twosday.formatted(.dateTime.year(.defaultDigits)) // "2022"
twosday.formatted(.dateTime.year(.extended())) // "22"
twosday.formatted(.dateTime.year(.extended(minimumLength: 2))) // "2022"
twosday.formatted(.dateTime.year(.padded(10))) // "0000002022"
twosday.formatted(.dateTime.year(.relatedGregorian())) // "2022"
twosday.formatted(.dateTime.year(.relatedGregorian(minimumLength: 2))) // "22"

twosday.formatted(Date.FormatStyle().year(.twoDigits)) // "22"
twosday.formatted(Date.FormatStyle().year(.defaultDigits)) // "2022"
twosday.formatted(Date.FormatStyle().year(.extended())) // "22"
twosday.formatted(Date.FormatStyle().year(.extended(minimumLength: 2))) // "2022"
twosday.formatted(Date.FormatStyle().year(.padded(10))) // "0000002022"
twosday.formatted(Date.FormatStyle().year(.relatedGregorian())) // "2022"
twosday.formatted(Date.FormatStyle().year(.relatedGregorian(minimumLength: 2))) // "22"

Setting the Locale

You can set the Locale by appending the .locale() method onto the last Symbol.

twosday.formatted(.dateTime.locale(Locale(identifier: "fr_FR"))) // "22/02/2022 à 2:22"
twosday.formatted(Date.FormatStyle().locale(Locale(identifier: "fr_FR"))) // "22/02/2022 à 2:22"

Attributed String Output

You can output an AttributedString by appending the attributed method onto the last symbol.

twosday.formatted(.dateTime.attributed)
twosday.formatted(Date.FormatStyle().attributed)

Localizing Number Systems

In cases where a given Locale has multiple number systems available, numeric format styles will default to using the number system which matches the your system’s Locale.current value. You’re able to explicitly set the number system for the Format Style to use by initializing a new Locale with the number system set using the BCP-47 or ICU Identifiers:

ICU

// Without
let defaultHebrew = Locale(identifier: "he")
Date.now.formatted(.dateTime.year().month().day().locale(defaultHebrew)) // "23 בספט׳ 2023"

// With
let hebrew = Locale(identifier: "he@numbers=hebr;calendar=hebrew")
Date.now.formatted(.dateTime.year().month().day().locale(hebrew)) // "כ״ג בספט׳ ב׳כ״ג"

Parsing Dates From Strings

Date.FormatStyle conforms to ParseableFormatStyle and can be set up to parse Date objects from a String.

try? Date.FormatStyle()
    .day()
    .month()
    .year()
    .hour()
    .minute()
    .second()
    .parse("Feb 22, 2022, 2:22:22 AM") // Feb 22, 2022, 2:22:22 AM

try? Date.FormatStyle()
    .day()
    .month()
    .year()
    .hour()
    .minute()
    .second()
    .parseStrategy.parse("Feb 22, 2022, 2:22:22 AM") // Feb 22, 2022, 2:22:22 AM

try? Date(
    "Feb 22, 2022, 2:22:22 AM",
    strategy: Date.FormatStyle().day().month().year().hour().minute().second().parseStrategy
) // Feb 22, 2022 at 2:22 AM

Date and Time Xcode 13+

A quick way of displaying the date and time.

This is the default formatter used when calling formatted on any date object. In general, it’s an easy way to display a date with or without it’s corresponding time component.

Each portion (the date and the time), can be customized in the following ways.

DateStyle and TimeStyle

To customize the display, you have the option of including a DateStyle or a TimeStyle parameter.

DateStyle Option Description
.omitted Omits the date from display
.numeric Displays the date components as numbers
.abbreviated Displays the year, month, and numerical day. Month is shortened
.long Displays the year, month, and numerical day. Month is in full
.complete Displays the year, month, weedkay, and numberical day in full
TimeStyle Option Description
.complete Shows the hour, minute, second, and time zone
.shortened Shortened hour, minute, and second for your locale
.omitted Omits the time from display
twosday.formatted(date: .abbreviated, time: .omitted) // "Feb 22, 2022"
twosday.formatted(date: .complete, time: .omitted) // "Tuesday, February 22, 2022"
twosday.formatted(date: .long, time: .omitted) // "February 22, 2022"
twosday.formatted(date: .numeric, time: .omitted) // "2/22/2022"

twosday.formatted(date: .omitted, time: .complete) // "2:22:22 AM MST"
twosday.formatted(date: .omitted, time: .shortened) // "2:22 AM"
twosday.formatted(date: .omitted, time: .standard) // "2:22:22 AM"

twosday.formatted(date: .abbreviated, time: .complete) // "Feb 22, 2022, 2:22:22 AM MST"
twosday.formatted(date: .abbreviated, time: .shortened) // "Feb 22, 2022, 2:22 AM"
twosday.formatted(date: .abbreviated, time: .standard) // "Feb 22, 2022, 2:22:22 AM"
twosday.formatted(date: .complete, time: .complete) // "Tuesday, February 22, 2022, 2:22:22 AM MST"
twosday.formatted(date: .complete, time: .shortened) // "Tuesday, February 22, 2022, 2:22 AM"
twosday.formatted(date: .complete, time: .standard) // "Tuesday, February 22, 2022, 2:22:22 AM"
twosday.formatted(date: .long, time: .complete) // "February 22, 2022, 2:22:22 AM MST"
twosday.formatted(date: .long, time: .shortened) // "February 22, 2022, 2:22 AM"
twosday.formatted(date: .long, time: .standard) // "February 22, 2022, 2:22:22 AM"
twosday.formatted(date: .numeric, time: .complete) // "2/22/2022, 2:22:22 AM MST"
twosday.formatted(date: .numeric, time: .shortened) // "2/22/2022, 2:22 AM"
twosday.formatted(date: .numeric, time: .standard) // "2/22/2022, 2:22:22 AM"

Setting the Locale & Calendar

In order to set the Locale and Calendar of the output string, you need to initialize an instance of Date.FormatStyle.

let frenchHebrew = Date.FormatStyle(
    date: .complete,
    time: .complete,
    locale: Locale(identifier: "fr_FR"),
    calendar: Calendar(identifier: .hebrew),
    timeZone: TimeZone(secondsFromGMT: 0)!,
    capitalizationContext: .standalone
)

twosday.formatted(frenchHebrew) // "Mardi 22 février 2022 ap. J.-C. 9:22:22 UTC"
frenchHebrew.format(twosday) // "Mardi 22 février 2022 ap. J.-C. 9:22:22 UTC"

You can also extend the FormatStyle protocol as a way of simplifying access to your new custom Date.FormatStyle

struct FrenchHebrewStyle: FormatStyle {
    typealias FormatInput = Date
    typealias FormatOutput = String

    static let frenchHebrew = Date.FormatStyle(
        date: .complete,
        time: .complete,
        locale: Locale(identifier: "fr_FR"),
        calendar: Calendar(identifier: .hebrew),
        timeZone: TimeZone(secondsFromGMT: 0)!,
        capitalizationContext: .standalone
    )

    func format(_ value: Date) -> String {
        FrenchHebrewStyle.frenchHebrew.format(value)
    }
}

extension FormatStyle where Self == FrenchHebrewStyle {
    static var frenchHebrew: FrenchHebrewStyle { .init() }
}

twosday.formatted(.frenchHebrew) // "Mardi 22 février 2022 ap. J.-C. 9:22:22 UTC"

Localizing Number Systems

In cases where a given Locale has multiple number systems available, numeric format styles will default to using the number system which matches the your system’s Locale.current value. You’re able to explicitly set the number system for the Format Style to use by initializing a new Locale with the number system set using the BCP-47 or ICU Identifiers:

ICU

// Without
let defaultHebrew = Locale(identifier: "he")
Date.now.formatted(.dateTime.year().month().day().locale(defaultHebrew)) // "23 בספט׳ 2023"

// With
let hebrew = Locale(identifier: "he@numbers=hebr;calendar=hebrew")
Date.now.formatted(.dateTime.year().month().day().locale(hebrew)) // "כ״ג בספט׳ ב׳כ״ג"

ISO 8601 Date StyleXcode 13+

The fastest way to output the globe’s standard date string.

Since this is an international standard, the amount of customization is limited with this format style.

let twosdayDateComponents = DateComponents(
    year: 2022,
    month: 2,
    day: 22,
    hour: 2,
    minute: 22,
    second: 22,
    nanosecond: 22
)
let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!

twosday.formatted(.iso8601) // "2022-02-22T09:22:22Z"

Setting the Locale

Even though this is technically possible. There’s no real reason to set the locale as the ISO 8601 standard is fixed.

You can set the locale by appending the .locale() method onto the end of the format style.

let franceLocale = Locale(identifier: "fr_FR")
twosday.formatted(.iso8601.locale(franceLocale)) // "2022-02-22T09:22:22Z"

Customizing Output

There are limited customization options available, and can only be accessed by creating a new instance of the Date.ISO8601FormatStyle struct.

// Initialization Customization
Date.ISO8601FormatStyle(
  dateSeparator: Date.ISO8601FormatStyle.DateSeparator,         // .omitted or .dash
  dateTimeSeparator: Date.ISO8601FormatStyle.DateTimeSeparator, // .space or .standard
  timeSeparator: Date.ISO8601FormatStyle.TimeSeparator,         // .colon or .omitted
  timeZoneSeparator: Date.ISO8601FormatStyle.TimeZoneSeparator, // .colon or .omitted
  includingFractionalSeconds: Bool, 
  timeZone: TimeZone
)


let isoFormat = Date.ISO8601FormatStyle(
    dateSeparator: .dash,
    dateTimeSeparator: .standard,
    timeSeparator: .colon,
    timeZoneSeparator: .colon,
    includingFractionalSeconds: true,
    timeZone: TimeZone(secondsFromGMT: 0)!
)

isoFormat.format(twosday) // "2022-02-22T09:22:22.000Z"
twosday.formatted(isoFormat)

// Method Chaining Customization
let isoStyle = Date.ISO8601FormatStyle(timeZone: TimeZone(secondsFromGMT: 0)!) // Time zone can only be set during initialization.
    .year()
    .day()
    .month()
    .dateSeparator(.dash) // .dash or .omitted
    .dateTimeSeparator(.standard) // .space or .standard
    .timeSeparator(.colon) // .colon or .omitted
    .timeZoneSeparator(.colon) // .colon or .omitted
    .time(includingFractionalSeconds: true)
    .locale(Locale(identifier: "fr_FR"))

isoStyle.format(twosday) // "2022-02-22T09:22:22.000"
twosday.formatted(isoStyle) // "2022-02-22T09:22:22.000"

Parsing ISO8601 Dates From Strings

Similar to parsing dates from strings, Date.ISO8601FormatStyle conforms to ParseableDateFormat and is purpose built to allow you to parse these types of dates.

try? Date.ISO8601FormatStyle(timeZone: TimeZone(secondsFromGMT: 0)!)
    .year()
    .day()
    .month()
    .dateSeparator(.dash)
    .dateTimeSeparator(.standard)
    .timeSeparator(.colon)
    .timeZoneSeparator(.colon)
    .time(includingFractionalSeconds: true)
    .parse("2022-02-22T09:22:22.000") // Feb 22, 2022, 2:22:22 AM

try? Date.ISO8601FormatStyle(timeZone: TimeZone(secondsFromGMT: 0)!)
    .year()
    .day()
    .month()
    .dateSeparator(.dash)
    .dateTimeSeparator(.standard)
    .timeSeparator(.colon)
    .timeZoneSeparator(.colon)
    .time(includingFractionalSeconds: true)
    .parseStrategy.parse("2022-02-22T09:22:22.000") // Feb 22, 2022, 2:22:22 AM


try? Date(
    "2022-02-22T09:22:22.000",
    strategy: Date.ISO8601FormatStyle(timeZone: TimeZone(secondsFromGMT: 0)!)
        .year()
        .day()
        .month()
        .dateSeparator(.dash)
        .dateTimeSeparator(.standard)
        .timeSeparator(.colon)
        .timeZoneSeparator(.colon)
        .time(includingFractionalSeconds: true)
        .parseStrategy
) // Feb 22, 2022 at 2:22 AM
`

Relative Date StyleXcode 13+

Quickly say approximately how long it will be between a date and now.

This format style will tell you the approximate distance to or from a date to now.

There are no options available to set the units you would like to display, the system will only show the largest unit.

Available Options

There are two sets of options available, the presentation style and the units style.

Presentation Style Description
.numeric A style that uses a numeric style to describe relative dates, such as “1 day ago” or “in 3 weeks”.
.named A style that uses named styles to describe relative dates, such as “yesterday”, “last week”, or “next week”.
Unit Style Description
.abbreviated A style that uses abbreviated units, such as “2 mo. ago”.
.narrow A style that uses the shortest units, such as “2 mo. ago”.
.spellOut A style that spells out units, such as “two months ago”.
.wide A style that uses full representation of units, such as “2 months ago”.
let thePast = Calendar(identifier: .gregorian).date(byAdding: .day, value: -14, to: Date())!

thePast.formatted(.relative(presentation: .numeric, unitsStyle: .abbreviated)) // "2 wk. ago"
thePast.formatted(.relative(presentation: .numeric, unitsStyle: .narrow)) // "2 wk. ago"
thePast.formatted(.relative(presentation: .numeric, unitsStyle: .spellOut)) // "two weeks ago"
thePast.formatted(.relative(presentation: .numeric, unitsStyle: .wide)) // "2 weeks ago"
thePast.formatted(.relative(presentation: .named, unitsStyle: .abbreviated)) // "2 wk. ago"
thePast.formatted(.relative(presentation: .named, unitsStyle: .narrow)) // "2 wk. ago"
thePast.formatted(.relative(presentation: .named, unitsStyle: .spellOut)) // "two weeks ago"
thePast.formatted(.relative(presentation: .named, unitsStyle: .wide)) // "2 weeks ago"

Setting the Locale & Calendar

The locale can be set by appending the .locale() method at the end of your .formatted() method call.

let franceLocale = Locale(identifier: "fr_FR")
thePast.formatted(.relative(presentation: .named, unitsStyle: .spellOut).locale(franceLocale)) // "il y a deux semaines"

The calendar can only be set by initializing a new instance of the Date.RelativeFormatStyle struct.

let relativeInFrench = Date.RelativeFormatStyle(
    presentation: .named,
    unitsStyle: .spellOut,
    locale: Locale(identifier: "fr_FR"),
    calendar: Calendar(identifier: .gregorian),
    capitalizationContext: .beginningOfSentence
)

thePast.formatted(relativeInFrench) // "Il y a deux semaines"
relativeInFrench.format(thePast) // "Il y a deux semaines"

You can easily re-use and access this custom format style by extending FormatStyle.

struct InFrench: FormatStyle {
    typealias FormatInput = Date
    typealias FormatOutput = String

    static let relativeInFrench = Date.RelativeFormatStyle(
        presentation: .named,
        unitsStyle: .spellOut,
        locale: Locale(identifier: "fr_FR"),
        calendar: Calendar(identifier: .gregorian),
        capitalizationContext: .beginningOfSentence
    )

    func format(_ value: Date) -> String {
        InFrench.relativeInFrench.format(value)
    }
}

extension FormatStyle where Self == InFrench {
    static var inFrench: InFrench { .init() }
}

thePast.formatted(.inFrench) // "Il y a deux semaines"

Verbatim Date Style Xcode 13+ Xcode 14+

A method of creating fixed, structured date format strings.

The verbatim date format style is as close as Format Styles get to the old date formatting string methods used by (NS)DateFormatter.

Unlike the older methods, the format string that you pass into the format style is much more regimented in how you access the various symbols.

Version Differences

  1. Xcode 13+ You use this style by initializing a new instance of Date.VerbatimFormatStyle which can be used directly, or passed into a .formatted method.
  2. Xcode 14+ Similar to other date format styles, you can now use the .formatted(.verbatim(…)) type method.

You can easily backport the Xcode 14 type method to your project that supports the Xcode 13 platforms by including the following extension in your project:

public extension FormatStyle where Self == Date.VerbatimFormatStyle {
    static func verbatim(
        _ format: Date.FormatString,
        locale: Locale? = nil,
        timeZone: TimeZone,
        calendar: Calendar
    ) -> Date.VerbatimFormatStyle {
        return Date.VerbatimFormatStyle(format: format, locale: locale, timeZone: timeZone, calendar: calendar)
    }
}

Format Strings

The power of the verbatim format style lies in the Date.FormatString that is passed in. This struct conforms to the ExpressibleByStringInterpolation protocol which allows us mix and match structured values (known as tokens) and strings as much as we want.

let twosdayDateComponents = DateComponents(
    year: 2022,
    month: 2,
    day: 22,
    hour: 22,
    minute: 22,
    second: 22
)

let twosday = Calendar(identifier: .gregorian).date(from: twosdayDateComponents)!

let verbatim = Date.VerbatimFormatStyle(
    format: "It's Twosday! \(year: .defaultDigits)-\(month: .abbreviated)(\(month: .defaultDigits))-\(day: .defaultDigits) at \(hour: .twoDigits(clock: .twentyFourHour, hourCycle: .oneBased)):\(minute: .defaultDigits):\(second: .defaultDigits)",
    locale: Locale(identifier: "en_US"),
    timeZone: TimeZone.current,
    calendar: .current
)

verbatim.format(twosday) // "It's Twosday! 2022-Feb(2)-22 at 22:22:22"

twosday.formatted(
    .verbatim(
        "It's Twosday! \(year: .defaultDigits)-\(month: .abbreviated)(\(month: .defaultDigits))-\(day: .defaultDigits) at \(hour: .twoDigits(clock: .twentyFourHour, hourCycle: .oneBased)):\(minute: .defaultDigits):\(second: .defaultDigits)",
        locale: Locale(identifier: "fr_FR"),
        timeZone: TimeZone.current,
        calendar: .current
    )
) // "It's Twosday! 2022-févr.(2)-22 at 22:22:22"
If you omit the Locale when constructing or calling this style, certain tokens will use their un-localized standard values. For example M02 instead of February for \(month: .abbreviated).

Symbol Tokens

Every conceivable piece of information inside of a Date is available for use.


Era

Option Description
.abbreviated Abbreviated Era name. For example, “AD”, “Reiwa”, “令和”.
.wide Wide era name. For example, “Anno Domini”, “Reiwa”, “令和”.
.narrow Narrow era name. For example, For example, “A”, “R”, “R”.

Year

Option Description
.defaultDigits Minimum number of digits that shows the full year. For example, 2, 20, 201, 2017, 20173.
.twoDigits Two low-order digits. Padded or truncated if necessary. For example, 02, 20, 01, 17, 73.
.padded(_ length: Int) Three or more digits. Padded if necessary. For example, 002, 020, 201, 2017, 20173.
.relatedGregorian(minimumLength:) Related Gregorian year. For non-Gregorian calendars, this corresponds to the extended Gregorian year in which the calendar’s year begins. Related Gregorian years are often displayed, for example, when formatting dates in the Japanese calendar — e.g. “2012(平成24)年1月15日” — or in the Chinese calendar — e.g. “2012壬辰年腊月初四”.
.extended(minimumLength:) Extended year. This is a single number designating the year of this calendar system, encompassing all supra-year fields. For example, for the Julian calendar system, year numbers are positive, with an era of BCE or CE. An extended year value for the Julian calendar system assigns positive values to CE years and negative values to BCE years, with 1 BCE being year 0.

YearForWeekOfYear

Option Description
.defaultDigits Minimum number of digits that shows the full year in “Week of Year”-based calendars. For example, 2, 20, 201, 2017, 20173.
.twoDigits Two low-order digits. Padded or truncated if necessary. For example, 02, 20, 01, 17, 73.
.padded(_ length: Int) Three or more digits. Padded if necessary. For example, 002, 020, 201, 2017, 20173.

CyclicYear

Calendars such as the Chinese lunar calendar (and related calendars) and the Hindu calendars use 60-year cycles of year names. If the calendar does not provide cyclic year name data, or if the year value to be formatted is out of the range of years for which cyclic name data is provided, then numeric formatting is used (behaves like Year).

Option Description
.abbreviated Abbreviated cyclic year name. For example, “甲子”.
.wide Wide cyclic year name. For example, “甲子”.
.narrow Narrow cyclic year name. For example, “甲子”.

Quarter

Option Description
.oneDigit Numeric: one digit quarter. For example 2.
.twoDigits Numeric: two digits with zero padding. For example 02.
.abbreviated Abbreviated quarter. For example Q2.
.wide The quarter spelled out in full, for example 2nd quarter.
.narrow Narrow quarter. For example 2.

Month

Option Description
.defaultDigits Minimum number of digits that shows the numeric month. Intended to be used in conjunction with Day.defaultDigits. For example, 9, 12.
.twoDigits 2 digits, zero pad if needed. For example, 09, 12.
.abbreviated Abbreviated month name. For example, “Sep”.
.wide Wide month name. For example, “September”. ``
.narrow Narrow month name. For example, “S”.

Week

Week symbols. Use with YearForWeekOfYear for the year field instead of Year.

Option Description
.defaultDigits Numeric week of year. For example, 8, 27.
.twoDigits Two-digit numeric week of year, zero padded as necessary. For example, 08, 27.
.weekOfMonth One-digit numeric week of month, starting from 1. For example, 1.

Day

Option Description
.defaultDigits Minimum number of digits that shows the full numeric day of month. For example, 1, 18.
.twoDigits Two-digit, zero-padded if necessary. For example, 01, 18.
.ordinalOfDayInMonth Ordinal of day in month. For example, the 2nd Wed in July would yield 2.
.ulianModified(minimumLength:) The field length specifies the minimum number of digits, with zero-padding as necessary.
This is different from the conventional Julian day number in two regards. First, it demarcates days at local zone midnight, rather than noon GMT. Second, it is a local number; that is, it depends on the local time zone. It can be thought of as a single number that encompasses all the date-related fields.
For example, 2451334.

DayOfYear

Option Description
.defaultDigits Minimum number of digits that shows the full numeric day of year. For example, 7, 33, 345.
.twoDigits Two-digit day of year, with zero-padding as necessary. For example, 07, 33, 345.
.threeDigits Three-digit day of year, with zero-padding as necessary. For example, 007, 033, 345.

Weekday

Option Description
.abbreviated Abbreviated day of week name. For example, “Tue”.
.wide Wide day of week name. For example, “Tuesday”.
.narrow Narrow day of week name. For example, “T”.
.short Short day of week name. For example, “Tu”.
.oneDigit Local day of week number/name. The value depends on the local starting day of the week.
.twoDigits Local day of week number/name, format style; two digits, zero-padded if necessary.

DayPeriod

The time period (for example, “a.m.” or “p.m.”). May be upper or lower case depending on the locale and other options.

Each of the options can be passed a width case.

  • abbreviated
  • wide
  • narrow
Option Description
.standard(_ width:) Standard day period. For example,
Abbreviated: 12 am.
Wide: 12 am
Narrow: 12a.
.with12s(_ width:) Day period including designations for noon and midnight. For example,
Abbreviated: mid
Wide: midnight
Narrow: md.
.conversational Conversational day period. For example,
Abbreviated: at night, nachm., ip.
Wide: at night, nachmittags, iltapäivällä.
Narrow: at night, nachm., iltap.

Minute

Option Description
.defaultDigits Minimum digits to show the numeric minute. Truncated, not rounded. For example, 8, 59.
.twoDigits Two-digit numeric, zero padded if needed. For example, 08, 59.

Second

Option Description
.defaultDigits Minimum digits to show the numeric second. Truncated, not rounded. For example, 8, 12.
.twoDigits Two digits numeric, zero padded if needed, not rounded. For example, 08, 12.

SecondFraction

Option Description
.fractional(_ val:) Fractional second (numeric).
Truncates, like other numeric time fields, but in this case to the number of digits specified by the associated Int.
For example, specifying 4 for seconds value 12.34567 yields 12.3456.
.milliseconds(_ val:) Milliseconds in day (numeric).
The associated Int specifies the minimum number of digits, with zero-padding as necessary. The maximum number of digits is 9.
This field behaves exactly like a composite of all time-related fields, not including the zone fields. As such, it also reflects discontinuities of those fields on DST transition days. On a day of DST onset, it will jump forward. On a day of DST cessation, it will jump backward. This reflects the fact that is must be combined with the offset field to obtain a unique local time value.

TimeZone

Each talkes a Width case.

  • short
  • long
Option Description
.specificName(_ width:) Specific non-location format. Falls back to shortLocalizedGMT if unavailable. For example,
short: “PDT”
long: “Pacific Daylight Time”.
.genericName(_ width:) Generic non-location format. Falls back to genericLocation if unavailable. For example,
short: “PT”. Fallback again to localizedGMT(.short) if genericLocation(.short) is unavaiable.
long: “Pacific Time”
.localizedGMT(_ width:) Short localized GMT format. For example,
short: “GMT-8”
long: “GMT-8:00”
.identifier(_ width:) The time zone ID. For example,
short: “uslax”
long: “America/Los_Angeles”.
.exemplarLocation The exemplar city (location) for the time zone. The localized exemplar city name for the special zone or unknown is used as the fallback if it is unavailable.
For example, “Los Angeles”.
.genericLocation The generic location format. Falls back to longLocalizedGMT if unavailable. Recommends for presenting possible time zone choices for user selection.
For example, “Los Angeles Time”.

StandaloneQuarter

Option Description
.oneDigit Standalone one-digit numeric quarter. For example 2.
.twoDigits Two-digit standalone numeric quarter with zero padding if necessary, for example 02.
.abbreviated Standalone abbreviated quarter. For example Q2.
.wide Standalone wide quarter. For example “2nd quarter”.
.narrow Standalone narrow quarter. For example “2”.

StandaloneMonth

Option Description
.defaultDigits Stand-alone minimum digits numeric month. Number/name (intended to be used without Day). For example, 9, 12.
.twoDigits Stand-alone two-digit numeric month. Two digits, zero pad if needed. For example, 09, 12.
.abbreviated Stand-alone abbreviated month.For example, “Sep”.
.wide Stand-alone wide month. For example, “September”.
.narrow Stand-alone narrow month. For example, “S”.

StandaloneWeekday

Option Description
.oneDigit Standalone local day of week number/name.
.abbreviated Standalone local day of week number/name. For example, “Tue”.
.wide Standalone wide local day of week number/name.For example, “Tuesday”.
.narrow Standalone narrow local day of week number/name. For example, “T”.
.short Standalone short local day of week number/name. For example, “Tu”.

VerbatimHour

Hour symbols that does not take users’ preferences into account, and is displayed as-is.

Each accepts an HourCycle and a Clock.

.HourCycle Description
.zeroBased The hour ranges from 0 to 11 in a 12-hour clock. Ranges from 0 to 23 in a 24-hour clock.
.oneBased The hour ranges from 1 to 12 in the 12-hour clock. Ranges from 1 to 24 in a 24-hour clock.
.Clock Description
.twelveHour In a 12-hour clock system, the 24-hour day is divided into two periods, a.m. and p.m, and each period consists of 12 hours.
- Note: Does not include the period marker (AM/PM). Specify a PeriodSymbol if that’s desired.
.twentyFourHour In a 24-hour clock system, the day runs from midnight to midnight, dividing into 24 hours.
- Note: If using twentyFourHour together with PeriodSymbol, the period is ignored.
Option Description
.defaultDigits(clock:, hourCycle:) Minimum digits to show the numeric hour. For example, 1, 12.
Or 23 if using the twentyFourHour clock.
- Note: This format does not take user’s locale preferences into account. Consider using defaultDigits if applicable.
.twoDigits(clock:, hourCycle:) Numeric two-digit hour, zero padded if necessary.
For example, 01, 12.
Or 23 if using the twentyFourHour clock.
- Note: This format does not take user’s locale preferences into account. Consider using defaultDigits if applicable.