iOS and macOS contain built in support for various ways to format dates, such as for human readable text and to show intervals between dates.
At WWDC19, Apple added a new RelativeDateTimeFormatter
, which formats relative dates from the current date, for example by formatting a past date as "X days ago" or today as "today".
This guide explores how to display and format dates using many of the different date formatters built into Apple's platforms.
Setup
As all of the built in date formatters are available in Apple's Foundation framework, no installation is required to use them. However, make sure to add import Foundation
to the top of your file to be able to use them in your code.
If you'd like to follow along with this guide, just create a Swift Playground using Xcode or the Swift Playgrounds app for iPad.
Additionally, you can click any of the class or header names to go to Apple's official Developer Documentation.
Formatting Basic Dates
Formatting basic dates is easy with DateFormatter
. It supports many different time and date styles, allowing you to get the format that you want.
Just create a formatter and set its time and date style to use it:
let formatter = DateFormatter()
formatter.timeStyle = .short
formatter.dateStyle = .short
let currentDate = Date()
formatter.string(from: currentDate) // 21/7/2019, 9:41 AM
DateFormatter
can be used with either a date or time style, or both.
Date Styles
None
The .none
style ignores the date and only uses the time style.
formatter.dateStyle = .none
formatter.string(from: currentDate)
Short
The .short
style only uses numbers without text.
formatter.dateStyle = .short
formatter.string(from: currentDate) // 21/7/2019
Medium
The .medium
style is more human readable and uses the short name of the month.
formatter.dateStyle = .medium
formatter.string(from: currentDate) // 21 Jul 2019
Long
The .long
style is the same as the medium style but slightly longer, using the full name of the month.
formatter.dateStyle = .long
formatter.string(from: currentDate) // 21 July 2019
Full
The .full
style displays the entire date, including the name of the week.
formatter.dateStyle = .full
formatter.string(from: currentDate) // Sunday, 21 July 2019
Time Styles
None
The .none
style ignores the time and only uses the date style.
formatter.timeStyle = .none
formatter.string(from: currentDate)
Short
The .short
style is the simplest time style and just shows the hours, minutes, and whether it is currently AM or PM.
formatter.timeStyle = .short
formatter.string(from: currentDate) // 9:41 AM
Medium
The .medium
style is the same as the short style but also displays seconds.
formatter.timeStyle = .medium
formatter.string(from: currentDate) // 9:41:00 AM
Long
The .long
style is the same as the short style but also displays the time zone.
formatter.timeStyle = .long
formatter.string(from: currentDate) // 9:41:00 AM GMT+2
Full
The .full
style displays everything from the other time styles as well as the entire name of the time zone.
formatter.timeStyle = .full
formatter.string(from: currentDate) // 9:41:00 AM Central European Summer Time
Formatting DateComponents
DateComponentsFormatter
formats DateComponents
instances, which contain dates and times specified in terms of units.
Just create a formatter and pass an instance of DateComponents
, and the formatter will represent the components the best it can.
let formatter = DateComponentsFormatter()
let components = DateComponents(hour: 9, minute: 41)
formatter.string(from: components) // 9:41
Unit Styles
DateComponentsFormatter
contains five unit styles, which format the date components in different ways.
formatter.unitsStyle = .positional // .positional, .abbreviated, .short, .full or .spellOut
Positional
The .positional
style is the default style, and uses typical date position formatting instead of separating each component like the abbreviated style.
formatter.unitsStyle = .positional
formatter.string(from: components) // 9:41
Abbreviated
The .abbreviated
style is the shortest style apart from the positional style and abbreviates the components as much as possible.
formatter.unitsStyle = .abbreviated
formatter.string(from: components) // 9h 41m
Short
The .short
style uses slightly longer versions of each date component.
formatter.unitsStyle = .short
formatter.string(from: components) // 9hr 41min
Full
The .full
style uses the full name for each date component.
formatter.unitsStyle = .full
formatter.string(from: components) // 9 hours, 41 minutes
Spelled Out
The .spellOut
style is the longest style and uses spelled out versions of everything.
formatter.unitsStyle = .spellOut
formatter.string(from: components) // Nine hours, forty-one minutes
Formatting Date Intervals
DateIntervalFormatter
is similar to the basic DateFormatter
, but it displays both a beginning and end date, such as 21-26 July 2019
.
To use it, create a formatter and generate a string from two dates:
let formatter = DateIntervalFormatter()
let currentDate = Date()
let twoMinutesAgo = Calendar.current.date(byAdding: .minute, value: -2, to: currentDate) ?? currentDate
formatter.string(from: currentDate, to: twoMinutesAgo) // 21/7/2019, 9:41-9:43 AM
let fiveDaysAway = Calendar.current.date(byAdding: .day, value: 5, to: currentDate) ?? currentDate
formatter.string(from: currentDate, to: fiveDaysAway) // 21/7/2019, 9:41 AM - 26/7/2019, 9:43 AM
Note that by default, the output will be based on the locale and time style from the device preferences, so you might see something different.
Date Styles
DateIntervalFormatter
uses the same date styles that the basic date formatter does. Here are examples of each style:
formatter.dateStyle = .none
formatter.string(from: currentDate, to: fiveDaysAway)
formatter.dateStyle = .short
formatter.string(from: currentDate, to: fiveDaysAway) // 21/7/2019-26/7/2019
formatter.dateStyle = .medium
formatter.string(from: currentDate, to: fiveDaysAway) // 21-26 Jul 2019
formatter.dateStyle = .long
formatter.string(from: currentDate, to: fiveDaysAway) // 21-26 July 2019
formatter.dateStyle = .full
formatter.string(from: currentDate, to: fiveDaysAway) // Sunday, 21-Friday, 26 July 2019
Time Styles
DateIntervalFormatter
uses the same time styles that the basic date formatter does. Here are examples of each style:
formatter.timeStyle = .none
formatter.string(from: currentDate, to: twoMinutesAgo)
formatter.timeStyle = .short
formatter.string(from: currentDate, to: twoMinutesAgo) // 11:00-11:02 AM
formatter.timeStyle = .medium
formatter.string(from: currentDate, to: twoMinutesAgo) // 11:00:00 AM-11:02:00 AM
formatter.timeStyle = .long
formatter.string(from: currentDate, to: twoMinutesAgo) // 11:00:00 AM GMT+2-11:02:00 AM GMT+2
formatter.timeStyle = .full
formatter.string(from: currentDate, to: twoMinutesAgo) // 11:00:00 AM Central European Summer Time-11:02:00 AM Central European Summer Time
Formatting Relative Dates
Note:
RelativeDateTimeFormatter
requires Xcode 11 and the latest beta versions of macOS 10.15 or iOS 13, which are currently in beta, as it is not available on previous versions.
RelativeDateTimeFormatter
is a new date formatter from WWDC19 that formats relative dates as the amount of time between two dates or according to the current date and time.
To use it, create a formatter and generate a string from two dates.
RelativeDateTimeFormatter
supports different date/time and unit styles, which we'll go through later on.
let formatter = RelativeDateTimeFormatter()
formatter.dateTimeStyle = .named
formatter.unitsStyle = .full
let currentDate = Date()
formatter.localizedString(for: currentDate, relativeTo: currentDate) // now
let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: currentDate) ?? currentDate
formatter.localizedString(for: yesterday, relativeTo: currentDate) // yesterday
let fiveDays = Calendar.current.date(byAdding: .day, value: 5, to: currentDate) ?? currentDate
formatter.localizedString(for: fiveDays, relativeTo: currentDate) // in 5 days
You can also use a instance of DateComponents
, which will format it based on the current date.
let minusOneDay = DateComponents(day: -1)
formatter.localizedString(from: minusOneDay) // yesterday
let plusOneDay = DateComponents(day: 1)
formatter.localizedString(from: plusOneDay) // tomorrow
As a bonus, all the strings that are generated are already localized, so you don't need to worry about localization.
Date/Time Styles
RelativeDateTimeFormatter
supports two different date/time styles, which can be used by setting them before generating the string.
formatter.dateTimeStyle = .numeric // .numeric or .named
Numeric
The .numeric
style is the default style and always uses the literal definition of the date.
formatter.dateTimeStyle = .numeric
formatter.localizedString(for: currentDate, relativeTo: currentDate) // in 0 seconds
formatter.localizedString(for: yesterday, relativeTo: currentDate) // 1 day ago
formatter.localizedString(for: fiveDays, relativeTo: currentDate) // in 5 days
Named
The .named
style falls back to the numeric style, but when possible, uses relative names such as yesterday
or tomorrow
.
formatter.dateTimeStyle = .named
formatter.localizedString(for: currentDate, relativeTo: currentDate) // now
formatter.localizedString(for: yesterday, relativeTo: currentDate) // yesterday
formatter.localizedString(for: fiveDays, relativeTo: currentDate) // in 5 days
Unit Styles
RelativeDateTimeFormatter
contains four different unit styles, which format the date in different ways.
formatter.unitsStyle = .full // .abbreviated, .short, .full or .spellOut
Abbreviated
The .abbreviated
style is the shortest style and abbreviates as much as possible.
formatter.unitsStyle = .abbreviated
let oneMonthAgo = Calendar.current.date(byAdding: .month, value: 1, to: currentDate) ?? currentDate
formatter.localizedString(for: currentDate, relativeTo: oneMonthAgo) // 1 mo. ago
Short
The .short
style is identical to the abbreviated style in English, but might generate different strings in other languages.
formatter.unitsStyle = .short
let oneMonthAgo = Calendar.current.date(byAdding: .month, value: 1, to: currentDate) ?? currentDate
formatter.localizedString(for: currentDate, relativeTo: oneMonthAgo) // 1 mo. ago
Full
The .full
style is the default style and uses longer names, such as month
instead of mo.
.
formatter.unitsStyle = .full
let oneMonthAgo = Calendar.current.date(byAdding: .month, value: 1, to: currentDate) ?? currentDate
formatter.localizedString(for: currentDate, relativeTo: oneMonthAgo) // 1 month ago
Spelled Out
The .spellOut
style is the longest style and uses spelled out versions of everything.
formatter.unitsStyle = .spellOut
let oneMonthAgo = Calendar.current.date(byAdding: .month, value: 1, to: currentDate) ?? currentDate
formatter.localizedString(for: currentDate, relativeTo: oneMonthAgo) // one month ago
Conclusion
In this guide, you learned about the different date formatters that are built into Apple's platforms, and how to use them.
I hope this post was useful and taught you something new. If you have any questions or feedback, feel free to mention me on Twitter or email me: [email protected]. Thanks for reading 📆