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 |
TheLocale
for the examples are allen_CA
(English, Canada) and are using the Gregorian Calendar.
You can access the date format style in two ways:
- Instantiate a new instance of the
Date.FormatStyle
struct - Use the
.dateTime
extension onFormatStyle
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’sLocale
and not the order that they are called.
Each symbol has customization options.
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"
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)
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:
// 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)) // "כ״ג בספט׳ ב׳כ״ג"
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.
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"
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"
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:
// 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"
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"
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"
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.
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"
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.
- 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. - 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)
}
}
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 theLocale
when constructing or calling this style, certain tokens will use their un-localized standard values. For exampleM02
instead ofFebruary
for\(month: .abbreviated)
.
Every conceivable piece of information inside of a Date
is available for use.
- Era
- Year
- YearForWeekOfYear
- CyclicYear
- Quarter
- Month
- Week
- Day
- DayOfYear
- Weekday
- DayPeriod
- Minute
- Second
- SecondFraction
- TimeZone
- StandaloneQuarter
- StandaloneMonth
- StandaloneWeekday
- VerbatimHour
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. |