Click the diagram to see it at full size.
In the previous article in this series, we looked at three key structs for date and time programming in Swift:
Date
represents a single point in time, using a format that can easily be translated into just about any calendar and time-reckoning system: a number of seconds relative to the start of the Third Millennium (January 1, 2001, 00:00:00 UTC).DateComponents
specifies time units like year, month, day, hour, minute, and more to represent either a point in time or a duration of time.Calendar
provides a context forDate
s, and convertsDate
s toDateComponents
andDateComponents
toDate
s.
These structs all deal with the internal representation of dates and times. In this article, we’ll look at the DateFormatter
class, which allows us to deal with their external representation — by converting Date
s into String
s, and properly-formatted String
s into Date
s.
Let’s convert a Date
into a String
, part 1: Just the date
Start a new playground and enter the following code, which gives us a Date
that we can format — the day when Alexander Graham Bell made the very first phone call:
// The user's calendar incorporates the user's locale and // time zone settings, which means it's the one you'll use // most often. let userCalendar = Calendar.current // On March 10, 1876, Alexander Graham Bell // made the first land line phone call. var firstLandPhoneCallDateComponents = DateComponents() firstLandPhoneCallDateComponents.year = 1876 firstLandPhoneCallDateComponents.month = 3 firstLandPhoneCallDateComponents.day = 10 let firstLandPhoneCallDate = userCalendar.date(from: firstLandPhoneCallDateComponents)! // The value "Mar 10, 1876, 12:00 AM" should appear in the // playground sidebar. firstLandPhoneCallDate
Now let’s try turning this date into a string with a DateFormatter
:
let myFormatter = DateFormatter() myFormatter.string(from: firstLandPhoneCallDate) // What gives?
You may be surprised that the result is an empty String
. This can be fixed by specifying a dateStyle
:
let myFormatter = DateFormatter() myFormatter.string(from: firstLandPhoneCallDate) // What gives? myFormatter.dateStyle = .short myFormatter.string(from: firstLandPhoneCallDate) // "3/10/76"
Let’s try the other dateStyle
s:
let myFormatter = DateFormatter() myFormatter.string(from: firstLandPhoneCallDate) // What gives? myFormatter.dateStyle = .short myFormatter.string(from: firstLandPhoneCallDate) // "3/10/76" myFormatter.dateStyle = .medium myFormatter.string(from: firstLandPhoneCallDate) // "Mar 10, 1876" myFormatter.dateStyle = .long myFormatter.string(from: firstLandPhoneCallDate) // "March 10, 1876" myFormatter.dateStyle = .full myFormatter.string(from: firstLandPhoneCallDate) // "Friday, March 10, 1876" myFormatter.dateStyle = .none myFormatter.string(from: firstLandPhoneCallDate) // ""
Why would there be a dateStyle
called .none
? I’ll explain in a little bit.
Let’s convert a Date
into a String
, part 2: A date and a time
Let’s work with an event for which we know both the date and time: the “Stevenote” where the iPad was introduced, which started on January 27, 2010, at 10:00 a.m. Pacific Time (UTC-8). We’ll define this as a Date
by adding the following code:
// (Previous code goes here) // The Stevenote where the iPad was introduced took place on // January 27, 2010 at 10:00 a.m. Pacific time. var iPadStevenoteDateComponents = DateComponents() iPadStevenoteDateComponents.year = 2010 iPadStevenoteDateComponents.month = 1 iPadStevenoteDateComponents.day = 27 iPadStevenoteDateComponents.hour = 10 iPadStevenoteDateComponents.minute = 0 iPadStevenoteDateComponents.timeZone = TimeZone(identifier: "America/Los_Angeles") let iPadStevenoteDate = userCalendar.date(from: iPadStevenoteDateComponents)!
Now that we have a date and time, let’s format it using the dateStyle and timeStyle properties:
// (Previous code goes here) myFormatter.dateStyle = .short myFormatter.timeStyle = .short myFormatter.string(from: iPadStevenoteDate) // "1/27/10, 1:00 PM" myFormatter.dateStyle = .medium myFormatter.timeStyle = .medium myFormatter.string(from: iPadStevenoteDate) // "Jan 27, 2010, 1:00:00 PM" myFormatter.dateStyle = .long myFormatter.timeStyle = .long myFormatter.string(from: iPadStevenoteDate) // "January 27, 2010 at 1:00:00 myFormatter.dateStyle = .full myFormatter.timeStyle = .full myFormatter.string(from: iPadStevenoteDate) // "Wednesday, January 27, 2010 at 1:00:00 PM Eastern Standard Time"
Now that we’re working with a date and time, let’s see what the .none style is for:
// (Previous code goes here) // Show only the time myFormatter.dateStyle = .none myFormatter.timeStyle = .full myFormatter.string(from: iPadStevenoteDate) // "1:00:00 PM Eastern Standard Time" // Show only the date myFormatter.dateStyle = .full myFormatter.timeStyle = .none myFormatter.string(from: iPadStevenoteDate) // "Wednesday, January 27, 2010"
Remember that in Swift, the Date
struct represents a single point in time, which has both a date and a time. The .none
style for DateFormatter
‘s dateStyle
and timeStyle
properties allows us to create a String
representation of a Date
that shows only its date or time part.
Let’s convert a Date
into a String
, part 3: Custom date/time formats
Before we begin working with custom date/time formats, I should point out that if you need to display a Date
as a String
to the user, it’s best if you use Swift’s built-in dateStyle
and timeStyle
values. They display dates and times properly, according to the user’s settings, which include country and language. You’d be surprised how date formats differ from culture to culture, and it’s better to let Swift do the formatting work.
However, there are times when you need to format dates and times in a specific way that doesn’t match the styles provided by DateFormatter
‘s dateStyle
and timeStyle
properties, such as when dealing with certain APIs. That’s where DateFormatter
‘s dateFormat
property comes in handy:
// (Previous code goes here) // Setting the locale to POSIX ensures that the user's locale // won't be used to format the Date. myFormatter.locale = Locale(identifier: "en_US_POSIX") // DateFormatter's format string uses the date format specifiers // spelled out in Unicode Technical Standard #35 (located at // http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns) myFormatter.dateFormat = "y-MM-dd" myFormatter.string(from: iPadStevenoteDate) // "2010-01-27"
You can use the date format specifiers listed in Appendix F of the Unicode Technical Standard #35 to define the formatting String
for the dateFormat
property. Here are some examples:
// (Previous code goes here) myFormatter.dateFormat = "'Year: 'y' Month: 'M' Day: 'd" myFormatter.string(from: iPadStevenoteDate) // "Year: 2010 Month: 1 Day: 27" myFormatter.dateFormat = "MM/dd/yy" myFormatter.string(from: iPadStevenoteDate) // "01/27/10" myFormatter.dateFormat = "MMM dd, yyyy" myFormatter.string(from: iPadStevenoteDate) // "Jan 27, 2010" myFormatter.dateFormat = "E MMM dd, yyyy" myFormatter.string(from: iPadStevenoteDate) // "Wed Jan 27, 2010" myFormatter.dateFormat = "EEEE, MMMM dd, yyyy' at 'h:mm a." myFormatter.string(from: iPadStevenoteDate) // "Wednesday, January 27, 2010 at 1:00 PM." myFormatter.dateFormat = "EEEE, MMMM dd, yyyy' at 'h:mm a zzzz." myFormatter.string(from: iPadStevenoteDate) // "Wednesday, January 27, 2010 at 1:00 PM Eastern Standard Time."
Let’s convert a String
into a Date
DateFormatter
works the other way — just as it can convert Date
s to String
s, it can also convert String
s to Date
s. By setting its dateFormat
to the format of the String
it should expect, you can use its date(from:)
method to convert a String
into a Date
:
// (Previous code goes here) myFormatter.dateFormat = "yyyy/MM/dd hh:mm Z" // Here's a date in the format specified by the string // assigned to dateFormat: let newDate1 = myFormatter.date(from: "2015/03/07 11:00 -0500") // Date - "Mar 7, 2015, 11:00 AM" // And here's the same date, but in a different format: let newDate2 = myFormatter.date(from: "Mar 7, 2015, 11:00:00 AM") // nil
Let’s change the dateFormat
string and try it again:
// (Previous code goes here) // Let's change the date format strings and try // date(from:) with the same two strings: myFormatter.dateFormat = "MMM d, yyyy, hh:mm a zz" myFormatter.date(from: "2015/03/07 11:00 -0500") // nil myFormatter.date(from: "Mar 7, 2015, 11:00 AM EST") // "Mar 7, 2015, 11:00 AM"
Wrapping it all up
Here’s a playground containing all the code we just worked with:
import UIKit // Let’s convert a Date into a String, part 1: Just the date // ========================================================= // The user's calendar incorporates the user's locale and // time zone settings, which means it's the one you'll use // most often. let userCalendar = Calendar.current // On March 10, 1876, Alexander Graham Bell // made the first land line phone call. var firstLandPhoneCallDateComponents = DateComponents() firstLandPhoneCallDateComponents.year = 1876 firstLandPhoneCallDateComponents.month = 3 firstLandPhoneCallDateComponents.day = 10 let firstLandPhoneCallDate = userCalendar.date(from: firstLandPhoneCallDateComponents)! // The value "Mar 10, 1876, 12:00 AM" should appear in the // playground sidebar. firstLandPhoneCallDate let myFormatter = DateFormatter() myFormatter.string(from: firstLandPhoneCallDate) // What gives? myFormatter.dateStyle = .short myFormatter.string(from: firstLandPhoneCallDate) // "3/10/76" myFormatter.dateStyle = .medium myFormatter.string(from: firstLandPhoneCallDate) // "Mar 10, 1876" myFormatter.dateStyle = .long myFormatter.string(from: firstLandPhoneCallDate) // "March 10, 1876" myFormatter.dateStyle = .full myFormatter.string(from: firstLandPhoneCallDate) // "Friday, March 10, 1876" myFormatter.dateStyle = .none myFormatter.string(from: firstLandPhoneCallDate) // "" // Let’s convert a Date into a String, part 2: A date and a time // ============================================================= // The Stevenote where the iPad was introduced took place on // January 27, 2010 at 10:00 a.m. Pacific time. var iPadStevenoteDateComponents = DateComponents() iPadStevenoteDateComponents.year = 2010 iPadStevenoteDateComponents.month = 1 iPadStevenoteDateComponents.day = 27 iPadStevenoteDateComponents.hour = 10 iPadStevenoteDateComponents.minute = 0 iPadStevenoteDateComponents.timeZone = TimeZone(identifier: "America/Los_Angeles") let iPadStevenoteDate = userCalendar.date(from: iPadStevenoteDateComponents)! myFormatter.dateStyle = .short myFormatter.timeStyle = .short myFormatter.string(from: iPadStevenoteDate) // "1/27/10, 1:00 PM" myFormatter.dateStyle = .medium myFormatter.timeStyle = .medium myFormatter.string(from: iPadStevenoteDate) // "Jan 27, 2010, 1:00:00 PM" myFormatter.dateStyle = .long myFormatter.timeStyle = .long myFormatter.string(from: iPadStevenoteDate) // "January 27, 2010 at 1:00:00 myFormatter.dateStyle = .full myFormatter.timeStyle = .full myFormatter.string(from: iPadStevenoteDate) // "Wednesday, January 27, 2010 at 1:00:00 PM Eastern Standard Time" // Show only the time myFormatter.dateStyle = .none myFormatter.timeStyle = .full myFormatter.string(from: iPadStevenoteDate) // "1:00:00 PM Eastern Standard Time" // Show only the date myFormatter.dateStyle = .full myFormatter.timeStyle = .none myFormatter.string(from: iPadStevenoteDate) // "Wednesday, January 27, 2010" // Let’s convert a Date into a String, part 3: Custom date/time formats // ==================================================================== // Setting the locale to POSIX ensures that the user's locale // won't be used to format the Date. myFormatter.locale = Locale(identifier: "en_US_POSIX") // DateFormatter's format string uses the date format specifiers // spelled out in Unicode Technical Standard #35 (located at // http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns) myFormatter.dateFormat = "y-MM-dd" myFormatter.string(from: iPadStevenoteDate) // "2010-01-27" myFormatter.dateFormat = "'Year: 'y' Month: 'M' Day: 'd" myFormatter.string(from: iPadStevenoteDate) // "Year: 2010 Month: 1 Day: 27" myFormatter.dateFormat = "MM/dd/yy" myFormatter.string(from: iPadStevenoteDate) // "01/27/10" myFormatter.dateFormat = "MMM dd, yyyy" myFormatter.string(from: iPadStevenoteDate) // "Jan 27, 2010" myFormatter.dateFormat = "E MMM dd, yyyy" myFormatter.string(from: iPadStevenoteDate) // "Wed Jan 27, 2010" myFormatter.dateFormat = "EEEE, MMMM dd, yyyy' at 'h:mm a." myFormatter.string(from: iPadStevenoteDate) // "Wednesday, January 27, 2010 at 1:00 PM." myFormatter.dateFormat = "EEEE, MMMM dd, yyyy' at 'h:mm a zzzz." myFormatter.string(from: iPadStevenoteDate) // "Wednesday, January 27, 2010 at 1:00 PM Eastern Standard Time." // Let’s convert a String into a Date // ================================== myFormatter.dateFormat = "yyyy/MM/dd hh:mm Z" // Here's a date in the format specified by the string // assigned to dateFormat: myFormatter.date(from: "2015/03/07 11:00 -0500") // Date - "Mar 7, 2015, 11:00 AM" // And here's the same date, but in a different format: myFormatter.date(from: "Mar 7, 2015, 11:00 AM EST") // nil // Let's change the date format strings and try // date(from:) with the same two strings: myFormatter.dateFormat = "MMM d, yyyy, hh:mm a zz" myFormatter.date(from: "2015/03/07 11:00 -0500") // nil myFormatter.date(from: "Mar 7, 2015, 11:00 AM EST") // "Mar 7, 2015, 11:00 AM"
In the next installment, we’ll look at date calculations.