Now that we know a bit about Date and DateComponents, let’s actually get a readable date in our Swift app, shall we? This is the job of DateFormatter. DateFormatter is a class that can take a Date, and output a String describing that time/date as its format instructions dictate. In other words, you tell it what you want to know, and it will give you a String that says that. It has a few pretty useful built-in formats, and the capability to accept a custom date format string.
There’s not too much to the actual creation of an DateFormatter object. It pretty much just has an empty initializer (and an NSCoding one, but we aren’t covering NSCoding today). After initializing your DateFormatter, you set the appropriate style for the date and time, and that’s all you need.
Built-In DateFormatter.Style Values
As mentioned earlier, there are some built in formats used for date and time. Both the dateStyle and timeStyle properties take a value from the DateFormatter.Style enumeration. They interpret the enumeration differently, but they are expressive enough to be shared between them. Below is an example of what the various DateFormatter.Styles do for their respective output. These examples are for an en-US locale:
DateFormatter.Style | Date Out | Time Out |
ShortStyle | 12/25/19 | 7:00 AM |
MediumStyle | Dec 25, 2019 | 7:00:00 AM” |
LongStyle | December 25, 2019 | 7:00:00 AM PST |
FullStyle | Wednesday, December 25, 2014 | 7:00:00 AM Pacific Standard Time |
Here’s a bit of sample code to create the above Date (using DateComponents, which you can read about in the previous post DateComponents — Class Reference), and then using DateFormatter in Swift:
let morningOfChristmasComponents = DateComponents(calendar: Calendar.current, year: 2019, month: 12, day: 25, hour: 7, minute: 0, second: 0) let morningOfChristmas = morningOfChristmasComponents.date! /***** DateFormatter Part *****/ let formatter = DateFormatter() formatter.dateStyle = .long formatter.timeStyle = .medium let dateString = formatter.string(from: morningOfChristmas) //dateString now contains the string: //"December 25, 2019 at 7:00:00 AM"
As you can see, you can set the dateStyle and timeStyle as different values. Also, like every use of enumerations in Swift, you can also use type inference and not write the enumeration’s name each time if the property it is set to is already typed as that enum. After that, you just call the stringFromDate instance method of the DateFormatter object you created, giving the date you want it to format as an argument, and it will return a String containing that date in a human-readable format the way you told it to.
Custom Fixed DateFormatter Styles
To be honest, I’ve pretty much only used the built-in DateFormatter.Styles in what I’ve worked on, they did what I needed. However, if you do want to do something different, you can write your own custom format string. This string will be a series of characters that DateFormatter knows are stand-ins for the values you want to show, and then replace them with the appropriate values.
The character patterns used in these format strings are based on the Unicode Technical Standard #35. Apple’s documentation for date formatting strings (available at Data Formatting Guide: Date Formatters) lists which revision of the standard is used for each iOS and OS X version up to OS X 10.9 and iOS 7. This has still not been updated to any later versions of the Operating systems, so I’m going to assume for now that it still is working with tr35-31.
As a side note, it would APPEAR that they are using at least revision 35. After some sleuthing, it responds correctly to the “r” formatter, which did not appear until revision 35. I don’t think there is much different for the Gregorian calendar though, so for now, I will write this assuming they are still using revision 31 at tr35-31.
So, basically, if you wanted to say something like, “Wednesday, 7 AM”, you would do something like this:
let dayTimePeriodFormatter = DateFormatter() dayTimePeriodFormatter.dateFormat = "EEEE, h a" let smallDateString = dayTimePeriodFormatter.string(from:morningOfChristmas) //smallDateString now contains the string "Wednesday, 7 AM".
Below I have included a chart of what I think will be the most useful date field symbols. These are written from the perspective of an “en-US” locale with the Gregorian calendar. If you want to see the full list, you can see it at the UTS #35-31: Unicode LDML: Dates.
Format | Description | Example |
“y” | A year with at least 1 digit. |
1 AD → “1” 42 AD → “42” 2014 AD → “2014” |
“yy” |
A year with exactly 2 digits. If less, it is padded with a zero. It will be truncated to the tens digit if larger. |
1 AD → “01” 42 AD → “42” 2014 AD → “14” |
“yyy” |
A year with at least 3 digits. If less, it is padded with zeros. |
1 AD → “001” 42 AD → “042” 2014 AD → “2014” |
“yyyy” |
A year with at least 3 digits. If less, it is padded with zeros. |
1 AD → “0001” 42 AD → “0042” 2014 AD → “2014” |
“M” | A month with at least 1 digit. |
July → “7” December → “12” |
“MM” |
A month with at least 2 digits. If less, it is padded with zeros. |
July → “07” December → “12” |
“MMM” | Three letter month abbreviation. |
July → “Jul” December → “Dec” |
“MMMM” | Full name of month. |
July → “July” December → “December” |
“MMMMM” |
One letter month abbreviation. Not unique, January, June, and July are all “J”. |
July → “J” December → “D” |
“d” | A day with at least 1 digit. |
4 → “4” 25 → “25” |
“dd” |
A day with at least 2 digits. If less, it is padded with a zero. |
4 → “04” 25 → “25” |
“E”, “EE”, or”EEE” | 3 letter day abbreviation of day name. |
Wednesday → “Wed” Thursday → “Thu” |
“EEEE” | Full day name. |
Wednesday → “Wednesday” Thursday → “Thursday” |
“EEEEE” |
1 letter day abbreviation of day name. Not unique, Tuesday and Thursday are both “T”. |
Wednesday → “W” Thursday → “T” |
“EEEEEE” | 2 letter day abbreviation of day name. |
Wednesday → “We” Thursday → “Th” |
“a” | Period of day (AM/PM). |
5 PM → “PM” 7 AM → “AM” |
“h” | A 1-12 based hour with at least 1 digit. |
5 PM → “5” 7 AM → “7” |
“hh” |
A 1-12 based hour with at least 2 digits. If less, it is padded with a zero. |
5 PM → “05” 7 AM → “07” |
“H” | A 0-23 based hour with at least 1 digit. |
5 PM → “17” 7 AM → “7” |
“HH” |
A 0-23 based hour with at least 2 digits. If less, it is padded with a zero. |
5 PM → “17” 7 AM → “07” |
“m” | A minute with at least 1 digit. |
40 → “40” 1 → “1” |
“mm” |
A minute with at least 2 digits. If less, it is padded with a zero. |
40 → “40” 1 → “01” |
“s” | A second with at least 1 digit. |
40 → “40” 1 → “1” |
“ss” |
A second with at least 2 digits. If less, it is padded with a zero. |
40 → “40” 1 → “01” |
“S” | Tenths place of fractional second. |
123 ms → “1” 7 ms → “0” |
“SS” |
Tenths and hundredths place of a fractional second. |
123 ms → “12” 7 ms → “00” |
“SSS” |
Tenths, hundredths, and thousandths place of a fractional second. |
123 ms → “123” 7 ms → “007” |
Two things to note about the fractional second. First, it does not round, it just shows the appropriate places (otherwise the 2 digit fractional second would show 7 ms as “01”). Secondly, the Unicode standard lets you use as many as you want, so “SSSSS” is valid. However, DateFormatter will not show anything below milliseconds, it will just show 0s below that. So 123456789 nanoseconds with a “SSSSSSSSS” format string will show up as “123000000”.
One major thing to note, there is a difference between “yyyy” and “YYYY”. In most cases, you probably want to use the lowercase one, “yyyy”. The uppercase one is the “Week of Year” style of year. If you’re curious about the “Week of Year” style of year, it is based off of something called the ISO Week Date, which is part of the ISO 8601 standard.
Suffice it to say though, you want to usually use “yyyy” in your DateFormatter, unless you are writing a Swift app for an institution that needs to use the ISO Week Date.
Now, there is one issue with using the fixed format style of custom formats. The reason they are called “fixed formats”, is because they print out exactly what you say, in the same order. That’s all well and good in your locale and calendar system, but (especially with the short date style) 06/02/2014 and 02/06/2014 are very different dates. One is the day Swift was released, the other is Rick Astley’s 48th birthday. If I gave a custom format string of “MM/dd/yyyy” to show Swift’s birthday to app users in Great Britain, they’ll be wondering if I Rickrolled them.
Luckily, Apple built-in a great method to DateFormatter to help us avoid this accidental Rickrolling.
The dateFormatFromTemplate method
You tell this DateFormatter class method what components you want, using the same format strings as used in the fixed format, and a locale, and it will give you a date format string that is appropriate for your locale. A very similar example of this was done in Apple’s Documentation: Data Formatting Guide: Date Formatters. So, let’s tell it to generate the appropriate format strings for the en-US locale, and the en-GB locale:
let usDateFormat = DateFormatter.dateFormat(fromTemplate: "MMddyyyy", options: 0, locale: Locale(identifier: "en-US")) //usDateFormat now contains an optional string "MM/dd/yyyy". let gbDateFormat = DateFormatter.dateFormat(fromTemplate: "MMddyyyy", options: 0, locale: Locale(identifier: "en-GB")) //gbDateFormat now contains an optional string "dd/MM/yyyy"
There are a few things to notice about this method. Firstly, to repeat, this is a class method, so it is being called on the DateFormatter class itself, not a specific instance of it.
Secondly, the template used is the same. It can be in any order, I just chose my default of the US order. It just needs to know what components you want, it will figure out what to do with them based on the locale specified.
Thirdly, ignore the options method. It appears to be there in case options are needed later, but according to the docs, there are no options defined yet, and you should just give it 0.
Fourthly, you probably would use Locale.current for the locale parameter. Unless you are trying to show how different locales and calendars would show the date provided, the “current” constant will show what makes sense for that user’s iOS device. I just hard coded it here to show it explicitly in the code what it was set to.
Finally, this actually returns an optional string, probably in case it cannot make a suitable dateFormat String from the template provided.
So now, we can use this like we used the fixed custom format string, by setting an instance of DateFormatter’s dateFormat property to it:
formatter.dateFormat = usDateFormat let usSwiftDayString = formatter.string(from: swiftDay) // usSwiftDayString now contains the string "06/02/2014". formatter.dateFormat = gbDateFormat let gbSwiftDayString = formatter.string(from: swiftDay) // gbSwiftDayString now contains the string "02/06/2014".
There, now I won’t accidentally Rickroll my friends at @SwiftLDN, or other readers in Great Britain.
Conclusion
After realizing the inverted date of Swift’s announcement was Rick Astley’s birthday, I had trouble not using a Rick Astley song title pun as the title to this post.
Much like how DateComponents made it a lot easier to work with Dates in your Swift code, with setting the individual components directly, DateFormatter gives you a lot of power in how you want an Date to appear to your Swift app’s users. There is even more power than what I showed here, but I felt that the chart would be big enough without including less used components like the quarter or theISO Week Date style of years. Nonetheless though, you can check those out at the Unicode Standard to get the rest of the values if you need them for your instance of DateFormatter.
This of course was the Swift version of how to use DateFormatter. If you want to see how to use it in Objective-C, check out the previous post Displaying a human readable NSDate.
I hope you found this article helpful. If you did, please don’t hesitate to share this post on Twitter or your social media of choice, every share helps. Of course, if you have any questions, don’t hesitate to contact me on the Contact Page, or on Twitter @CodingExplorer, and I’ll see what I can do. Thanks!