Time​Interval, Date, and Date​Interval

Nestled between Madrid’s Centro and Salamanca districts, just a short walk from the sprawling Buen Retiro Park, The Prado Museum boasts an extensive collection of works from Europe’s most celebrated painters. But if, during your visit, you begin to tire of portraiture commissioned by 17th-century Spanish monarchs, consider visiting the northernmost room of the 1st floor — Sala 002. There you’ll find this Baroque era painting by the French artist Simon Vouet.

You’d be forgiven for wondering why this pair of young women, brandishing a hook and spear, stand menacingly over a cowering old man while a mob of cherubim tears at his back. It is, of course, allegorical: reading the adjacent placard, you’ll learn that this piece is entitled Time defeated by Hope and Beauty. The old man? That’s Time. See the hourglass in his hand and scythe at his feet?

Take a moment, standing in front of this painting, to reflect on the enigmatic nature of time. Think now about how our limited understanding of time is reflected in — or perhaps exacerbated by — the naming of the Foundation date and time APIs.

It’s about time we got them straight.


Seconds are the fundamental unit of time. They’re also the only unit that has a fixed duration.

Months vary in length (“30 days hath September”), as do years (“53 weeks hath 71 years every cycle of 400”). Certain years pick up an extra day (leap years are misnamed if you think about it), and days gain and lose an hour from daylight saving time (thanks, Benjamin Franklin). And that’s to say nothing of leap seconds, which are responsible for such oddities as the 61 second minute, the 3601 second hour, and, of course, the 1209601 second fortnight.

TimeInterval (née NSTimeInterval) is a typealias for Double that represents duration as a number of seconds. You’ll see it as a parameter or return type for APIs that deal with a duration of time. Being a double-precision floating-point number, TimeInterval can represent submultiples in its fraction, (though for anything beyond millisecond precision, you’ll want to use something else).

Date and Time

It’s unfortunate that the Foundation type representing time is named Date. Colloquially, one typically distinguishes “dates” from “times” by saying that the former has to do with calendar days and the latter has more to do with the time of day. But Date is entirely orthogonal from calendars, and contrary to its name represents an absolute point in time.

Another source of confusion for Date is that, despite representing an absolute point in time, it’s defined by a time interval since a reference date:

public struct Date : ReferenceConvertible, Comparable, Equatable {
    public typealias ReferenceType = NSDate

    fileprivate var _time: TimeInterval

    
}

The reference date, in this case, is the first instant of January 1, 2001, Greenwich Mean Time (GMT).

Date Intervals and Time Intervals

DateInterval is a recent addition to Foundation. Introduced in iOS 10 and macOS Sierra, this type represents a closed interval between two absolute points in time (again, in contrast to TimeInterval, which represents a duration in seconds).

So what is this good for? Consider the following use cases:

Getting the Date Interval of a Calendar Unit

In order to know the time of day for a point in time — or what day it is in the first place — you need to consult a calendar. From there, you can determine the range of a particular calendar unit, like a day, month, or year. The Calendar method dateInterval(of:for:) makes this really easy to do:

let calendar = Calendar.current
let date = Date()
let dateInterval = calendar.dateInterval(of: .month, for: date)

Because we’re invoking Calendar, we can be confident in the result that we get back. Look how it handles daylight saving transition without breaking a sweat:

let dstComponents = DateComponents(year: 2018,
                                   month: 11,
                                   day: 4)
calendar.dateInterval(of: .day,
                      for: calendar.date(from: dstComponents)!)?.duration
// 90000 seconds

It’s 2018. Don’t you think that it’s time you stopped hard-coding secondsInDay = 86400?

Calculating Intersections of Date Intervals

For this example, let’s return to The Prado Museum and admire its extensive collection of paintings by Rubens — particularly this apparent depiction of the god of Swift programming.

Rubens, like Vouet, painted in the Baroque tradition. The two were contemporaries, and we can determine the full extent of how they overlap in the history of art with the help of DateInterval:

import Foundation

let calendar = Calendar.current

// Simon Vouet
// 9 January 1590 – 30 June 1649
let vouet =
    DateInterval(start: calendar.date(from:
        DateComponents(year: 1590, month: 1, day: 9))!,
                 end: calendar.date(from:
                    DateComponents(year: 1649, month: 6, day: 30))!)

// Peter Paul Rubens
// 28 June 1577 – 30 May 1640
let rubens =
    DateInterval(start: calendar.date(from:
                            DateComponents(year: 1577, month: 6, day: 28))!,
                 end: calendar.date(from:
                            DateComponents(year: 1640, month: 5, day: 30))!)

let overlap = rubens.intersection(with: vouet)!

calendar.dateComponents([.year],
                        from: overlap.start,
                        to: overlap.end) // 50 years

According to our calculations, there was a period of 50 years where both painters were living.

We can even take things a step further and use DateIntervalFormatter to provide a nice representation of that time period:

let formatter = DateIntervalFormatter()
formatter.timeStyle = .none
formatter.dateTemplate = "%Y"
formatter.string(from: overlap)
// "1590 – 1640"

Beautiful. You might as well print this code out, frame it, and hang it next to The Judgement of Paris.


The fact is, we still don’t really know what time is (or if it even actually exists). But I’m hopeful that we, as developers, will find the beauty in Foundation’s Date APIs, and in time, learn how to overcome our lack of understanding.

That does it for this week’s article. See you all next time.

NSMutableHipster

Questions? Corrections? Issues and pull requests are always welcome.

This article uses Swift version 4.2. Find status information for all articles on the status page.

Written by Mattt
Mattt

Mattt (@mattt) is a writer and developer in Portland, Oregon.

Next Article

Getting code to compile is different than doing things correctly. But sometimes it takes the former to ultimately get to the latter.