What we’ve covered so far, and what we’ll cover in this installment
So far, in this series on programming with dates and times in Swift 5, we’ve looked at:
- The basic structs, which let us create dates and times, and work with their components. They are:
Date
, which represents a single point in time,DateComponents
, which represents the units that make up a date, such as year, month, day, hour, and minute, and which can be used to represent either a single point in time or a duration of time, andCalendar
, which provides a context forDate
s, and allows us to convert betweenDate
s andDateComponents
.
- The
DateFormatter
class, which convertsDate
s into formattedString
s, and formattedString
s intoDate
s.
With this knowledge under our belts, let’s get to this article’s topic: doing date calculations.
Creating a couple of Date
s to work with
Let’s create a couple of Date
s to work with:
- The date and time of the Stevenote where the iPhone was introduced: January 9, 2007, 10:00 a.m. Pacific time (UTC-8), and
- The date and time of the Stevenote where the iPad was introduced: January 27, 2010, 10:00 a.m. Pacific time (UTC-8).
Start with a fresh playground, and paste or enter the following code into it:
// The user's calendar incorporates the user's locale and // time zone settings, which means it's the one you'll use // most often. let userCalendar = Calendar.current // Let's create a Date for the start of the Stevenote // where the iPhone was introduced (January 9, 2007, 10:00:00 Pacific time) // using DateComponents. let iPhoneStevenoteDateComponents = DateComponents( timeZone: TimeZone(abbreviation: "PST"), year: 2007, month: 1, day: 9, hour: 10 ) let iPhoneStevenoteDate = userCalendar.date(from: iPhoneStevenoteDateComponents)! // Let's create a Date for the start of the Stevenote // where the iPad was introduced (January 27, 2010, 10:00:00 Pacific time) // using DateFormatter. var dateMakerFormatter = DateFormatter() dateMakerFormatter.calendar = userCalendar dateMakerFormatter.dateFormat = "MMM d, yyyy, hh:mm a zz" let iPadStevenoteDate = dateMakerFormatter.date(from: "Jan 27, 2010, 10:00 AM PST")!
In the code above, we’ve created our dates in two different ways:
- We created
iPhoneStevenoteDate
by setting up aDateComponents
struct and then using the user’sCalendar
to convert thoseDateComponents
into aDate
. - We created
iPadStevenoteDate
by converting itsString
representation into aDate
using aDateFormatter
.
Date
comparisons, part 1
Now that we have two Date
s, let’s compare them. In Swift 5, we can use familiar comparison operators — <
, <=
, ==
, !=
, >
, >==
— to tell which Date
came first, or if they represent the exact (and I do mean exact) same point in time.
Add the following code to the playground and run it:
print("Did the iPhone Stevenote come BEFORE the iPad Stevenote? " + "\(iPhoneStevenoteDate < iPadStevenoteDate)") print("Did the iPhone Stevenote come AFTER the iPad Stevenote? " + "\(iPhoneStevenoteDate > iPadStevenoteDate)") print("Did the iPad Stevenote come BEFORE the iPhone Stevenote? " + "\(iPadStevenoteDate < iPhoneStevenoteDate)") print("Does the iPad Stevenote come AFTER the iPhone Stevenote? " + "\(iPadStevenoteDate > iPhoneStevenoteDate)") print("Do the iPhone Stevenote and the iPad Stevenote fall on the EXACT SAME date and time? " + "\(iPhoneStevenoteDate == iPadStevenoteDate)") print("Do the iPhone Stevenote and the iPad Stevenote fall on different dates and times? " + "\(iPhoneStevenoteDate != iPadStevenoteDate)")
The output should be:
Did the iPhone Stevenote come BEFORE the iPad Stevenote? true
Did the iPhone Stevenote come AFTER the iPad Stevenote? false
Did the iPad Stevenote come BEFORE the iPhone Stevenote? false
Does the iPad Stevenote come AFTER the iPhone Stevenote? true
Do the iPhone Stevenote and the iPad Stevenote fall on the EXACT SAME date and time? false
Do the iPhone Stevenote and the iPad Stevenote fall on different dates and times? true
Note that these are comparisons of Date
s, which measure time down to the nearest nanosecond. If you compare two Date
s named date1
and date2
, where date2
represents a point in time one nanosecond after date1
, they will not be equal; date2
will be greater than date1
.
A little later on in this article, we’ll look at more “human” ways of comparing Date
s.
How far apart are the iPhone and iPad Stevenotes, part 1: In seconds, using Date
’s timeIntervalSince()
method
Date
‘s timeIntervalSince
method can give us the difference between two dates and times — in seconds.
Add the following code to the playground and run it:
print("Number of seconds between the iPhone Stevenote and the iPad Stevenote: " + "\(iPhoneStevenoteDate.timeIntervalSince(iPadStevenoteDate))") print("Number of seconds between the iPad Stevenote and the iPhone Stevenote: " + "\(iPadStevenoteDate.timeIntervalSince(iPhoneStevenoteDate))")
The output should be:
Number of seconds between the iPhone Stevenote and the iPad Stevenote: -96249600.0
Number of seconds between the iPad Stevenote and the iPhone Stevenote: 96249600.0
The results tell us that there were 96,248,600 seconds between the iPhone Stevenote and the iPad Stevenote.
While there are cases when you’ll want to know how many seconds there are between two given points in time, there are also many cases where you’ll want to find the differences between two points in time using other units, such as days, weeks, months, and years, not to mention hours and minutes. Date
‘s timeIntervalSince
method isn’t going to work for these cases.
How far apart are the iPhone and iPad Stevenotes, part 2: In days, using Calendar
’s dateComponents(_:from:to:)
method
Most of the time, when you are calculating how far apart two given Date
s are, you’ll be using this method of the Calendar
struct:
dateComponents(components, from: startDate, to: endDate)
Here’s a run-down of its parameters:
Parameter | Description |
---|---|
components |
A Set (expressed in array notation) of Calendar.Component values specifying the time units you want, which can be:
|
startDate: |
The start Date of the time period. |
endDate: |
The end Date of the time period. |
Let’s use dateComponents(_:from:to:)
to find out how many days there were between the iPhone Stevenote and the iPad Stevenote.
Add the following code to the playground and run it:
let daysBetweenStevenotes = userCalendar.dateComponents([.day], from: iPhoneStevenoteDate, to: iPadStevenoteDate) print("There were \(daysBetweenStevenotes.day!) days between the iPhone Stevenote of 2007 and the iPad Stevenote of 2010.")
The output should be:
There were 1114 days between the iPhone Stevenote of 2007 and the iPad Stevenote of 2010.
In the code above, we passed dateComponents(_:from:to:)
three values:
- An array containing the
Calendar.Component
value.day
, which specifies that we want the result expressed as the difference betweeniPadStevenoteDate
andiPhoneStevenoteDate
in terms of days. - The two dates in question,
iPhoneStevenoteDate
andiPadStevenoteDate
.
As the result tells us, there were 1,114 days between the iPhone Stevenote and the iPad Stevenote.
How far apart are the iPhone and iPad Stevenotes, part 3: In weeks
By changing the contents of the array of Calendar.Component
values that we provide in the first argument of Calendar
’s dateComponents(_:from:to:)
method, we can get the result expressed in different time units.
Add the following code to the playground and run it:
let weeksBetweenStevenotes = userCalendar.dateComponents([.weekOfYear], from: iPhoneStevenoteDate, to: iPadStevenoteDate) print("There were \(weeksBetweenStevenotes.weekOfYear!) weeks between the iPhone Stevenote of 2007 and the iPad Stevenote of 2010.")
The output should be:
There were 159 weeks between the iPhone Stevenote of 2007 and the iPad Stevenote of 2010.
In the code above, we passed dateComponents(_:from:to:)
three values:
- An array containing the
Calendar.Component
value.weekOfYear
, which specifies that we want the result expressed as the difference betweeniPadStevenoteDate
andiPhoneStevenoteDate
in terms of the numbered weeks of the year on which both dates fall. For example, if event1 took place on week 2 of a year and event2 took place on week 5, the difference between the two in.weekOfYear
terms would be 3. - The two dates in question,
iPhoneStevenoteDate
andiPadStevenoteDate
.
The result indicates that 159 weeks passed between the iPhone Stevenote and the iPad Stevenote.
If you do the math, 159 times 7 days is 1,113 days, but our previous calculation said that the iPhone Stevenote and the iPad Stevenote were 1,114 days apart. That’s because the two events are 159 whole weeks apart, plus an extra day.
How far apart are the iPhone and iPad Stevenotes, part 4: In years, months, and days
We can also put multiple values of Calendar.Component
into the array that we provide as the first argument of Calendar
’s dateComponents(_:from:to:)
method.
Add the following code to the playground and run it:
let yearsMonthsDaysHoursMinutesBetweenStevenotes = userCalendar.dateComponents( [.year, .month, .day, .hour, .minute], from: iPhoneStevenoteDate, to: iPadStevenoteDate ) let years = yearsMonthsDaysHoursMinutesBetweenStevenotes.year! let months = yearsMonthsDaysHoursMinutesBetweenStevenotes.month! let days = yearsMonthsDaysHoursMinutesBetweenStevenotes.day! let hours = yearsMonthsDaysHoursMinutesBetweenStevenotes.hour! let minutes = yearsMonthsDaysHoursMinutesBetweenStevenotes.minute! print("There were \(years) years, \(months) months, \(days) days, \(hours) hours, and \(minutes) minutes between the the iPhone Stevenote of 2007 and the iPad Stevenote of 2010.")
In the code above, we passed dateComponents(_:from:to:)
three values:
- An array containing the
Calendar.Component
values.year, .month, .day, .hour, .minute
, which specifies that we want the result expressed as the difference betweeniPadStevenoteDate
andiPhoneStevenoteDate
in terms of years, months, days, hours, and minutes. The method uses the largest applicable component before using smaller ones — for example, it will give results like 1 month and 5 days rather than 35 days. - The two dates in question,
iPhoneStevenoteDate
andiPadStevenoteDate
.
The results show that the iPhone Stevenote and the iPad Stevenote were 3 years and 18 days apart.
Date
addition, part 1: What’s the last day of a 90-day warranty that starts today?
Now that we know how to answer the question “What’s the difference in time between two Date
s?”, let’s try answering a different question: “If we add a time interval to a Date
, what’s the resulting Date
?”
To answer this question, we’ll use this method of Calendar
:
date(byAdding: timeInterval, value: numberOfTimeUnits to: startDate)
Here’s a run-down of its parameters:
Parameter | Description |
---|---|
timeInterval |
A dateComponents struct whose properties contain values defining the interval of time. |
numberOfTimeUnits |
The number of timeInterval units to be added to the Date in question. |
startDate |
The Date in question. |
Let’s start with a simple bit of code that tells us the last day of a 90-day warranty whose term starts right now:
// What's the last day of a 90-day warranty that starts today? let lastDay = userCalendar.date(byAdding: .day, value: 90, to: Date())! print("90 days from now is: \(lastDay.description(with: Locale(identifier: "en_US")))")
The result is a Date
representing a point in time 90 days from the present. On my computer, the output looked like this:
90 days from now is: Optional(“Tuesday, August 25, 2020 at 10:30:46 PM Eastern Daylight Time”)
Date
addition, part 2: What was the date 5 weeks ago?
Just as we can convert addition to subtraction by adding a negative value, we can also do Date
subtraction by providing date(byAdding:value:to:)
with negative values. Here’s an example of code that returns a date that is an interval of time prior to the date in question:
// What was the date 5 weeks ago? let fiveWeeksAgo = userCalendar.date(byAdding: .weekOfYear, value: -5, to: Date())! print("5 weeks ago was: \(fiveWeeksAgo.description(with: Locale(identifier: "en_US")))")
The result is a Date
representing a point in time 5 weeks in the past. On my computer, the output looked like this:
5 weeks ago was: Wednesday, April 22, 2020 at 11:12:40 PM Eastern Daylight Time
Date
addition, part 3: What time will it be 4 hours and 30 minutes from now, and 4 hours and 30 minutes ago?
The date(byAdding:value:to:)
method works when you just want to add one kind of time unit — a minute, hour, day, week, month, or year — to a Date
. If you want to add multiple kinds of time units to a Date
, such as 4 hours and 30 minutes, you need to use this Calendar
method instead:
date(byAdding: timeIntervalComponents, to: startDate)
Here’s a run-down of its parameters:
Parameter | Description |
---|---|
timeIntervalComponents |
A dateComponents struct whose properties contain values defining the interval of time. |
startDate |
The Date in question. |
Here’s the code that answers the question “What time will it be 4 hours and 30 minutes from now?”
// What time will it be 4 hours and 30 minutes from now? // First, we need to define a DateComponents struct representing // a time interval of 4 hours and 30 minutes var fourHoursThirtyMinutes = DateComponents() fourHoursThirtyMinutes.hour = 4 fourHoursThirtyMinutes.minute = 30 // Now add the interval to the Date let fourHoursThirtyMinutesFromNow = userCalendar.date( byAdding: fourHoursThirtyMinutes, to: Date() )! print("4 hours and 30 minutes from now will be: \(fourHoursThirtyMinutesFromNow.description(with: Locale(identifier: "en_US")))")
In the code above, we did the following:
- First, we defined a
DateComponents
struct representing a 4-hour, 30-minute span of time, - then we added that span of time to the present date and time using the
date(byAdding:to:)
method.
The result is a Date
representing a time 4 hours and 30 seconds in the future.
Let’s find out what the Date
was 4 hours and 30 seconds ago:
// What time was it 4 hours and 30 minutes ago? var minusFourHoursThirtyMinutes = DateComponents() minusFourHoursThirtyMinutes.hour = -4 minusFourHoursThirtyMinutes.minute = -30 let fourHoursThirtyMinutesAgo = userCalendar.date( byAdding: fourHoursThirtyMinutes, to: Date() )! print("4 hours and 30 minutes ago was: \(fourHoursThirtyMinutesAgo.description(with: Locale(identifier: "en_US")))")
On my computer, the output looked like this:
4 hours and 30 minutes from now will be: Thursday, May 28, 2020 at 3:42:40 AM Eastern Daylight Time
4 hours and 30 minutes ago was: Thursday, May 28, 2020 at 3:42:40 AM Eastern Daylight Time
Date comparisons, part 2: Making Date comparisons a little more “human”
One recurring theme in science fiction (and especially in Star Trek) is the tendency for ultra-smart characters and computers to be overly, needlessly, pointlessly precise. The writers for the original series often did this with Spock, and it seemed that at least a few writers were aware of this annoying trope in later series. Here’s a bit of dialogue from The Next Generation:
Data: 6 days, 13 hours, 47 minutes.
Riker: What, no seconds?
Data: I have discovered, sir, a certain level of impatience when I calculate a lengthy time interval to the nearest second. [beat] However if you wish…
Riker: No. No. Minutes is fine.
Date
‘s comparison operators have the same problem with being overly precise.
Consider the following Date
s related to the announcement of SwiftUI:
- The start of the announcement, 2 hours and 8 minutes into the WWDC 2019 keynote: June 3, 2019, 12:08:00 p.m. PDT
- One second after the start of the announcement: June 3, 2019, 12:09:00 p.m. PDT
- Five minutes after the start of the announcement: June 3, 2019, 12:13:00 p.m. PDT
- Three hours after the start of the announcement: June 3, 2019, 03:08:00 p.m. PDT
Date
‘s comparison operators think of all these points in time as very different, but depending on your circumstances you may think of them as being practically the same:
- In most cases, there really isn’t a difference between the time when SwiftUI was announced and one second after.
- If you’re concerned only with the day when SwiftUI was announced and not the exact time, there’s effectively no difference between any of the
Date
s listed above.
Calendar
‘s compare(_:to:toGranularity)
method allows us to perform Date
comparisons at different levels of granularity:
compare(firstDate, to: secondDate, toGranularity: granularity)
Here’s a run-down of its parameters:
Parameter | Description |
---|---|
firstDate |
The first Date in the comparison. |
secondDate |
The second Date in the comparison. |
granularity |
The level of precision for the comparison, expressed as an Calendar.Component value, which includes:
|
This is a Cocoa method named “compare”, so you’ve probably guessed that its return type is ComparisonResult
. Here’s what it returns:
If… | compare returns: |
---|---|
firstDate is earlier than secondDate , when compared at the specified granularity |
.orderedAscending |
firstDate is equal to secondDate , when compared at the specified granularity |
.orderedSame |
firstDate is later than secondDate , when compared at the specified granularity |
.orderedDescending |
It’s easier to show compare(_:to:toGranularity)
in action than to explain how it works. Add the following code into the playground:
// Let's define some Dates relative to the SwiftUI announcement // (June 3, 2019, 12:08 p.m. PDT) let swiftUIAnnouncementDateComponents = DateComponents( timeZone: TimeZone(abbreviation: "PDT"), year: 2019, month: 6, day: 3, hour: 12, minute: 8 ) let swiftUIAnnouncement = userCalendar.date(from: swiftUIAnnouncementDateComponents)! let swiftUIAnnouncementPlusOneSecond = userCalendar.date( byAdding: .second, value: 1, to: swiftUIAnnouncement )! let swiftUIAnnouncementPlusFiveMinutes = userCalendar.date( byAdding: .minute, value: 5, to: swiftUIAnnouncement )! let swiftUIAnnouncementPlusThreeHours = userCalendar.date( byAdding: .hour, value: 3, to: swiftUIAnnouncement )! // This returns false, because when measuring time at the granularity of a SECOND, // swiftUIAnnouncement happens BEFORE swiftUIAnnouncementPlusOneSecond. let test1 = userCalendar.compare(swiftUIAnnouncement, to: swiftUIAnnouncementPlusOneSecond, toGranularity: .second) == .orderedSame print("test1: \(test1)") // This returns true, because when measuring time at the granularity of a SECOND, // swiftUIAnnouncement happens BEFORE swiftUIAnnouncementPlusOneSecond. let test2 = userCalendar.compare(swiftUIAnnouncement, to: swiftUIAnnouncementPlusOneSecond, toGranularity: .second) == .orderedAscending print("test2: \(test2)") // This returns true, because when measuring time at the granularity of a MINUTE, // swiftUIAnnouncement happens AT THE SAME TIME AS swiftUIAnnouncementPlusOneSecond. let test3 = userCalendar.compare(swiftUIAnnouncement, to: swiftUIAnnouncementPlusOneSecond, toGranularity: .minute) == .orderedSame print("test3: \(test3)") // This returns true, because when measuring time at the granularity of an HOUR, // swiftUIAnnouncement happens AT THE SAME TIME AS swiftUIAnnouncementPlusFiveMinutes. let test4 = userCalendar.compare(swiftUIAnnouncement, to: swiftUIAnnouncementPlusFiveMinutes, toGranularity: .hour) == .orderedSame print("test4: \(test4)") // This returns true, because when measuring time at the granularity of a MINUTE, // swiftUIAnnouncementPlusFiveMinutes happens AFTER swiftUIAnnouncement. let test5 = userCalendar.compare(swiftUIAnnouncementPlusFiveMinutes, to: swiftUIAnnouncement, toGranularity: .minute) == .orderedDescending print("test5: \(test5)") // This returns true, because when measuring time at the granularity of a DAY, // swiftUIAnnouncement happens AT THE SAME TIME AS swiftUIAnnouncementPlusThreeHours. let test6 = userCalendar.compare(swiftUIAnnouncement, to: swiftUIAnnouncementPlusThreeHours, toGranularity: .day) == .orderedSame print("test6: \(test6)")
The output should be:
test1: false
test2: true
test3: true
test4: true
test5: true
test6: true
Wrapping it all up
Here’s the playground containing all the code we just worked with:
import UIKit // Creating a couple of Dates to work with // ======================================= // The user's calendar incorporates the user's locale and // time zone settings, which means it's the one you'll use // most often. let userCalendar = Calendar.current // Let's create a Date for the start of the Stevenote // where the iPhone was introduced (January 9, 2007, 10:00:00 Pacific time) // using DateComponents. let iPhoneStevenoteDateComponents = DateComponents( timeZone: TimeZone(abbreviation: "PST"), year: 2007, month: 1, day: 9, hour: 10 ) let iPhoneStevenoteDate = userCalendar.date(from: iPhoneStevenoteDateComponents)! // Date comparisons, part 1 // ======================== // Let's create a Date for the start of the Stevenote // where the iPad was introduced (January 27, 2010, 10:00:00 Pacific time) // using DateFormatter. var dateMakerFormatter = DateFormatter() dateMakerFormatter.calendar = userCalendar dateMakerFormatter.dateFormat = "MMM d, yyyy, hh:mm a zz" let iPadStevenoteDate = dateMakerFormatter.date(from: "Jan 27, 2010, 10:00 AM PST")! print("Did the iPhone Stevenote come BEFORE the iPad Stevenote? " + "\(iPhoneStevenoteDate < iPadStevenoteDate)") print("Did the iPhone Stevenote come AFTER the iPad Stevenote? " + "\(iPhoneStevenoteDate > iPadStevenoteDate)") print("Did the iPad Stevenote come BEFORE the iPhone Stevenote? " + "\(iPadStevenoteDate < iPhoneStevenoteDate)") print("Does the iPad Stevenote come AFTER the iPhone Stevenote? " + "\(iPadStevenoteDate > iPhoneStevenoteDate)") print("Do the iPhone Stevenote and the iPad Stevenote fall on the EXACT SAME date and time? " + "\(iPhoneStevenoteDate == iPadStevenoteDate)") print("Do the iPhone Stevenote and the iPad Stevenote fall on different dates and times? " + "\(iPhoneStevenoteDate != iPadStevenoteDate)") // How far apart are the iPhone and iPad Stevenotes, part 1: In seconds, // using Date’s timeIntervalSince() method // ===================================================================== print("Number of seconds between the iPhone Stevenote and the iPad Stevenote: " + "\(iPhoneStevenoteDate.timeIntervalSince(iPadStevenoteDate))") print("Number of seconds between the iPad Stevenote and the iPhone Stevenote: " + "\(iPadStevenoteDate.timeIntervalSince(iPhoneStevenoteDate))") // How far apart are the iPhone and iPad Stevenotes, part 2: // In days, using Calendar’s dateComponents(_:from:to:) method // =========================================================== let daysBetweenStevenotes = userCalendar.dateComponents([.day], from: iPhoneStevenoteDate, to: iPadStevenoteDate) print("There were \(daysBetweenStevenotes.day!) days between the iPhone Stevenote of 2007 and the iPad Stevenote of 2010.") // How far apart are the iPhone and iPad Stevenotes, part 3: In weeks // ================================================================== let weeksBetweenStevenotes = userCalendar.dateComponents([.weekOfYear], from: iPhoneStevenoteDate, to: iPadStevenoteDate) print("There were \(weeksBetweenStevenotes.weekOfYear!) weeks between the iPhone Stevenote of 2007 and the iPad Stevenote of 2010.") // How far apart are the iPhone and iPad Stevenotes, part 4: // In years, months, and days // ========================================================= let yearsMonthsDaysHoursMinutesBetweenStevenotes = userCalendar.dateComponents( [.year, .month, .day, .hour, .minute], from: iPhoneStevenoteDate, to: iPadStevenoteDate ) let years = yearsMonthsDaysHoursMinutesBetweenStevenotes.year! let months = yearsMonthsDaysHoursMinutesBetweenStevenotes.month! let days = yearsMonthsDaysHoursMinutesBetweenStevenotes.day! let hours = yearsMonthsDaysHoursMinutesBetweenStevenotes.hour! let minutes = yearsMonthsDaysHoursMinutesBetweenStevenotes.minute! print("There were \(years) years, \(months) months, \(days) days, \(hours) hours, and \(minutes) minutes between the the iPhone Stevenote of 2007 and the iPad Stevenote of 2010.") // Date addition, part 1: // What’s the last day of a 90-day warranty that starts today? // =========================================================== let lastDay = userCalendar.date(byAdding: .day, value: 90, to: Date())! print("90 days from now is: \(lastDay.description(with: Locale(identifier: "en_US")))") // Date addition, part 2: What was the date 5 weeks ago? // ===================================================== let fiveWeeksAgo = userCalendar.date(byAdding: .weekOfYear, value: -5, to: Date())! print("5 weeks ago was: \(fiveWeeksAgo.description(with: Locale(identifier: "en_US")))") // Date addition, part 3: // What time will it be 4 hours and 30 minutes from now, and // 4 hours and 30 minutes ago? // ========================================================= // What time will it be 4 hours and 30 minutes from now? // First, we need to define a DateComponents struct representing // a time interval of 4 hours and 30 minutes var fourHoursThirtyMinutes = DateComponents() fourHoursThirtyMinutes.hour = 4 fourHoursThirtyMinutes.minute = 30 // Now add the interval to the Date let fourHoursThirtyMinutesFromNow = userCalendar.date( byAdding: fourHoursThirtyMinutes, to: Date() )! print("4 hours and 30 minutes from now will be: \(fourHoursThirtyMinutesFromNow.description(with: Locale(identifier: "en_US")))") // What time was it 4 hours and 30 minutes ago? var minusFourHoursThirtyMinutes = DateComponents() minusFourHoursThirtyMinutes.hour = -4 minusFourHoursThirtyMinutes.minute = -30 let fourHoursThirtyMinutesAgo = userCalendar.date( byAdding: fourHoursThirtyMinutes, to: Date() )! print("4 hours and 30 minutes ago was: \(fourHoursThirtyMinutesAgo.description(with: Locale(identifier: "en_US")))") // Date comparisons, part 2: Making Date comparisons a little more “human” // ======================================================================= // Let's define some Dates relative to the SwiftUI announcement // (June 3, 2019, 12:08 p.m. PDT) let swiftUIAnnouncementDateComponents = DateComponents( timeZone: TimeZone(abbreviation: "PDT"), year: 2019, month: 6, day: 3, hour: 12, minute: 8 ) let swiftUIAnnouncement = userCalendar.date(from: swiftUIAnnouncementDateComponents)! let swiftUIAnnouncementPlusOneSecond = userCalendar.date( byAdding: .second, value: 1, to: swiftUIAnnouncement )! let swiftUIAnnouncementPlusFiveMinutes = userCalendar.date( byAdding: .minute, value: 5, to: swiftUIAnnouncement )! let swiftUIAnnouncementPlusThreeHours = userCalendar.date( byAdding: .hour, value: 3, to: swiftUIAnnouncement )! // This returns false, because when measuring time at the granularity of a SECOND, // swiftUIAnnouncement happens BEFORE swiftUIAnnouncementPlusOneSecond. let test1 = userCalendar.compare(swiftUIAnnouncement, to: swiftUIAnnouncementPlusOneSecond, toGranularity: .second) == .orderedSame print("test1: \(test1)") // This returns true, because when measuring time at the granularity of a SECOND, // swiftUIAnnouncement happens BEFORE swiftUIAnnouncementPlusOneSecond. let test2 = userCalendar.compare(swiftUIAnnouncement, to: swiftUIAnnouncementPlusOneSecond, toGranularity: .second) == .orderedAscending print("test2: \(test2)") // This returns true, because when measuring time at the granularity of a MINUTE, // swiftUIAnnouncement happens AT THE SAME TIME AS swiftUIAnnouncementPlusOneSecond. let test3 = userCalendar.compare(swiftUIAnnouncement, to: swiftUIAnnouncementPlusOneSecond, toGranularity: .minute) == .orderedSame print("test3: \(test3)") // This returns true, because when measuring time at the granularity of an HOUR, // swiftUIAnnouncement happens AT THE SAME TIME AS swiftUIAnnouncementPlusFiveMinutes. let test4 = userCalendar.compare(swiftUIAnnouncement, to: swiftUIAnnouncementPlusFiveMinutes, toGranularity: .hour) == .orderedSame print("test4: \(test4)") // This returns true, because when measuring time at the granularity of a MINUTE, // swiftUIAnnouncementPlusFiveMinutes happens AFTER swiftUIAnnouncement. let test5 = userCalendar.compare(swiftUIAnnouncementPlusFiveMinutes, to: swiftUIAnnouncement, toGranularity: .minute) == .orderedDescending print("test5: \(test5)") // This returns true, because when measuring time at the granularity of a DAY, // swiftUIAnnouncement happens AT THE SAME TIME AS swiftUIAnnouncementPlusThreeHours. let test6 = userCalendar.compare(swiftUIAnnouncement, to: swiftUIAnnouncementPlusThreeHours, toGranularity: .day) == .orderedSame print("test6: \(test6)")
You can download the playground here (3KB, zipped Xcode playground file).
In the next installment, we’ll look at making working with dates and times in Swift 5 even better with with some syntactic magic.