TimeInterval, Date, and DateInterval
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.
Time
(née NSTime
) 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,
Time
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 : Reference Convertible, Comparable, Equatable {
public typealias Reference Type = NSDate
fileprivate var _time: Time Interval
…
}
The reference date, in this case, is the first instant of January 1, 2001, Greenwich Mean Time (GMT).
Date Intervals and Time Intervals
Date
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 Time
, 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 date
makes this really easy to do:
let calendar = Calendar.current
let date = Date()
let date Interval = calendar.date Interval(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 dst Components = Date Components(year: 2018,
month: 11,
day: 4)
calendar.date Interval(of: .day,
for: calendar.date(from: dst Components)!)?.duration
// 90000 seconds
It’s 2018.
Don’t you think that it’s time you stopped hard-coding seconds
?
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 Date
:
import Foundation
let calendar = Calendar.current
// Simon Vouet
// 9 January 1590 – 30 June 1649
let vouet =
Date Interval(start: calendar.date(from:
Date Components(year: 1590, month: 1, day: 9))!,
end: calendar.date(from:
Date Components(year: 1649, month: 6, day: 30))!)
// Peter Paul Rubens
// 28 June 1577 – 30 May 1640
let rubens =
Date Interval(start: calendar.date(from:
Date Components(year: 1577, month: 6, day: 28))!,
end: calendar.date(from:
Date Components(year: 1640, month: 5, day: 30))!)
let overlap = rubens.intersection(with: vouet)!
calendar.date Components([.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 Date
to provide a nice representation of that time period:
let formatter = Date Interval Formatter()
formatter.time Style = .none
formatter.date Template = "%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.