developers

Introduction to Date and Time Programming in Swift, Part 1

Learn how to create dates and times using Swift’s powerful date and time objects.

Mar 30, 202333 min read

At first glance, working with dates and times in Swift seems unnecessarily complicated. While JavaScript manages to work with the

Date
class, Swift has this collection of classes and structs:

Chart showing the relationships between Swift’s date and time objects: Date, DateComponents, Calendar, DateFormatter, TimeZone, Locale, and String.

Some developers think that Swift’s system for working with dates and times is overengineered, but that’s because they hold mistaken beliefs about time. Swift provides a powerful system for storing, displaying, and performing calculations on dates and times that’s flexible enough to work across date and time formats, time zones, languages, and even different calendar systems.

For example, in a handful of lines of Swift code, you can determine the date for the third Wednesday of July at 3:30 p.m. Pacific Time and then display that date as it would appear in the Coptic calendar system in Melbourne, Australia’s time zone. In fact, the final task in this tutorial will be to write this code. (I don’t want to even think about what it would take to write the equivalent code in JavaScript.)

This tutorial will introduce you to the objects that Swift provides for working with dates and times. You’ll learn how to create date/time objects in Swift, extract information from them, and display their values to your users. It’s an interactive exercise that you’ll perform in a Swift playground so that you can learn by doing.

👀 Look for the 🛠 emoji if you’d like to skim through the content while focusing on the build and execution steps.

💻 You can find an Xcode playground with all the code in this article in this GitHub repository — it’s

intro-dates-times-swift-1.playground
.

Creating Dates: The Basics

A number line with an arrow pointing at “0” that says “Reference Date: January 1, 2001, 00:00:00 UTC and key dates in phone and iOS history.

Swift uses the

Date
struct to represent dates and times. Every
Date
instance stores a
Double
value representing seconds relative to the start of the Third Millennium — January 1, 2001, 00:00:00 UTC. Positive values represent dates and times after this reference point, while negative values represent dates and times before this reference point.

Creating an object representing the current date and time

Let’s create a

Date
instance representing the current date and time.

🛠️ Open Xcode, start a new blank macOS playground (I find them more reliable and less crash-prone than iOS playgrounds), then add and run the following code:

import Cocoa

let momentInTime = Date()        // Current date and time
print("momentInTime contains the current date and time!")
print("• momentInTime.description: \(momentInTime.description)")

Here’s how the output looked on my computer when I ran it:

momentInTime contains the current date and time!
• momentInTime.description: 2023-12-13 16:44:32 +0000

Date
’s
description
property returns the date and time value as a string specifying the date, time, and time zone stored in the
Date
instance. You’ll be running the code on a different day and time, so its output will be different. However, they’ll have the same “date-time-time zone” format. Note that the time zone is
+0000
, or 0 hours and 0 minutes apart from Coordinated Universal Time, or UTC for short.

Getting the value of a
Date

Date
has very few properties for extracting the date and time, and the values they provide aren’t convenient:

Date

property

What it does
timeIntervalSinceNow
Returns the number of seconds between the date and time represented by the
Date
instance and the current date and time. If the
Date
instance is in the past, this is a negative number.
timeIntervalSinceReferenceDate
Returns the number of seconds between the date and time represented by the
Date
instance and Swift’s reference date, January 1, 2001 at 00:00:00 UTC. If the
Date
instance represents a date and time before the reference date, this is a negative number.
timeIntervalSince1970
Returns the number of seconds between the date and time represented by the
Date
instance and the start of the Unix epoch, January 1, 1970 at 00:00:00 UTC. Many Unix-based systems and programming languages (such as JavaScript) use this as their reference date. If the
Date
instance represents a date and time before the start of the Unix epoch, this is a negative number.

🛠️ Try them out by adding the following to the playground and running it:

let secondsAgo = -momentInTime.timeIntervalSinceNow
print("• momentInTime happened \(secondsAgo) seconds ago,")
let secondsSinceReferenceDate = momentInTime.timeIntervalSinceReferenceDate
print("• \(secondsSinceReferenceDate) seconds since January 1, 2001,")
let secondsSinceUnixEpochDate = momentInTime.timeIntervalSince1970
print("• \(secondsSinceUnixEpochDate) seconds since January 1, 1970.")

The output should tell you that

momentInTime
happened:

  • a number of seconds since you ran the previous code snippet,
  • over 724 million seconds since January 1, 2001, and
  • over 1.7 billion seconds since January 1, 1970.

These values, while correct, aren’t meaningful to most people. The exercises in this tutorial will cover better ways of getting the values out of a

Date
instance.

Getting localized strings for
Date
objects

Date
has a
description(with:)
method that returns the date and time value as a string based on the
Locale
object that you pass to it. Think of
Locale
as a way of describing the language, culture, and conventions that should be used when presenting information to the user.

🛠️ Add this code to the playground and run it:

print("Date descriptions in different locales:")
// US English
print("• en-US: \(momentInTime.description(with: Locale(identifier: "en-US")))")
// UK English
print("• en-GB: \(momentInTime.description(with: Locale(identifier: "en-GB")))")
// Spanish (general)
print("• es: \(momentInTime.description(with: Locale(identifier: "es")))")
// Simplified Chinese
print("• zh-Hans: \(momentInTime.description(with: Locale(identifier: "zh-Hans")))")

You’ll see

momentInTime
displayed in four locale’s languages, in the format appropriate for that locale.
description(with:)
displays a
Date
instance’s date and time using the time zone of the system it’s running on instead of UTC.

Locale
’s
availableIdentifiers
property contains the identifiers for all the locales supported by iOS. These identifiers are also listed in this chart.

Creating dates and times the hard way

In addition to the default initializer,

Date
also has a set of initializers for creating dates and times by specifying the number of seconds before or after a reference date and time. They’re listed in the table below:

InitializerWhat it does
Date(timeIntervalSinceNow:)
Creates a
Date
representing the date and time for the given number of seconds before or after the current date and time.
Date(timeIntervalSinceReferenceDate:)
Creates a
Date
representing the date and time for the given number of seconds before or after Swift’s reference date, January 1, 2001 at 00:00:00 UTC.
Date(timeIntervalSince1970:)
Creates a
Date
representing the date and time for the given number of seconds before or after the start of the Unix epoch, January 1, 1970 at 00:00:00 UTC. Many Unix-based systems and programming languages (such as JavaScript) use this as their reference date.
Date(timeInterval:since:)
This one takes two arguments: a number of seconds and a
Date
instance. It creates a
Date
representing the date and time for the given number of seconds before or after the date and time represented by the given
Date
instance.

Let’s use these initializers to create

Date
objects for the following events:

  • 5 seconds in the past
  • 8 minutes in the future
  • The start of Steve Jobs’ keynote (“Stevenote”) where he introduced the first iPhone: January 9, 2007 at 10:00 a.m. Pacific Standard Time (UTC-8)
  • The start of Steve Jobs’ keynote where he introduced the first iPad: January 27, 2010 at 10:00 a.m. Pacific Standard Time (UTC-8)
  • The start of Tim Cook’s keynote (“Timnote”) where he introduced Apple Silicon: June 22, 2020 at 10:00 a.m. Pacific Daylight Time (UTC-7)

🛠️ Add the following to the playground and run it:

// We’ll use the user’s current locale over and over,
// so let’s put it into a handy variable.
// `Locale.autoupdatingCurrent` always contains the current locale,
// even when the user changes their settings.
var userLocale = Locale.autoupdatingCurrent

// Let’s create some dates by...

// ...specifying a number of seconds before or after the current time:
let fiveSecondsAgo = Date(timeIntervalSinceNow: -5)
let eightMinutesFromNow = Date(timeIntervalSinceNow: 8 * 60)
print("\nCreating dates and times the hard way:")
print("• 5 seconds ago, it was \(fiveSecondsAgo.description(with: userLocale)).")
print("• 8 minutes from now, it will be \(eightMinutesFromNow.description(with: userLocale)).")

// ...specifying the number of seconds before or after
// the start of the Third Millennium:
let iPhoneStevenoteSwiftInterval = 190_058_400.0
// (Swift ignores underscores in numbers, so we’re using them
// to group digits to make the number easier to read.)
var iPhoneStevenoteDate = Date(timeIntervalSinceReferenceDate: iPhoneStevenoteSwiftInterval)
print("• The iPhone Stevenote took place on \(iPhoneStevenoteDate.description(with: userLocale)).")

// ...specifying the number of seconds before or after
// the start of the Unix epoch:
let iPadStevenoteUnixInterval = 1_264_615_200.0
var iPadStevenoteDate = Date(timeIntervalSince1970: iPadStevenoteUnixInterval)
print("• The iPad Stevenote took place on \(iPadStevenoteDate.description(with: userLocale)).")

// ...specifying the number of seconds before or after
// another date. The Apple Silicon Timnote took place
// 328,230,000 seconds after the iPad Stevenote:
let appleSiliconTimnoteIPadStevenoteInterval = 328_230_000.0
var appleSiliconTimnoteDate = Date(
    timeInterval: appleSiliconTimnoteIPadStevenoteInterval,
    since: iPadStevenoteDate
)
print("• The Apple Silicon Timnote took place on \(appleSiliconTimnoteDate.description(with: userLocale)).")

You should see the dates and times for five seconds ago, eight minutes in the future, and for the keynotes announcing the original iPhone, the original iPad, and Apple Silicon. These dates and times will be expressed in your system’s local time, in a format determined by your system’s

Locale
settings.

The code works, but once again, most people don’t tell time in terms of seconds before or after a reference date. Let’s look at a more human-friendly way to define dates and times.

Creating dates and times the simpler way with
DateFormatter

The

DateFormatter
class works in conjunction with
Date
to do two things:

  • It converts strings containing various formats of date and time information into
    Date
    instances, and
  • it also creates string representations of
    Date
    instances in various formats.

Let’s use

DateFormatter
to create
Date
instances using parameters that are much easier to understand. Let’s use the date and time of “The Mother of All Demos,” where computer engineer Doug Englebart demonstrated so many elements of modern computing for the first time. It happened on December 9, 1968, at 3:45 p.m. Pacific Standard Time (UTC-8).

🛠️ Add this code to the playground and run it:

let myDateFormatter = DateFormatter()

// 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)
myDateFormatter.dateFormat = "MMMM d, y 'at' h:mm a, zzzz"

var motherOfAllDemosDate = myDateFormatter.date(from: "December 9, 1968 at 3:45 PM, Pacific Standard Time")!
print("\nCreating dates and times with DateFormatter:")
print("• The Mother of All Demos took place on \(motherOfAllDemosDate.description(with: userLocale)).")

When you run the playground, the last line of its output will be the date and time of the Mother of All Demos, displayed in your local time and system locale’s language and format.

Let’s take a closer look at the first two lines of the code you just entered:

let myDateFormatter = DateFormatter()
myDateFormatter.dateFormat = "MMMM d, y 'at' h:mm a, zzzz"

The first line is pretty straightforward: it creates a new instance of

DateFormatter
named
myDateFormatter
. I mention it only to point out that there’s some general agreement among Swift developers that creating
DateFormatter
objects is computationally expensive. If you need to use a
DateFormatter
often, it’s better to create a dedicated instance for each use case and reuse it when needed.

The second line specifies the format of the string that

myDateFormatter
should expect to convert into a
Date
. Here are the formatting strings used in setting
myDateFormatter
’s
dateFormat
property:

  • MMMM
    : The name of a month, spelled out in full. For locales whose language is English, this is one of a set of full month names from
    January
    through
    December
    .
  • d
    : The day of the month. A single
    d
    means that this can either be a single digit (e.g.:
    1
    ) or a double-digit with a leading zero for single-digit days (e.g.:
    01
    ).
  • y
    : The year. A single
    d
    means that this can either be a single digit long and up to four digits long. \
  • h
    : The hour. A single
    h
    means that this can either be a single digit (e.g.:
    1
    ) or a double-digit with a leading zero for single-digit hours (e.g.:
    01
    ).
  • mm
    : The minute. The double
    m
    means single-digit minutes should be zero-padded (e.g.:
    01
    ).
  • zzzz
    : The time zone name, spelled out in full (you may want to consult this list of time zone abbreviations and full names).

For a full list of the formatting strings for

DateFormatter
, consult the Unicode standard for specifying date format patterns.

Let’s look at the last two lines of that code:

var motherOfAllDemosDate = myDateFormatter.date(from: "December 9, 1968 at 3:45 PM, Pacific Standard Time")!
print("The Mother of All Demos took place on \(motherOfAllDemosDate.description(with: userLocale)).")

The first line uses

myDateFormatter
’s
date(from:)
method to create a
Date
based on the argument provided to the
from:
parameter. This method’s return type is the optional
Date?
, which means it returns a
Date
if the provided argument is in the expected format and can be converted into a valid
Date
, or
nil
otherwise.

The second line prints the date when the Mother of All Demos took place.

If you’re given the task of converting many date strings in some arbitrary format — perhaps your app has to process a CSV file or database table — you’ll find

DateFormatter
’s string-to-
Date
conversion capability very useful.

Creating dates and times in an even simpler way with
ISO8601DateFormatter

Given that there are several different formats for dates and times, we’re fortunate that many applications and services follow the ISO 8601 standard for expressing date and time information in string form.

InformationISO 8601 format
Date of the Mother of All Demos1968-12-09
Date and time of the Mother of All Demos in UTC1968-12-09T23:45:00Z
or
1968-12-09T23:45:00+00:00
Date and time of the Mother of All Demos in Pacific Standard Time (UTC-8)1968-12-09T15:45:00-08:00

ISO8601DateFormatter
is a class specifically for converting date strings in ISO 8601 format into
Date
instances and vice versa. Let’s redefine
motherOfAllDemosDate
using
ISO8601DateFormatter
.

🛠️ Add the following to the playground and run it:

let iso8601DateFormatter = ISO8601DateFormatter()
motherOfAllDemosDate = iso8601DateFormatter.date(from: "1968-12-09T15:45:00-08:00")!
print("\nOnce again, the Mother of All Demos took place on \(motherOfAllDemosDate.description(with: userLocale)).")

Once again, the last line of output will be the date and time of the Mother of All Demos, displayed in your local time and system locale’s language and format.

Creating dates with

ISO8601DateFormatter
requires one less line than doing so with
DateFormatter
.

Creating
Date
s with
Calendar
and
DateComponents

Given a specific date like “December 9, 1968” or a specific date and time like “December 9, 1968 at 3:45 p.m. Pacific Standard Time,” it’s a fairly straightforward process to create a

Date
using a
DateFormatter
or
ISO8601DateFormatter
.

However, you may be asked to create dates and times based on information that doesn’t include a given month or day in the Gregorian calendar (also known as “the Western calendar”). Here are some examples:

  • The 10,000th hour of the year
  • The 234th day of the year
  • The first Friday of the year
  • The first Friday in June (National Donut Day #1)
  • Thursday on the 33rd week of the year
  • An “overflow” date, such as September 50

For cases like these, we’ll need

Calendar
and
DateComponents
. Let’s look at these two structs and then build some
Date
s with them.

Calendar

We tell time using units such as years, months, days, hours, and minutes, all of which evolved over thousands of years and are based on the relative positions and motions of the sun, earth, and moon. It’s much easier for us to understand “January 9, 2007, 10:00 a.m. Pacific Standard Time” than “190,058,400 seconds after the start of the Third Millennium, UTC.”

The

Calendar
struct provides much-needed context for
Date
instances. It allows us to convert back and forth between computer-friendly
Date
objects and human-friendly units like years, months, days, and so on. Think of it as a ruler or tape measure that you can place beside
Date
’s time system. Most of the time, you’ll use the Gregorian calendar; if you picture it alongside the way
Date
measures time, it would look like this:

Swift’s reference timeline, shown lined up with the Gregorian calendar

In addition to the Gregorian calendar,

Calendar
supports 15 other calendar systems, including the Buddhist and Hebrew calendars, which are shown below alongside the Gregorian calendar and
Date
’s time system:

Swift’s reference timeline, shown lined up with the Gregorian, Hebrew, and Buddhist calendars

Let’s start with

Calendar
by determining what your system’s current calendar is:

🛠️ Add this code to the playground and run it:

print("\nCalendar:")
print("• The current calendar is \(Calendar.current).")
print("• The current calendar’s time zone is \(Calendar.current.timeZone) ")

On my system, the output is:

Calendar:
• The current calendar is gregorian (current).
• The current calendar’s time zone is America/New_York (fixed (equal to current))

Calendar.current
(and by extension, its properties, including
Calendar.current.timeZone
) is a static read-only property based on the user’s settings. Your system is probably set to the
gregorian
calendar, and its time zone is set to your system’s time zone settings.

Calendar
doesn’t do much on its own. It simply provides a human-friendly set of references to Swift’s system for telling time. It requires a companion class that allows us to make use of the years, months, days, hours, and minutes that a
Calendar
provides.

Creating a
Date
with
Calendar
and
DateComponents

The

DateComponents
struct allows us to assemble either...

  • a point in time, or
  • an interval of time

...out of components such as year, month, day, hour, minute, and more. For now, we’ll use

DateComponents
to assemble a point in time.

Diagram showing that we can take a set of DateComponents and then use a Calendar to create a Date.

Here’s the complete set of components that

DateComponents
uses:

ComponentDescription
calendar
The calendar system of the date. Its default value is the system’s calendar, which is usually
gregorian
.
day
The day number of the date. For January 9, 2007, 18:00 UTC, this value is
9
.
era
The era of the date, which depends its calendar system. In this case, we’re using the Gregorian calendar, which has two eras:
  • BCE (a.k.a. BC), represented by the integer value
    0
  • CE (a.k.a. AD), represented by the integer value
    1
hour
The hour number of the date. For January 9, 2007, 18:00 UTC, this value is
18
.
minute
The minute number of the date. For January 9, 2007, 18:00 UTC, this value is
0
.
month
The month number of the date, with month numbers starting at
1
. For January 9, 2007, 18:00 UTC, this value is
1
.
nanosecond
The nanosecond number of the date. For January 9, 2007, 18:00 UTC, this value is
0
.
quarter
The quarter number of the date, with quarter numbers starting at
0
. January 9, 2007, 18:00:00 UTC is in the first quarter of the year, so this value is
0
.
second
The second number of the date. For January 9, 2007, 18:00 UTC, this value is
0
.
timeZone
The time zone of the date. This will depend on the time zone of the calendar used.
weekday
The day of the week of the date. In the
gregorian
calendar, Sunday is
1
, Monday is
2
, Tuesday is
3
, and so on. January 9, 2007, was a Wednesday, so this value for that date is
3
.
weekdayOrdinal
The position of the weekday within the next larger specified calendar unit.
  • If the next larger specified unit is
    month
    , this is the position of the weekday within that month. If this value is set to
    1
    and
    weekday
    is set to
    4
    (Wednesday), this will specify the first Wednesday of the given month.
  • If the next larger specified unit is
    year
    , this returns the position of the weekday within that year. If this value is set to
    1
    and
    weekday
    is set to
    4
    (Wednesday), this will specify the first Wednesday of the given year.
weekOfMonth
The week of the month of the date, where the first week of the month is
1
. January 9, 2007, 18:00 UTC fell on the 2nd week of January 2007, so this value is
2
.
weekOfYear
The week of the year of the date, where the first week of the year is
1
. January 9, 2007, 18:00 UTC fell on the 2nd week of 2007, so this value is
2
.
year
The year number of the date. For January 9, 2007, 18:00 UTC, this value is
2007
.
yearForWeekOfYear
Oh wow, this is so hard to explain that I’ll leave it to Apple’s docs.

You don’t have to specify all the components in a

DateComponents
instance; you have to specify just enough to define a date.

Let’s use

DateComponents
to construct a
Date
object using a year, month, day, and time. We’ll do this by rebuilding the date and time of the original iPhone Stevenote: January 9, 2007 at 10:00 a.m., Pacific Standard Time (UTC-8).

🛠️ Add the following to the playground and run it:

print("\nCreating a Date with Calendar and DateComponents:")

var gregorianCalendar = Calendar(identifier: .gregorian)
gregorianCalendar.locale = userLocale

let iPhoneStevenoteComponents = DateComponents(
  calendar: gregorianCalendar,
  timeZone: TimeZone(identifier: "America/Los_Angeles"),
  year: 2007,
  month: 1,
  day: 9,
  hour: 10,
  minute: 00
)
iPhoneStevenoteDate = gregorianCalendar.date(from: iPhoneStevenoteComponents)!
print("• Once again, the iPhone Stevenote took place on \(iPhoneStevenoteDate.description(with: userLocale)).")

On my system, the code above results in this output:

Once again, the iPhone Stevenote took place on Tuesday, January 9, 2007 at 1:00:00 PM Eastern Standard Time.

The date and time that you’ll see will depend on your time zone, but it will be the local equivalent date and time of January 9, 2007 at 10:00:00 AM UTC-8.

Let’s take a closer look at the newly-added code. Here are the first two lines:

var gregorianCalendar = Calendar(identifier: .gregorian)
gregorianCalendar.locale = userLocale

In the first line, we’re creating a

Calendar
instance using the
Calendar(identifier:)
initializer. We’re specifying that the calendar’s date system is
.gregorian
, which is the shorthand form of the full calendar identifier,
Calendar.Identifier.gregorian
.

Newly-instantiated

Calendar
instances don’t have a set
locale
, so we set it to the user’s current locale in the second line. Doing this sets the
Calendar
to the user’s preferred language, which is useful when we want to get a list of weekday and month names.

Here’s the third line:

let iPhoneStevenoteComponents = DateComponents(
  timeZone: TimeZone(identifier: "America/Los_Angeles"),
  year: 2007,
  month: 1,
  day: 9,
  hour: 10,
  minute: 00
)

This line creates a set of date components that represent January 9, 2007 at 10:00 a.m., Pacific Standard Time (UTC-8). Note that I’ve specified the time zone with a geographic identifier —

America/Los_Angeles
— instead of a time zone identifier like
PST
or a time offset from UTC like
-08:00
. I prefer to use geographic identifiers because they let me delegate figuring out if an event happened during Standard Time or Daylight Saving Time to Foundation, which will always get it right.

DateComponents
’ initializer has a lot of optional parameters. In the code above, we used only those parameters we needed to specify the date, time, and time zone of the original iPhone Stevenote. We’ll look at some of the other parameters shortly.

Here are the final two lines of the code:

iPhoneStevenoteDate = gregorianCalendar.date(from: iPhoneStevenoteComponents)!
print("\nOnce again, the iPhone Stevenote took place on \(iPhoneStevenoteDate.description(with: userLocale)).")

The first line uses

gregorianCalendar
and the
DateComponents
we just created to create a new
Date
, which we assigned to
iPhoneStevenoteDate
.
iPhoneStevenoteComponents
provides the values corresponding to the date and time of the iPhone Stevenote, and
gregorianCalendar
provides the context for those values and creates the
Date
.

Let’s create a

DateComponents
instance in a slightly different way. We’ll do it for the date of the original iPad Stevenote, which took place on January 27, 2010 at 10:00 a.m. Pacific Standard Time.

🛠️ Add this code to the playground and run it:

var iPadStevenoteComponents = DateComponents()
iPadStevenoteComponents.year = 2010
iPadStevenoteComponents.month = 1
iPadStevenoteComponents.day = 27
iPadStevenoteComponents.hour = 10
iPadStevenoteComponents.minute = 0
iPadStevenoteComponents.timeZone = TimeZone(identifier: "America/Los_Angeles")
iPadStevenoteDate = gregorianCalendar.date(from: iPadStevenoteComponents)!
print("• Once again, the iPad Stevenote took place on \(iPadStevenoteDate.description(with: userLocale)).")

The code above takes a different approach to set up a

DateComponents
instance by using the default initializer to instantiate
DatComponents
, followed by setting its various properties to specify the desired date’s year, month, day, hour, minute, and time zone. The process of creating a
Date
from
DateComponents
remains the same: using
Calendar
’s
date(from:)
method.

What will the day and time be 10,000 hours into 2024?

So far, we’ve created

Date
s based on known dates and times. How about Dates where we don’t have a specific date but have enough criteria to specify a date? This is where
Calendar
and
DateComponets
shine.
Calendar
does its best to work with the
DateComponents
that you give it, and
DateComponents
gives you all sorts of ways to specify a date.

You’ve probably heard of the “10,000 Hour Rule,” which states that on average, it takes a person about 10,000 hours of directed practice to become an expert at something. We’re going to ignore the debate over whether it’s true and focus on the 10,000 hours instead. If you could practice for 10,000 hours non-stop, without having to take a break to eat, sleep, or do anything else, and you started on January 1, 2024 at midnight, what would the date and time be when you had completed your practice?

🛠️ Add the following to the playground and run it:

let tenThousandHoursComponents = DateComponents(
    year: 2024,
    hour: 10000
)
let tenThousandHoursInto2023Date = gregorianCalendar.date(from: tenThousandHoursComponents)!
print("\n• 10,000 hours into 2024, the date and time will be \(tenThousandHoursInto2023Date.description(with: userLocale)).")
print("• In UTC, that’s \(tenThousandHoursInto2023Date.description).")

Here’s what my system displayed when I ran the code:

• 10,000 hours into 2024, the date and time will be Thursday, February 20, 2025 at 4:00:00 PM Eastern Standard Time.
• In UTC, that’s 2025-02-20 21:00:00 +0000.

You may have trouble believing that a 10,000-hour practice session that started at the very start of 2023 would end nearly three months into 2024, but it’s true. 10,000 hours is 8 hours short of 417 days. You can’t practice for that length of time continuously, which is why many people prefer to say it takes 10 years to become an expert — that’s how long it takes to accumulate 10,000 hours by breaking it into sessions of three hours every day.

What will the 234th day of 2024 be?

DateComponents
makes this calculation easy.

🛠️ Add this code to the playground and run it:

let day234Components = DateComponents(
    year: 2024,
    day: 243
)
let day234Date = gregorianCalendar.date(from: day234Components)!
print("\n• The 234th day of 2024 will be \(day234Date.description(with: userLocale)).")

On my system, this code produces the following output:

• The 234th day of 2024 will be Friday, August 30, 2024 at 12:00:00 AM Eastern Daylight Time.

What date is the first Friday of 2024?

Let’s use a couple of

DateComponents
properties to find out what date the first Friday of 2024 will fall on. These properties are:

  • weekday
    : An integer representing a day of the week. The first day of the week, Sunday, is represented by 1, and the last day of the week, Saturday, is represented by 7. We want a Friday, so we’ll set this value to
    6
    .
  • weekdayOrdinal
    : The ordinal, or order number, of the given weekday. We want the first Friday, so we’ll set this value to
    1
    .

🛠️ Add the following to the playground and run it:

let firstFriday2024Components = DateComponents(
    year: 2024,        // We want a date in 2024
    weekday: 6,        // It’s a Friday
    weekdayOrdinal: 1  // The first one
)
let firstFriday2024Date = gregorianCalendar.date(from: firstFriday2024Components)!
print("\n• The first Friday of 2024 will be \(firstFriday2024Date.description(with: userLocale)).")

Here’s what my system displayed when I ran the code:

• The first Friday of 2024 will be Friday, January 5, 2024 at 12:00:00 AM Eastern Standard Time.

What date is the first National Donut Day of 2023?

Apparently, just one National Donut Day isn’t enough. The second one of the year is a stationary day; it always happens on November 5. But the first one of the year is a moving day — it’s the first Friday in June. Let’s let Swift tell us what that date is for 2023.

🛠️ Add this code to the playground and run it:

let firstDonutDay2024Components = DateComponents(
    year: 2024,        // We want a date in 2024
    month: 6,          // in June
    weekday: 6,        // It’s a Friday
    weekdayOrdinal: 1  // The first one
)
let firstDonutDay2024Date = gregorianCalendar.date(from: firstDonutDay2024Components)!
print("\n• The first National Donut Day of 2024 will be \(firstDonutDay2024Date.description(with: userLocale)).")

On my system, this code produces the following output:

• The first National Donut Day of 2024 will be Friday, June 7, 2024 at 12:00:00 AM Eastern Daylight Time.

What date is the Thursday of the 33rd week of the year?

We’ll use

DateComponents
weekOfYear
property to solve this one.

🛠️ Add the following to the playground and run it:

let thursday33rdWeek2024Components = DateComponents(
    year: 2024,     // We want a date in 2024
    weekday: 5,     // It’s a Thursday
    weekOfYear: 33  // on the 33rd week of the year
)
let thursday33rdWeek2024Date = gregorianCalendar.date(from: thursday33rdWeek2024Components)!
print("\n• The Thursday of the 33rd week of 2024 will be \(thursday33rdWeek2024Date.description(with: userLocale)).")

Here’s what my system displayed when I ran the code:

• The Thursday of the 33rd week of 2024 will be Thursday, August 15, 2024 at 12:00:00 AM Eastern Daylight Time.

What happens if you set
DateComponents
to September 50, 2024?

Let’s look at the case of overflow. What happens if you try to create a

Date
using components that define the nonsense date “September 50, 2024?”

🛠️ Add this code to the playground and run it:

var sept50DateComponents = DateComponents(
  year: 2024,
  month: 9,
  day: 50)
let sept50Date = gregorianCalendar.date(from: sept50DateComponents)!
print("\n• September 50, 2024 is actually \(sept50Date.description(with: userLocale)).")

On my system, this code produces the following output:

• September 50, 2024 is actually Sunday, October 20, 2024 at 12:00:00 AM Eastern Daylight Time.

These date components we provided are treated as “the start of September, plus 50 days” — September’s 30 days, plus an additional 20 days into October.

Extracting
DateComponents
from a
Date
with
Calendar

Just as we can use a

Calendar
and
DateComponents
to make a
Date
, we can also use
Date
and a
Calendar
to extract its
DateComponents
.

Diagram showing that we can take a Date and then use a Calendar to create a set of DateComponents.

Extracting all the components from a
Date

Let’s extract all the components from a

Date
we’ve already defined:
iPhoneStevenoteDate
, which is January 9, 2007, 10:00 a.m. Pacific Standard Time.

🛠️ Add the following to the playground and run it:

var iPhoneStevenoteDateComponents = gregorianCalendar.dateComponents(
    [
        .calendar,
        .day,
        .era,
        .hour,
        .minute,
        .month,
        .nanosecond,
        .quarter,
        .second,
        .timeZone,
        .weekday,
        .weekdayOrdinal,
        .weekOfMonth,
        .weekOfYear,
        .year,
        .yearForWeekOfYear
    ],
    from: iPhoneStevenoteDate
)
print("\nThe date components for the iPhone Stevenote date:")
print("• Calendar: \(iPhoneStevenoteDateComponents.calendar!.identifier)")
print("• Day: \(iPhoneStevenoteDateComponents.day!)")
print("• Era: \(iPhoneStevenoteDateComponents.era!)")
print("• Hour: \(iPhoneStevenoteDateComponents.hour!)")
print("• Minute: \(iPhoneStevenoteDateComponents.minute!)")
print("• Month: \(iPhoneStevenoteDateComponents.month!)")
print("• Nanosecond: \(iPhoneStevenoteDateComponents.nanosecond!)")
print("• Quarter: \(iPhoneStevenoteDateComponents.quarter!)")
print("• Second: \(iPhoneStevenoteDateComponents.second!)")
print("• Time zone: \(iPhoneStevenoteDateComponents.timeZone!)")
print("• Weekday: \(iPhoneStevenoteDateComponents.weekday!)")
print("• Weekday ordinal: \(iPhoneStevenoteDateComponents.weekdayOrdinal!)")
print("• Week of month: \(iPhoneStevenoteDateComponents.weekOfMonth!)")
print("• Week of year: \(iPhoneStevenoteDateComponents.weekOfYear!)")
print("• Year: \(iPhoneStevenoteDateComponents.year!)")
print("• Year for week of year: \(iPhoneStevenoteDateComponents.yearForWeekOfYear!)")

Here’s what my system displayed when I ran the code:

The date components for the iPhone Stevenote date:
• Calendar: gregorian
• Day: 9
• Era: 1
• Hour: 13
• Minute: 0
• Month: 1
• Nanosecond: 0
• Quarter: 0
• Second: 0
• Time zone: America/New_York (fixed (equal to current))
• Weekday: 3
• Weekday ordinal: 2
• Week of month: 2
• Week of year: 2
• Year: 2007
• Year for week of year: 2007

The component values that you’ll see are determined by your system’s time zone. If you want to see the system components in the time zone where the Stevenote took place (Steve Jobs gave his keynote in San Francisco, which is in the

America/Los_Angeles
time zone), use a
Calendar
whose
timeZone
property is set to
America/Los_Angeles
.

🛠️ Add this code to the playground and run it:

var pacificCalendar = Calendar(identifier: .gregorian)
pacificCalendar.timeZone = TimeZone(identifier: "America/Los_Angeles")!
pacificCalendar.locale = Locale.autoupdatingCurrent
iPhoneStevenoteDateComponents = pacificCalendar.dateComponents(
    [
        .calendar,
        .day,
        .era,
        .hour,
        .minute,
        .month,
        .nanosecond,
        .quarter,
        .second,
        .timeZone,
        .weekday,
        .weekdayOrdinal,
        .weekOfMonth,
        .weekOfYear,
        .year,
        .yearForWeekOfYear
    ],
    from: iPhoneStevenoteDate
)
print("\nThe date components for the iPhone Stevenote date - for the Pacific Calendar — are:")
print("• Calendar: \(iPhoneStevenoteDateComponents.calendar!.identifier)")
print("• Day: \(iPhoneStevenoteDateComponents.day!)")
print("• Era: \(iPhoneStevenoteDateComponents.era!)")
print("• Hour: \(iPhoneStevenoteDateComponents.hour!)")
print("• Minute: \(iPhoneStevenoteDateComponents.minute!)")
print("• Month: \(iPhoneStevenoteDateComponents.month!)")
print("• Nanosecond: \(iPhoneStevenoteDateComponents.nanosecond!)")
print("• Quarter: \(iPhoneStevenoteDateComponents.quarter!)")
print("• Second: \(iPhoneStevenoteDateComponents.second!)")
print("• Time zone: \(iPhoneStevenoteDateComponents.timeZone!)")
print("• Weekday: \(iPhoneStevenoteDateComponents.weekday!)")
print("• Weekday ordinal: \(iPhoneStevenoteDateComponents.weekdayOrdinal!)")
print("• Week of month: \(iPhoneStevenoteDateComponents.weekOfMonth!)")
print("• Week of year: \(iPhoneStevenoteDateComponents.weekOfYear!)")
print("• Year: \(iPhoneStevenoteDateComponents.year!)")
print("• Year for week of year: \(iPhoneStevenoteDateComponents.yearForWeekOfYear!)")

You’ll see the following output:

The date components for the iPhone Stevenote date - for the Pacific Calendar — are:
• Calendar: gregorian
• Day: 9
• Era: 1
• Hour: 10
• Minute: 0
• Month: 1
• Nanosecond: 0
• Quarter: 0
• Second: 0
• Time zone: America/Los_Angeles (fixed)
• Weekday: 3
• Weekday ordinal: 2
• Week of month: 2
• Week of year: 2
• Year: 2007
• Year for week of year: 2007

This time, the output lists the components for the date of the Stevenote in its local date and time, regardless of your system’s time zone.

What day of the week and week of the year will April Fools’ Day 2024 fall on?

In many parts of the world, April 1 is a day for playing pranks on others. In the English-speaking world, it has a name: April Fools’ Day.

Since it has an assigned date, we already know its what month and day it will fall on. But it takes effort to determine what weekday it will be on a given April 1, or what week of the year it will be.

DateComponents
can help us. We can use a
DateComponents
instance to construct a
Date
for April 1, 2024 and another
DateComponents
instance to extract the
weekday
and
weekOfYear
values for that
Date
.

🛠️ Add the following to the playground and run it:

// Create the date
let aprilFoolsDay2024Components = DateComponents(
    year: 2024,
    month: 4,
    day: 1
)
let aprilFoolsDay2024Date = gregorianCalendar.date(from: aprilFoolsDay2024Components)!

// Extract the weekday and week of the year from the date
let wantedComponents = gregorianCalendar.dateComponents(
    [
        .weekday,
        .weekOfYear,
    ],
    from: aprilFoolsDay2024Date
)
let aprilFools2024Weekday = wantedComponents.weekday!
let aprilFools2024WeekdayName = gregorianCalendar.weekdaySymbols[aprilFools2024Weekday - 1]
let aprilFools2024WeekOfYear = wantedComponents.weekOfYear!
print("\nApril Fools’ Day 2024:")
print("• happens on day \(aprilFools2024Weekday) of the week (\(aprilFools2024WeekdayName)),")
print("• on week \(aprilFools2024WeekOfYear) of 2024.")

Running this code reveals that April 1, 2024 will fall on weekday 2 (Monday), and that it will occur on the 14th week of the year. To specify which weekday day 2 is, I used

Calendar
’s
weekdaySymbols
array to access the name of the corresponding weekday, which in this case is
Monday
.

The Big Challenge

At the start of this chapter, I said that Swift’s date and time system lets you do the following without having to do the calculations yourself, while using only a few lines of code:

  • Get the date for the third Wednesday of July at 3:30 p.m. Pacific Time
  • Display it as it would appear in the Coptic calendar system in Melbourne, Australia’s time zone with a few lines of code.

🛠️ Let’s give it a try! Add this code to the playground and run it:

print("\nThe Big Challenge!")

// First, create the the date —
// third Wednday of July at 3:30 p.m. Pacific Time
let challengeDateComponents = DateComponents(
    calendar: gregorianCalendar,
    timeZone: TimeZone(identifier: "America/Los_Angeles")!,
    year: 2024,
    month: 7,
    hour: 15,
    minute: 30,
    weekday: 4,
    weekdayOrdinal: 3
)
let challengeDate = gregorianCalendar.date(from: challengeDateComponents)!
print("• The challenge date in the Gregorian calendar is \(challengeDate.description(with: userLocale)).")

var melbourneCalendar = Calendar(identifier: .gregorian)
melbourneCalendar.timeZone = TimeZone(identifier: "Australia/Melbourne")!
let melbourneDateComponents = melbourneCalendar.dateComponents(
  [
    .year,
    .month,
    .day,
    .weekday,
    .hour,
    .minute
  ],
  from: challengeDate)
print("• melbourneDateComponents: \(melbourneDateComponents)")

// Create the Coptic calendar and set its locale to Arabic(Egypt)
// so that it us Arabic month nam,
// and its time zone to Melbourne.
var copticCalendar = Calendar(identifier: .coptic)
copticCalendar.locale = Locale(identifier: "ar_EG")
copticCalendar.timeZone = TimeZone(identifier: "Australia/Melbourne")!

// Extract the date components from the date using the Coptic calendar
let challengeCopticComponents = copticCalendar.dateComponents(
  [
    .year,
    .month,
    .day,
    .weekday,
    .hour,
    .minute
  ],
  from: challengeDate)
let challengeYear = challengeCopticComponents.year!
let challengeMonth = challengeCopticComponents.month!
let challengeMonthName = copticCalendar.monthSymbols[challengeMonth - 1]
let challengeDay = challengeCopticComponents.day!
let challengeWeekday = challengeCopticComponents.weekday!
let challengeWeekdayName = copticCalendar.weekdaySymbols[challengeWeekday - 1]
let challengeHour = challengeCopticComponents.hour!
let challengeMinute = challengeCopticComponents.minute!
print("• The challenge date in the Coptic calendar happens on: ")
print("••• year \(challengeYear)")
print("••• month \(challengeMonth) (\(challengeMonthName))")
print("••• day \(challengeDay)")
print("••• weekday \(challengeWeekday) (\(challengeWeekdayName))")
print("••• hour \(challengeHour)")
print("••• minute \(challengeMinute) (Melbourne time).")

You’ll see the following output:

The Big Challenge!
• The challenge date in the Gregorian calendar is Wednesday, July 17, 2024 at 6:30:00 PM Eastern Daylight Time.
• melbourneDateComponents: year: 2024 month: 7 day: 18 hour: 8 minute: 30 weekday: 5 isLeapMonth: false 
• The challenge date in the Coptic calendar happens on: 
••• year 1740
••• month 11 (أبيب)
••• day 11
••• weekday 5 (الخميس)
••• hour 8
••• minute 30 (Melbourne time).

The output shows that:

  • In my time zone (US Eastern), the challenge date is Wednesday, July 17, 2024 at 6:30 p.m..
  • In Melbourne, Australia, that date is Thursday (weekday 5), July 18, 2024 at 8:30 a.m. in the Gregorian calendar.
  • In the Coptic calendar, the date is 11th day of أبيب (“Epip” or “Abib”, month 11), which is a Thursday (الخميس). I verified that this lines up with July 20, 2024 using this calendar page.

Next Steps

💻 Once again, you can find an Xcode playground with all the code in this article in this GitHub repository — it’s

intro-dates-times-swift-1.playground
.

In the next chapter of this tutorial, we’ll take a closer look at ways of converting

Date
s into strings to display to the user.