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.

Setting Up the Playground

Enter a name, tell Xcode where you'd like to save the playground, and hit create.

Setting Up the Playground

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.

Date Interval Initialization

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)

Date Interval Initialization

The same rule applies. If the start date is later than the end date, an exception is thrown.

Date Interval Initialization Exception

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 type Date
  • end of type Date
  • and duration of type TimeInterval

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)

Calculating the Intersection Between Two Date Intervals In Swift

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.

Formatting a Date Interval With DateIntervalFormatter

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.