Updated: December 2021
Since its introduction to the Apple development platform in 2014 (announced at WWDC 2014), Swift continues to mature rapidly due to its open development community (swift.org). As such, Swift code is clean code with clear consistent syntax and strongly typed, compiles to a fast executable, and includes many advanced design and coding principles. It is truly a great modern programming language!
Alas, developing code for real-world apps is hard and still a humbling experience. This post focuses on the insanely complex rabbit hole known as date and time and its implementation in Swift. Some of the complexity of getting date and time correct is humorously presented by Tom Scott in the following video.
An enlightening article by Dave Delong (Your Calendrical Fallacy Is …) lists some of the false assumptions regarding date and time including:
Are days always 24 hours long?
Does an hour ever occur twice in a single day?
Does every day have a midnight?
Are days in a work week always contiguous?
Does a year always consist of 12 months?
Does a leap year always occur every 4 years except when the year is evenly divisible by 100, unless it is also divisible by 400?
Does each year always have just one New Year’s day?
Are timezones always on the hour mark?
Do the rules for Daylight Saving Time change?
As referenced in the video, updates to the Timezone Database still occur. The recent updates (Jan 31, 2018) to the Timezone Database are highlighted in an article by Dave Delong (The 2018c Timezone Datebase Update). Ideally, time zones would be equally spaced around the earth every 15 degrees in longitude. Not even close! The following world map of current timezones highlights this complexity.
And, finally, the overall weirdness of calendars, dates and times is examined philosophically by Dave Delong (Intercalation).
Bottom-line: Use the blackbox approach. Always use Calendar, Date, DateFormatter, Locale, and DateComponents), which are all built on top of the ICU (International Components for Unicode) libraries.
The software examples in the next several sections are included as an Xcode playground located on GitHub.
Basic Date and Time
In Swift, a Date() value encapsulates a single point in time, independent of any particular calendrical system or time zone. To accomplish this, a Date() value is stored as a Double (64-bit floating point number) counting the number of seconds as a relative offset from the reference date of January 1, 2001 at 00:00:00 UTC (Coordinated Universal Time).
So let’s begin with getting the current date/time and printing out the default result as an ISO8061-formatted string referenced to UTC.
To see the actual number of seconds relative to Apple’s reference date (January 1, 2001 at 00:00:00 UTC), the code needs to be more specific. The fractional part of the result represents the fractional part of a second.
To see the date given an integer (e.g., truncated to one second resolution) representing the number of seconds relative to Apple’s reference date (January 1, 2001 at 00:00:00 UTC), the code to convert to a date is still straightforward.
Note to Unix users: To see the actual number of seconds relative to the Unix’s reference data known as ‘epoch date’ (January 1, 1970 at 00:00:00 UTC), the code needs to be specific with use of the ‘timeIntervalSince1970’ modifier.
Standard Date and Time Formatting
However, the real feature-rich functionality benefit in Swift comes from using DateFormatter, Locale, and Calendar. This is how Swift supports timezones, localized date formats, and calendar arithmetic (e.g., 1 month ago, 5 days later). To use DateFormatter, we need to create a DateFormatter object. Then, we can specify various date and time formatting options as shown in the examples below (based on en_US in Seattle).
The above options provide commonly used combinations of date and time formats. We can access just date only (timeStyle set to .none) or time only (dateStyle set to .none) as the following examples show (based on en_US in Seattle).
Localization of Date and Time
Now for the cool part. We can create a Locale object that will auto-magically localize the date and time formatted results.
In iOS, localization changes will cause an app restart. Typically, the DateFormatter will re-instantiate with an updated locale. However, we can do better and ensure auto-updating by adding the following code:
Custom Date and Time Formatting
In addition to the standard date and time formats associated with DateFormatter, custom date and time formats are entirely possible with much flexibility.
An extensive suite of iOS Swift custom formatting options is available at NSDataFormatter.
SwiftUI Date and Time
Beginning with iOS15, we get some simplified date/time formats without the need for a date formatter object. And we still have the existing date formatter and all its options.
Dates and Codable
Matching date/time string formats when coding with ISO8601 data is different depending on whether the date/time string format is in whole seconds or includes fractional seconds (i.e., milliseconds).
After spending some time learning about handling date and time in Swift and the Xcode development environment, the functionality that I needed for an app under development is relatively clean. I am especially pleased with the localization benefit with using Locale. It is important to always test both functionality and performance. When using DateFormatter, the recommendation for better performance is to create a single instance of DateFormatter. And, iOS 15 brings simplified date and time formatting to SwiftUI.