Formatting dates in Swift using Date.FormatStyle on iOS 15
Published on: May 27, 2022Working with dates isn’t easy. And showing them to your users in the correct locale hasn’t always been easy either. With iOS 15, Apple introduced a new way to convert Date
objects from and to String
. This new way comes in the form of the new Formatter
api that replaces DateFormatter
.
As any seasoned iOS developer will tell you, DateFormatter
objects are expensive to create, and therefor kind of tedious to manage correctly. With the new Formatter
api, we no longer need to work with DateFormatter
. Instead, we can ask a date to format itself based on our requirements in a more performant, easier to use way.
In this post I will show you how you can convert Date
objects to String
as well as how you can extract a Date
from a String
.
Converting a Date to a String
The most straightforward way to convert a Date
to a String
is the following:
let formatted = Date().formatted() // 5/26/2022, 7:52 PM
By default, the formatted()
function uses a compact configuration for our String
. The way formatted()
converts our Date
to String
takes into account the user’s current locale. For example, if my device was set to be in Dutch, the date would be formatted as 26-5-2022 19:54
which is a more appropriate formatting for the Dutch language.
However, this might not always be what we need. For example, we might want to have our date formatted as May 26 2022, 7:52 PM
. We can use the following code to do that:
let formatted = Date().formatted(
.dateTime
.day().month(.wide).year()
.hour().minute()
)
Let’s break this code apart a bit. The formatted
function takes an object that conforms to the FormatStyle
protocol as its argument. There are various ways for us to create such an object. The FormatStyle
protocol has several convenient extensions that can provide us with several different formatters.
For example, when sending a Date
to a server, we’ll often need to send our dates as ISO8601 compliant strings. Before I explain the code you just saw, I want to show you how to grab an ISO8601 compliant string from the current Date
.
let formatted = Date().formatted(.iso8601) // 2022-05-26T18:06:55Z
Neat, huh?
Okay, back to the example from before. The .datetime
formatter is used as a basis for our custom formatting. We can call various functions on the object that’s returned by the .datetime
static property to select the information that we want to show.
Some of these properties, like the month, can be configured to specify how they should be formatted. In the case of .month
, we can choose the .wide
formatting to spell out the full month name. We could use .narrow
to abbreviate the month down to a single letter, or we could use one of the other options to represent the month in different ways.
If you omit a property, like for example .year()
, our formatted date will omit the year that’s embedded in the Date
. And again, the underlying formatter will always automatically respect your user’s locale which is really convenient.
Another way to format the date is to by specifying how you want the date and time to be formatted respectively:
let formatted = Date().formatted(date: .complete, time: .standard) // Thursday, May 26, 2022, 8:15:28 PM
The above provides a very verbose formatted string. We can make a more compact one using the following settings:
let formatted = Date().formatted(date: .abbreviated, time: .shortened) // May 26, 2022, 8:16 PM
It’s even possible to omit the date or time entirely by using the .omitted
option:
let formatted = Date().formatted(date: .abbreviated, time: .omitted) // May 26, 2022
There are tons of different combinations you could come up with so I highly recommend you explore this api some more to get a sense of how flexible it really is.
Creating a Date from a String
Converting String
to Date
is slightly less convenient than going from a Date
to a String
but it’s still not too bad. Here’s how you could cover the common case of converting an ISO8601 compliant string to a Date
:
let string = "2022-05-26T18:06:55Z"
let expectedFormat = Date.ISO8601FormatStyle()
let date = try! Date(string, strategy: expectedFormat)
We make use of the Date
initializer that takes a string and a formatting strategy that’s used to parse the string.
We can also use and configure an instance of FormatStyle
to specify the components that we expect to be present in our date string and let the system parse it using the user’s locale:
let string = "May 26, 2022, 8:30 PM"
let expectedFormat = Date.FormatStyle()
.month().year().day()
.hour().minute()
let date = try! Date(string, strategy: expectedFormat)
The order of our date components doesn’t matter; they will automatically be rearranged to match the user’s locale. This is super powerful, but it does mean that we can’t use this to parse dates on devices that use a different locale than the one that matches the string’s locale. The best locale agnostic date string is ISO8601 so if you have control over the date strings that you’ll parse, make sure you use ISO8601 when possible.
Summary
In this short article, you learned how you can use iOS 15’s FormatStyle
to work format Date
objects. You saw how to go from Date
to String
, and the other way around. While FormatStyle
is more convenient than DateFormatter
, it’s iOS 15 only. So if you’re still supporting iOS 14 you’ll want to make sure to check out DateFormatter
too.