Whenever I'm working with dates, I end up creating extensions to make my life a little bit easier. A few months ago I noticed that Apple introduced a brand new type to the Foundation framework, which makes working with dates easier, the DateInterval
structure in Swift or, if you prefer Objective-C, the NSDateInterval
class. Both types are available on iOS 10.0+, tvOS 10.0+, macOS 10.12+, and watchOS 3.0+.
In this episode, I show you how to use the DateInterval
structure and why it's such an interesting addition to the Foundation framework. The NSDateInterval
class is very similar in use.
Setting Up a Playground
Open Xcode and create a new playground by choosing the Blank template from the iOS section.
Enter a name, tell Xcode where you'd like to save the playground, and hit create.
Remove the contents of the playground and add an import statement for Foundation, the framework in which the DateInterval
structure is defined.
import Foundation
What Is DateInterval
As the name implies, a DateInterval
instance defines an interval between two dates. Even though the DateInterval
structure is a small and simple type, what I like is that it abstracts the details of working with date intervals. This is reflected by one of the initializers the DateInterval
structure defines, init(start:end:)
.
import Foundation
let now = Date()
let tomorrow = now.addingTimeInterval(24.0 * 3600.0)
let dateInterval = DateInterval(start: now, end: tomorrow)
Creating a DateInterval Instance
The DateInterval
structure defines three initializers. Let's take a look at each of these initializers.
init(start:duration:)
The init(start:duration:)
initializer of the DateInterval
structure accepts a start date and a duration of type TimeInterval
. Because the start date of a date interval needs to be earlier than the end date, the duration cannot be less than 0
. An exception is thrown if you pass a negative duration to the initializer.
import Foundation
let now = Date()
let dateInterval = DateInterval(start: now, duration: 3.0 * 24.0 * 3600.0)
The playground shows a description of the date interval.
init(start:end:)
We already covered the init(start:end:)
initializer. It accepts a start date and an end date.
import Foundation
let now = Date()
let tomorrow = now.addingTimeInterval(24.0 * 3600.0)
let dateInterval = DateInterval(start: now, end: tomorrow)
The same rule applies. If the start date is later than the end date, an exception is thrown.
init()
If you create a DateInterval
instance by invoking init()
, the startDate
and endDate
properties are equal. This means the value of the duration
property is equal to 0
. But that's rarely what you want.
Properties
I already mentioned that the DateInterval
structure is a very simple, lightweight type. It defines three properties:
start
of typeDate
end
of typeDate
- and
duration
of typeTimeInterval
Operations
What makes the DateInterval
structure interesting is the range of operations you can perform on DateInterval
instances. Not only can you compare date intervals, you can calculate intersections or you can ask an instance whether it contains a particular date.
This may sound obvious, but I like that this tiny type encapsulates the logic for performing such operations. Date operations are often messy and prone to errors. The DateInterval
structure makes this less of a problem.
My favorite method of the DateInterval
structure is the intersection(with:)
method. This method accepts another date interval and returns the intersection of the date intervals.
import Foundation
let start1 = Date(timeIntervalSinceNow: -470482.0)
let end1 = Date(timeIntervalSinceNow: 20482.0)
let start2 = start1.addingTimeInterval(112560.0)
let end2 = end1.addingTimeInterval(-222304.0)
let dateInterval1 = DateInterval(start: start1, end: end1)
let dateInterval2 = DateInterval(start: start2, end: end2)
let intersection = dateInterval1.intersection(with: dateInterval2)
Formatting Date Intervals
The DateIntervalFormatter
class has been around since iOS 8 and macOS 10.10. Apple added a convenience method in iOS 10 and macOS 10.12 to add support for the DateInterval
structure. This is how it works.
import Foundation
let start1 = Date(timeIntervalSinceNow: -470482.0)
let end1 = Date(timeIntervalSinceNow: 20482.0)
let start2 = start1.addingTimeInterval(112560.0)
let end2 = end1.addingTimeInterval(-222304.0)
let dateInterval1 = DateInterval(start: start1, end: end1)
let dateInterval2 = DateInterval(start: start2, end: end2)
let intersection = dateInterval1.intersection(with: dateInterval2)
if let intersection = dateInterval1.intersection(with: dateInterval2) {
// Initialize Date Interval Formatter
let dateIntervalFormatter = DateIntervalFormatter()
// Configure Date Interval Formatter
dateIntervalFormatter.dateStyle = .short
dateIntervalFormatter.timeStyle = .medium
// String From Date Interval
dateIntervalFormatter.string(from: intersection)
}
We initialize an instance of the DateIntervalFormatter
class, specify the date and time style, and invoke the string(from:)
method, passing in a DateInterval
instance.
Wrapping Up
The Foundation framework is packed with little know classes, structures, and functions that make your life as a developer easier. The DateInterval
structure is one such type. Give it a try the next time you're working with dates and date intervals.