close icon
iOS

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.

Last Updated On: December 21, 2023

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
timeIntervalSinceNowReturns 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.
timeIntervalSinceReferenceDateReturns 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.
timeIntervalSince1970Returns 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
DateCreates a Date representing the date and time for the given number of seconds before or after the current date and time.
DateCreates 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.
DateCreates 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.
DateThis 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 Dates 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 Dates 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
calendarThe calendar system of the date. Its default value is the system’s calendar, which is usually gregorian.
dayThe day number of the date. For January 9, 2007, 18:00 UTC, this value is 9.
eraThe 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
hourThe hour number of the date. For January 9, 2007, 18:00 UTC, this value is 18.
minuteThe minute number of the date. For January 9, 2007, 18:00 UTC, this value is 0.
monthThe month number of the date, with month numbers starting at 1. For January 9, 2007, 18:00 UTC, this value is 1.
nanosecondThe nanosecond number of the date. For January 9, 2007, 18:00 UTC, this value is 0.
quarterThe 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.
secondThe second number of the date. For January 9, 2007, 18:00 UTC, this value is 0.
timeZoneThe time zone of the date. This will depend on the time zone of the calendar used.
weekdayThe 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.
weekdayOrdinalThe 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.
weekOfMonthThe 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.
weekOfYearThe 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.
yearThe year number of the date. For January 9, 2007, 18:00 UTC, this value is 2007.
yearForWeekOfYearOh 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 Dates 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 DateComponentsweekOfYear 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 Dates into strings to display to the user.

  • Twitter icon
  • LinkedIn icon
  • Faceboook icon