Categories
Swift Kick

How to work with dates and times in Swift, part four: A more Swift-like way to get the time interval between two dates [Updated for Swift 2]

date and time

Update, August 26, 2015: I’ve updated this article so that its code works with Swift 2. It compiles under the latest version of Xcode 7, beta 6.

swift kick

In previous installments in this series, we’ve covered:

In this installment, we’ll make getting the time interval between two dates — which normally involves a lot of typing — a little more Swift-like.

One common date arithmetic operation is to determine the interval between two given dates. This is usually a clunky two-step process based on NSCalendar‘s components method, which expects at least three parameters:

  • The time components you want the method to return, such as years, months, days, hours, minutes, and seconds. This is expressed by ORing together NSCalendarUnit values, and
  • the two dates, in NSDate form.

Let’s look at how it works. First, we’ll need a couple of dates. Create a new playground and put the following code into it:

// Playground - noun: a place where people can play

import UIKit

let userCalendar = NSCalendar.currentCalendar()

// Let's create some dates to work with
// ====================================

// It's 3:45:30 a.m., New Year's Day. Time to go home.
let goHomeYoureDrunkTimeComponents = NSDateComponents()
goHomeYoureDrunkTimeComponents.year = 2015
goHomeYoureDrunkTimeComponents.month = 1
goHomeYoureDrunkTimeComponents.day = 1
goHomeYoureDrunkTimeComponents.hour = 3
goHomeYoureDrunkTimeComponents.minute = 45
goHomeYoureDrunkTimeComponents.second = 30
let goHomeYoureDrunkTime = userCalendar.dateFromComponents(goHomeYoureDrunkTimeComponents)!

// Let's create an NSDate representing Bad Poetry Day (August 18)
// at 4:20:10 p.m.
let badPoetryDayComponents = NSDateComponents()
badPoetryDayComponents.year = 2015
badPoetryDayComponents.month = 8
badPoetryDayComponents.day = 18
badPoetryDayComponents.hour = 16
badPoetryDayComponents.minute = 20
badPoetryDayComponents.second = 10
let badPoetryDay = userCalendar.dateFromComponents(badPoetryDayComponents)!

In your playground’s sidebar, you should see the string representations of those dates:

  • goHomeYoureDrunkTime should display as something like January 1, 2015 at 3:45 a.m., and
  • badPoetryDay should display as something like August 18, 2015 at 4:20 p.m..

Let’s find out how many days, hours, minutes, and seconds there are between goHomeYoureDrunkTime and badPoetryDay with the following code:

// (Previous code goes here)

// How many days, hours, minutes, and seconds between
// goHomeYoureDrunkTime and badPoetryDay?
let dayHourMinuteSecond: NSCalendarUnit = [.Day,
                                           .Hour,
                                           .Minute,
                                           .Second]
let difference = NSCalendar.currentCalendar().components(
  dayHourMinuteSecond,
  fromDate: goHomeYoureDrunkTime,
  toDate: badPoetryDay,
  options: [])
difference.day     // 229
difference.hour    // 12
difference.minute  // 34
difference.second  // 40

You should see from difference that there are 229 days, 12 hours, 34 minutes, and 40 seconds between the two dates. We did a lot of typing to get this result, and there should be a nicer way to do it. How about this:

// (Previous code goes here)

// A date subtraction operation that returns an NSDateComponents
// instance specifying the days, hours, miniutes and seconds
// between two given NSDates
// =============================================================

func -(lhs: NSDate, rhs: NSDate) -> NSDateComponents
{
  let dayHourMinuteSecond: NSCalendarUnit = [.Day,
                                             .Hour,
                                             .Minute,
                                             .Second]
  return NSCalendar.currentCalendar().components(dayHourMinuteSecond,
    fromDate: rhs,
    toDate: lhs,
    options: [])
}

// Let's test it:
let diff = badPoetryDay - goHomeYoureDrunkTime
diff.day     // 229
diff.hour    // 12
diff.minute  // 34
diff.second  // 40

With this code, we’ve overloaded the - operator, so that when both its operands are NSDates, it returns an NSDateComponents instance specifying the days, hours, minutes, and seconds between the two. I could’ve coded it so that it also returned the time in terms of months and years, but the size of those units vary depending on the month and year, while days, hours, minutes, and seconds always represent the same amount of time.

dates and times in swift - smallRelated articles

A very brief introduction to date formatting in Swift and iOS: The oversight in a mostly-good book on Swift programming led me down the path of writing articles about dates and times in Swift, starting with this one, where I look atNSDateFormatter.

How to work with dates and times in Swift, part one: An introduction of Cocoa’s date and time classes, and how they work together. This article covers UTC (Coordinated Universal Time), and the key classes: NSDate, NSCalendar, NSDateComponents.

How to work with dates and times in Swift, part two: Calculations with dates: Now that we’ve got the basics, it’s time to do some date arithmetic: comparing two dates to see which one is the earlier and later one, finding out how far apart two dates are, and adding and subtracting from dates.

How to work with dates and times in Swift, part three: Making date arithmetic more Swift-like: Cocoa’s date and time classes have an Objective-C heritage, which in the Swift context, feel kind of clunky. In this article, I look at ways — and by ways, I mean helper functions and class extensions — to make date calculations feel more like Swift.

Categories
Uncategorized

What the Sex Pistols and Tampa Bay Startup Week have in common

never mind the bollocks

Here’s a story that anyone who’s taking part in any of the activities of Tampa Bay Startup Week — or wishes they could take part — should read. It’s a story about a seemingly insignificant gathering of like-minded people, and how the ripples of what its attendees did can still be felt today, an ocean away…

It’s June of 1976 in Manchester, England, and a small group of people gather in a tiny venue called the Lesser Free Trade Hall to see a band play. There’s nothing really remarkable about this group of 42 people, and that evening’s featured musicians are unknown at the time.

The band calls themselves the Sex Pistols.

The Sex Pistols.

As I mentioned, there were no famous people in the crowd at this show, or at the follow-up show that happened about a month later. The Sex Pistols had not yet caused an uproar throughout Britain with songs like Anarchy in the UK and God Save the Queen, and it was well before they invaded the US in 1978.

Attendees ranged from the local mailman to a few rebellious school children. But a handful of others in that small audience became some of the most influential people in independent and now mainstream music.

A gig attended by a few dozen in a venue that could easily hold hundreds would normally be considered a flop, but turned out to be anything but an ordinary concert. The influence of the Sex Pistols and the punk rock movement they helped kickstart can still be heard today in every band that features a spikey-haired youngling beating rapid power chords on a guitar. Johnny Rotten would later found the more experimental Public Image Ltd, and manager Malcolm McLaren would cast his musical net even wider, branching out into disco, funk, hip, electronic music, world music, and even opera.

That “handful of others” in the audience were just as important. Among them were:

These output of the bands that arose from this one gig would help define alternative rock and its subgenres, from punk to goth to synthpop to grunge, for decades to come. All this came from a concert that almost nobody cared about at the time, attended by people nobody had heard of at the time.

“The gig that changed the world,” as alt-rock aficionados sometimes call it, did so because it brought together people with similar interests who were passionate about what they did. Its attendees saw that popular music was changing, and after being inspired by a group of troublemakers, decided that they could be part of that change. They went on to create music their way, and make their mark on the world.

tb startup week organizers

The people behind Tampa Bay Startup Week (pictured above) may not look punk rock, but they’ve most certainly got its DIY, “we have an idea and we’re going for it” spirit. Like the Sex Pistols, they’re a band of troublemakers putting on an event on a shoestring budget (yes, Chase is sponsoring, but without them, the budget would likely go from shoestring to none), and at the moment, it isn’t being noticed by most of the world outside “the other bay area”.

Like the music scene in Manchester the mid-1970s, the work-life dynamic in Tampa Bay in the mid 2010s is undergoing some big changes:

If you look carefully, you can see the initial rumblings of change here, from the One Million Cups gathering that takes place every Wednesday to all the local interest in The Iron Yard to places like The HiveTampa Hackerspace, and Eureka! Factory to the ex-Marine who’s doing good and helping your beard feel good at the same time. I see a lot of the necessary ingredients for change here that I saw in Toronto in the mid-2000s, and so does GeekWire…and with a subtropical climate to boot!

I hope that like those 42 people who attended that Sex Pistols concert in 1976, that some of the people at Tampa Bay Startup Week’s events will get inspired, start their own businesses, and shake the universe.

(I’ll be at tonight’s tech cocktail mixer with my accordion. If you ask, I’ll gladly play you my rendition of Anarchy in the UK.)

Upcoming Tampa Bay Startup Week events

Today:

Tomorrow:

This article also appears in my personal blog, The Adventures of Accordion Guy in the 21st Century.

Categories
Current Events Tampa Bay Uncategorized

Scenes from Tampa Bay Startup Week’s kickoff party

tampa bay startup week

Photo by David Betz.

tampa bay startup week buttonMonday marked the beginning of Tampa Bay Startup Week, a five-day-long series of events meant to bring creatives, techies, entrepreneurs, and anyone who’s a combination of any of those together to meet, plot, and party. There’s a small but interesting tech scene here in the Tampa Bay area, and a number of factors including the subtropical climate, low cost of living, and the influx of people to the area — you might call it a brain gain — could help it grow dramatically over the next few years.

joey and anitra at startup week tampa bay kickoff

Me and Anitra, working the room. Photo by Laicos.

The week’s kickoff party took place at the Chase Basecamp, a rented venue on 7th Avenue, the main street of Ybor City (pronounced “EE-bor”), Tampa’s nightlife and party neighborhood. The Basecamp (located at the corner of 7th Avenue and 20th Street), serves as the central meeting place for Startup Week participants, as well as a venue for many of the scheduled events.

tbstartupweek kickoff 1

Photo by Laicos.

While chatting up the people from local mobile development shop Sourcetoad, I was introduced to the friendly-looking gentleman below, who went up to me and said “I just have to tell you, I love that accordion!”

bob buckhorn 1

Photo by Laicos.

As he walked away, Anitra told me that I just shook hands with Bob Buckhorn, mayor of Tampa. I’m a relatively recent transplant from Toronto, so I’ve never seen a photo of him, and I’m too used to picturing the mayor as either a sweaty, drug- and booze-addled, embarrassing mess, or too attached to highfalutin’ extravaganzas that are full of sound and fury but ultimately signifying nothing to care about a small grassroots effort like this one. I’m also not used to a mayor with his approval rating.

bob buckhorn 3

Photo by Yours Truly.

He gave a short speech to the crowd, in which he encouraged everyone to meet other people of like minds and ambitions, do what we do, “be a little crazy”, disrupt things, and start businesses. He talked about the brain drain that existed until recently, when people would leave Tampa in search of their fortunes. The situation has been turned around, what with Florida being one of the most moved-to states in the U.S. (as of this writing, it’s the third most populous state, after California and Texas), the population growth in the Tampa Bay/Jacksonville corridor and “Orlampa”, and Penske rental truck data that suggests that the Tampa Bay/Sarasota area is in the top 10 most moved-to locales. He asked the group to keep working to make Tampa a better place to be, if only to make sure that his daughters don’t move away to Atlanta, Austin, or anyplace else.

The money quote that got the audience to really put their hands together:

“I want Tampa Bay to be the economic engine of the southeast.”

It’s bold. It’s ambitious. I like it.

After all the speechifying, he then did what any good mayor would do: take control of the decks and drop a fat beat.

bob buckhorn 2

Photo by Laicos.

Anitra and I spent the rest of the evening either catching up with or getting to know the people in attendance, including:

tampa bay startup week banner

Here’s what’s happening with Tampa Bay Startup Week today and tomorrow. These events are free — just visit the Tampa Bay Startup Week site and sign up!

Today (Tuesday, February 3):

Tomorrow (Wednesday, February 4):

This article also appears in my personal blog, The Adventures of Accordion Guy in the 21st Century.

Categories
Swift Kick

How to work with dates and times in Swift, part three: Making date arithmetic more Swift-like [Updated for Swift 2]

date and time

Update, August 26, 2015: I’ve updated this article so that its code works with Swift 2. It compiles under the latest version of Xcode 7, beta 6.

swift kickSo far in this series, we’ve looked at:

So far, everything we’ve done has a distinctly un-Swift-like feel to it. That’s because Cocoa’s date and time classes were built with its original programming language, Objective-C, in mind. In this article, we’ll look at ways to make date calculations feel more “human” and Swift-like.

Make date comparisons more Swift-like

Let’s start with a new playground and some quick definitions:

  • A reference to the user’s calendar,
  • an NSDateFormatter and format string that makes it easy to define dates in a hurry,
  • and two dates:
    • Valentine’s Day (February 14, 2015 at midnight)
    • St. Patrick’s Day (March 17, 2015 at midnight)

Here’s what your code should look like:

// Playground - noun: a place where people can play

import UIKit

let userCalendar = NSCalendar.currentCalendar()

// Let's create some dates to work with
// ====================================

// If you need to instantiate a number of pre-defined dates,
// an NSDateFormatter will get it done in the fewest number of lines.
let dateMaker = NSDateFormatter()
dateMaker.dateFormat = "yyyy/MM/dd hh:mm:ss Z"

// I'm setting these dates according to my time zone (UTC-0500);
// feel free to adjust the values according to your time zone.
let valentinesDay = dateMaker.dateFromString("2015/02/14 00:00:00 -05:00")!
let stPatricksDay = dateMaker.dateFromString("2015/03/17 00:00:00 -05:00")!

In the previous article, we looked at NSDate's compare method, which compares two NSDates and returns a result of type NSComparisonResult as shown in the table below:

If… compare returns…
the first date is earlier than the second date .OrderedAscending
the first date is equal to the second date .OrderedSame
the first date is later than the second date .OrderedDescending

Add the following code to your playground:

// (Previous code goes here)

// Comparing dates, part 1
// =======================

// First, the clunky NSDate::compare way
// -------------------------------------

// Returns true because Valentine's Day comes before St. Patrick's Day
valentinesDay.compare(stPatricksDay) == .OrderedAscending

// Returns true because they're the same date
valentinesDay.compare(valentinesDay) == .OrderedSame

// Returns true because St. Patrick's Day comes after Valentine's Day
stPatricksDay.compare(valentinesDay) == .OrderedDescending

The compare method works well, but its syntax has that C-style clunkiness. It’s a bit jarring in Swift, which has a lot of features that so-called “scripting” languages have. Wouldn’t it be nice if we could compare dates using the ==, <, and > operators?

Let’s make it happen. Add the following code to your playground:

// (Previous code goes here)

// Let's overload the <, >, and == operators to do date comparisons
// ----------------------------------------------------------------

func ==(lhs: NSDate, rhs: NSDate) -> Bool
{
  return lhs === rhs || lhs.compare(rhs) == .OrderedSame
}

func <(lhs: NSDate, rhs: NSDate) -> Bool
{
  return lhs.compare(rhs) == .OrderedAscending
}

func >(lhs: NSDate, rhs: NSDate) -> Bool
{
  return lhs.compare(rhs) == .OrderedDescending
}

// Comparisons are less clunky now!
// --------------------------------

valentinesDay < stPatricksDay   // true
valentinesDay == valentinesDay  // true
stPatricksDay > valentinesDay   // true

With these functions, we’re simply overloading the ==, <, and > operators so that they work on NSDates and hide the clunky compare syntax behind some syntactic sugar. In case you’re wondering about the parameter names, lhs is short for “left-hand side” and rhs is short for “right-hand side”.

Note than in our overload of the == operator, there are a couple of ways that two dates can be considered equal:

  • Their compare result is NSComparisonResult.OrderedSame, or
  • the two dates being compared are the same NSDate object (=== is the identity operator; if a === b, then a and b both reference the same object).

Make date comparisons more “human”

picard data riker

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.

NSDate‘s compare method, and, by extension, the ==, <, and > overloads we defined, have the same problem with being overly precise. Let’s consider a case where we have two NSDates that are only a second apart:

  • Groundhog Day 2015 (February 2, 2015) at 12:00 a.m. EST
  • One second after Groundhog Day 2015 at 12:00 a.m. EST
// (Previous code goes here)

// Comparing dates, part 2
// =======================

// Let's create two dates that are only one second apart:
let groundhogDay = dateMaker.dateFromString("2015/02/02 00:00:00 -05:00")!
let groundhogDayPlus1Second = dateMaker.dateFromString("2015/02/02 00:00:01 -05:00")!

// This line returns false:
groundhogDay == groundhogDayPlus1Second
// This line returns true:
groundhogDay < groundhogDayPlus1Second
// NSDate's compare method is too precise for a lot of uses!

For most purposes, we’d consider midnight on Groundhog Day and one second after midnight Groundhog Day the to be the same time. We need a way to do date comparisons at granularities other than seconds.

If you’re targeting iOS 8 or later, such a way already exists: NSCalendar‘s compareDate method! It expects the following parameters:

Parameter Description
fromDate The first date in the comparison.
toDate The other date in the comparison.
toUnitGranularity The level of precision for the comparison, expressed as an NSCalendarUnit value, which includes:

  • .CalendarUnitSecond
  • .CalendarUnitMinute
  • .CalendarUnitHour
  • .CalendarUnitDay
  • .CalendarUnitMonth
  • .CalendarUnitYear

This is a Cocoa method with the word “compare” in its name, and you’ve probably guessed that its return type is NSComparisonResult. Here’s what it returns:

If… compareDate returns…
fromDate is earlier than toDate, when compared at the specified level of precision .OrderedAscending
fromDate is equal to toDate, when compared at the specified level of precision .OrderedSame
fromDate is later than toDate, when compared at the specified level of precision .OrderedDescending

Let’s try compareDate out:

// (Previous code goes here)

// Enter NSCalendar's compareDate method, available in iOS 8 and later

// This returns false, because 2015/02/02 00:00:00 and 2015/02/02 00:00:01
// are NOT both within the same SECOND.
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayPlus1Second,
  toUnitGranularity: .Second)
  == .OrderedSame

// This returns true, because 2015/02/02 00:00:00 and 2015/02/02 00:00:01
// ARE both within the same MINUTE.
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayPlus1Second,
  toUnitGranularity: .Minute)
  == .OrderedSame

// This returns true, because 2015/02/02 00:00:00 and 2015/02/02 00:00:01
// ARE both within the same HOUR.
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayPlus1Second,
  toUnitGranularity: .Hour)
  == .OrderedSame

// This returns true, because 2015/02/02 00:00:00 and 2015/02/02 00:00:01
// ARE both within the same DAY.
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayPlus1Second,
  toUnitGranularity: .Day)
  == .OrderedSame

Note that compareDate checks to see if the two given dates are in the same given time period. It doesn’t check to see if the two given dates are separated at most by the given time period. If you’re finding that distinction hard to follow, don’t worry; it’s hard to explain.

It’s easy to demonstrate, however. Suppose we create a new NSDate that represents one second before Groundhog Day and run some compareDate tests on it:

// (Previous code goes here)

// 1 second before Groundhog Day, it was the previous day.
// Note what happens when we use compareDate:
let groundhogDayMinus1Second = dateMaker.dateFromString("2015/02/01 11:59:59 -05:00")!

// This returns false, because 2015/02/02 00:00:00 and 2015/02/01 11:59:59
// are NOT both within the same SECOND.
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayMinus1Second,
  toUnitGranularity: .Second)
  == .OrderedSame

// This returns false, because while 2015/02/02 00:00:00 and 2015/02/01 11:59:59
// ARE within a minute of each other, they're not both within the SAME MINUTE
// (00:00 vs. 11:59).
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayMinus1Second,
  toUnitGranularity: .Minute)
  == .OrderedSame

// This returns false, because while 2015/02/02 00:00:00 and 2015/02/01 11:59:59
// ARE within an hour of each other, they're not both within the SAME HOUR
// (0 vs. 11).
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayMinus1Second,
  toUnitGranularity: .Hour)
  == .OrderedSame

// This returns false, because while 2015/02/02 00:00:00 and 2015/02/01 11:59:59
// ARE within a day of each other, they're not both within the SAME DAY
// (February 2 vs. February 1).
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayMinus1Second,
  toUnitGranularity: .Day)
  == .OrderedSame

// This returns true, because while 2015/02/02 00:00:00 and 2015/02/01 11:59:59
// ARE in the same month.
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayMinus1Second,
  toUnitGranularity: .Month)
  == .OrderedSame

Note that compareDate isn’t available in iOS versions prior to 8. Code targeting iOS 7 or earlier will require writing an equivalent method, which I’ll leave as an exercise for the reader.

Making date arithmetic more Swift-like

You may have noticed in the code so far that I’ve been creating NSDates by using an instance of NSDateFormatter and a defined format string. That’s because this approach uses fewer lines than creating an NSDateComponents instance, setting its properties, then using a calendar to use the NSDateComponents instance to create an NSDate. Unfortunately, there’s no built-in quick way to build an NSDateComponents instance that represents an interval of time.

This means that answering questions like “What will the date be 1 month, 8 days, 6 hours, and 17 minutes after Groundhog Day?” requires a lot of yak shaving, my favorite term for “tedious setting-up”:

// (Previous code goes here)

// Date arithmetic
// ===============

// The clunky way
// --------------

// Suppose we want to get the date of Groundhog Day plus
// 1 month, 8 days, 6 hours, and 17 minutes
let timeInterval = NSDateComponents()
timeInterval.month = 1
timeInterval.day = 8
timeInterval.hour = 6
timeInterval.minute = 17
// The resulting date should be March 10, 2015, 6:17 a.m.
let resultDate = userCalendar.dateByAddingComponents(timeInterval,
  toDate: groundhogDay,
  options: [])!

This approach is a clunky Objective-C-flavored way of doing things. I’d much rather do this calculation with code that looked like this:

let resultDate = groundhogDay + 1.months + 8.days + 6.hours + 17.minutes

Luckily, we’re working with Swift. Some judicious use of operator overloading and extensions will let us do just that!

First, we need to overload some operators to simplify date component arithmetic:

// (Previous code goes here)

// The neat way
// ------------

// First, we define methods that allow us to add and subtract
// NSDateComponents instances

// The addition and subtraction code is nearly the same,
// so we've factored it out into this method
func combineComponents(lhs: NSDateComponents,
  rhs: NSDateComponents,
  _ multiplier: Int = 1)
  -> NSDateComponents
{
  let result = NSDateComponents()
  let undefined = Int(NSDateComponentUndefined)
  
  result.second = ((lhs.second != undefined ? lhs.second : 0) +
    (rhs.second != undefined ? rhs.second : 0) * multiplier)
  result.minute = ((lhs.minute != undefined ? lhs.minute : 0) +
    (rhs.minute != undefined ? rhs.minute : 0) * multiplier)
  result.hour = ((lhs.hour != undefined ? lhs.hour : 0) +
    (rhs.hour != undefined ? rhs.hour : 0) * multiplier)
  result.day = ((lhs.day != undefined ? lhs.day : 0) +
    (rhs.day != undefined ? rhs.day : 0) * multiplier)
  result.month = ((lhs.month != undefined ? lhs.month : 0) +
    (rhs.month != undefined ? rhs.month : 0) * multiplier)
  result.year = ((lhs.year != undefined ? lhs.year : 0) +
    (rhs.year != undefined ? rhs.year : 0) * multiplier)
  return result
}

// With combineComponents defined,
// overloading + and - is simple

func +(lhs: NSDateComponents, rhs: NSDateComponents) -> NSDateComponents
{
  return combineComponents(lhs, rhs: rhs)
}

func -(lhs: NSDateComponents, rhs: NSDateComponents) -> NSDateComponents
{
  return combineComponents(lhs, rhs: rhs, -1)
}

// We'll need to overload unary - so we can negate components
prefix func -(components: NSDateComponents) -> NSDateComponents {
  let result = NSDateComponents()
  let undefined = Int(NSDateComponentUndefined)
  
  if(components.second != undefined) { result.second = -components.second }
  if(components.minute != undefined) { result.minute = -components.minute }
  if(components.hour != undefined) { result.hour = -components.hour }
  if(components.day != undefined) { result.day = -components.day }
  if(components.month != undefined) { result.month = -components.month }
  if(components.year != undefined) { result.year = -components.year }
  return result
}

I derived these functions from Axel Schlueter’s SwiftDateTimeExtensions library. He wrote them when Swift was still in beta; I updated them so that they compile with the current version and added a couple of tweaks of my own. They make it possible to:

  • Add the respective second, minute, hour, day, month, and year properties of two NSDateComponent instances,
  • subtract the second, minute, hour, day, month, and year properties of one NSDateComponents instance from the corresponding properties of another NSDateComponents instance, and
  • negate the second, minute, hour, day, month, and year properties of an NSDateComponents instance.

The addition and subtraction operations are so similar and so tedious; that’s a sign that there’s an opportunity to DRY up the code. That’s why we have the combineComponents method doing the work and the + and - overloads calling it with the right parameters. The combineComponents code is dense with ternary conditional operators, so I thought I’d explain what’s going on under the hood with this flowchart:

combineComponents

You may be wondering why we defined a negation method and didn’t use it when performing subtraction. That’s because the negation method simply ignores undefined components, while addition and subtraction require treating undefined component values as 0. The negation method comes in handy in other scenarios, which I’ll show later.

Now that we’ve got date component addition, subtraction, and negation defined, let’s extend the Int type so that it has some instance properties that let us define components with statements like 5.seconds, 3.minutes, 7.hours, 2.days, 4.weeks, and so on:

// (Previous code goes here)

// Next, we extend Int to bring some Ruby-like magic
// to date components

extension Int {
  
  var seconds: NSDateComponents {
    let components = NSDateComponents()
    components.second = self;
    return components
  }
  
  var second: NSDateComponents {
    return self.seconds
  }
  
  var minutes: NSDateComponents {
    let components = NSDateComponents()
    components.minute = self;
    return components
  }
  
  var minute: NSDateComponents {
    return self.minutes
  }
  
  var hours: NSDateComponents {
    let components = NSDateComponents()
    components.hour = self;
    return components
  }
  
  var hour: NSDateComponents {
    return self.hours
  }
  
  var days: NSDateComponents {
    let components = NSDateComponents()
    components.day = self;
    return components
  }
  
  var day: NSDateComponents {
    return self.days
  }
  
  var weeks: NSDateComponents {
    let components = NSDateComponents()
    components.day = 7 * self;
    return components
  }
  
  var week: NSDateComponents {
    return self.weeks
  }
  
  var months: NSDateComponents {
    let components = NSDateComponents()
    components.month = self;
    return components
  }
  
  var month: NSDateComponents {
    return self.months
  }
  
  var years: NSDateComponents {
    let components = NSDateComponents()
    components.year = self;
    return components
  }
  
  var year: NSDateComponents {
    return self.years
  }
  
}

Once again, I derived these functions from Axel Schlueter’s SwiftDateTimeExtensions library and added a couple of tweaks of my own.

There’s a little redundancy in the code above; it’s to allow for grammatically correct code. I didn’t like seeing code like 1.seconds, 1.minutes, 1.hours, 1.days, and so on.

With the date component addition and subtraction overloads and the extension to Int, building date components that represent time intervals is far less tedious:

// (Previous code goes here)

// Building an NSDateComponents instance that represents
// a time interval is now a lot nicer:
let newTimeInterval = 1.month + 8.days + 6.hours + 17.minutes
// Let's confirm that it works
newTimeInterval.month   // 1
newTimeInterval.day     // 8
newTimeInterval.hour    // 6
newTimeInterval.minute  // 17

With all our tweaks, adding components to dates using NSCalendar‘s dateByAddingComponents feels clunky by comparison. Here are some operator overloads that make this sort of coding more elegant if you’re working with dates and date components expressed in terms of the user’s current calendar:

// (Previous code goes here)

// Let's make it easy to add dates and components,
// and subtract components from dates

// Date + component
func +(lhs: NSDate, rhs: NSDateComponents) -> NSDate
{
  return NSCalendar.currentCalendar().dateByAddingComponents(rhs,
    toDate: lhs,
    options: [])!
}

// Component + date
func +(lhs: NSDateComponents, rhs: NSDate) -> NSDate
{
  return rhs + lhs
}

// Date - component
// (Component - date doesn't make sense)
func -(lhs: NSDate, rhs: NSDateComponents) -> NSDate
{
  return lhs + (-rhs)
}

With these methods, date arithmetic now looks like this:

// (Previous code goes here)

// Look at how easy date arithmetic is now:

// What's the date and time 2 weeks, 1 day, 13 hours, and 57 minutes
// after Groundhog Day 2015?
groundhogDay + 2.weeks + 1.day + 13.hours + 57.minutes
// (Answer: February 17, 2015, 1:57 p.m.)

// Adding dates to date components is quite flexible:
2.weeks + 1.day + 13.hours + 57.minutes + groundhogDay
2.weeks + 1.day + groundhogDay + 13.hours + 57.minutes

// What was the date 1 year, 2 months, and 12 days
// prior to Groundhog Day 2015?
groundhogDay - 1.year - 2.months - 12.days
/// (Answer: November 20, 2013)

That code is so much more pleasant to read (and write!).

And finally, a Ruby on Rails trick comes to Swift

Ruby on Rails lets you do very readable calculations like 2.days.from_now and 2.days.ago. We can bring that Rails magic to Swift by using everything we’ve build so far and extending NSDateComponents with two computer properties:

// (Previous code goes here)

// And finally some Ruby on Rails magic, that allows us to create dates
// with code like "2.days.fromNow" and "2.days.ago"

extension NSDateComponents {
  
  var fromNow: NSDate {
    let currentCalendar = NSCalendar.currentCalendar()
    return currentCalendar.dateByAddingComponents(self,
      toDate: NSDate(),
      options: [])!
  }
  
  var ago: NSDate {
    let currentCalendar = NSCalendar.currentCalendar()
    return currentCalendar.dateByAddingComponents(-self,
      toDate: NSDate(),
      options: [])!
  }
  
}

Equipped with everything we’ve made, we can now write code like this:

// (Previous code goes here)

// Let's test the Rails magic!
// (It's August 26, 2015, 23:06 as I execute this code)

// August 28, 2015, 11:06 p.m.
2.days.fromNow
// August 29, 2015, 2:23 a.m.
(2.days + 3.hours + 17.minutes).fromNow
// August 24, 2015, 11:06 p.m.
2.days.ago
// August 24, 2015, 7:49 p.m.
(2.days + 3.hours + 17.minutes).ago

The entire playground

And with that. we’ve got a more Swift-like way of doing date arithmetic. I’m going to take all these methods and extensions and post them as a library on GitHub, but in the meantime, here’s the complete playground for the exercises in this article. Go forth and write some readable date/time code!

// Date/time overloads and extensions Swift playground
// by Joey deVilla - August 2015
// Partially based on Axel Schlueter's SwiftDateTimeExtensions library
// (https://github.com/schluete/SwiftDateTimeExtensions)
//
// Released under the MIT License (MIT)
// (c) 2015 Joey deVilla


import UIKit

let userCalendar = NSCalendar.currentCalendar()

// Let's create some dates to work with
// ====================================

// If you need to instantiate a number of pre-defined dates,
// an NSDateFormatter will get it done in the fewest number of lines.
let dateMaker = NSDateFormatter()
dateMaker.dateFormat = "yyyy/MM/dd hh:mm:ss Z"

// I'm setting these dates according to my time zone (UTC-0500);
// feel free to adjust the values according to your time zone.
let valentinesDay = dateMaker.dateFromString("2015/02/14 00:00:00 -05:00")!
let stPatricksDay = dateMaker.dateFromString("2015/03/17 00:00:00 -05:00")!


// Comparing dates, part 1
// =======================

// First, the clunky NSDate::compare way
// -------------------------------------

// Returns true because Valentine's Day comes before St. Patrick's Day
valentinesDay.compare(stPatricksDay) == .OrderedAscending

// Returns true because they're the same date
valentinesDay.compare(valentinesDay) == .OrderedSame

// Returns true because St. Patrick's Day comes after Valentine's Day
stPatricksDay.compare(valentinesDay) == .OrderedDescending

// Let's overload the <, >, and == operators to do date comparisons
// ----------------------------------------------------------------

func ==(lhs: NSDate, rhs: NSDate) -> Bool
{
  return lhs === rhs || lhs.compare(rhs) == .OrderedSame
}

func <(lhs: NSDate, rhs: NSDate) -> Bool
{
  return lhs.compare(rhs) == .OrderedAscending
}

func >(lhs: NSDate, rhs: NSDate) -> Bool
{
  return lhs.compare(rhs) == .OrderedDescending
}

// Comparisons are less clunky now!
// --------------------------------

valentinesDay < stPatricksDay   // true
valentinesDay == valentinesDay  // true
stPatricksDay > valentinesDay   // true


// Comparing dates, part 2
// =======================

// Let's create two dates that are only one second apart:
let groundhogDay = dateMaker.dateFromString("2015/02/02 00:00:00 -05:00")!
let groundhogDayPlus1Second = dateMaker.dateFromString("2015/02/02 00:00:01 -05:00")!

// This line returns false:
groundhogDay == groundhogDayPlus1Second
// This line returns true:
groundhogDay < groundhogDayPlus1Second
// NSDate's compare method is too precise for a lot of uses!

// Enter NSCalendar's compareDate method, available in iOS 8 and later

// This returns false, because 2015/02/02 00:00:00 and 2015/02/02 00:00:01
// are NOT both within the same SECOND.
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayPlus1Second,
  toUnitGranularity: .Second)
  == .OrderedSame

// This returns true, because 2015/02/02 00:00:00 and 2015/02/02 00:00:01
// ARE both within the same MINUTE.
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayPlus1Second,
  toUnitGranularity: .Minute)
  == .OrderedSame

// This returns true, because 2015/02/02 00:00:00 and 2015/02/02 00:00:01
// ARE both within the same HOUR.
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayPlus1Second,
  toUnitGranularity: .Hour)
  == .OrderedSame

// This returns true, because 2015/02/02 00:00:00 and 2015/02/02 00:00:01
// ARE both within the same DAY.
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayPlus1Second,
  toUnitGranularity: .Day)
  == .OrderedSame

// 1 second before Groundhog Day, it was the previous day.
// Note what happens when we use compareDate:
let groundhogDayMinus1Second = dateMaker.dateFromString("2015/02/01 11:59:59 -05:00")!

// This returns false, because 2015/02/02 00:00:00 and 2015/02/01 11:59:59
// are NOT both within the same SECOND.
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayMinus1Second,
  toUnitGranularity: .Second)
  == .OrderedSame

// This returns false, because while 2015/02/02 00:00:00 and 2015/02/01 11:59:59
// ARE within a minute of each other, they're not both within the SAME MINUTE
// (00:00 vs. 11:59).
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayMinus1Second,
  toUnitGranularity: .Minute)
  == .OrderedSame

// This returns false, because while 2015/02/02 00:00:00 and 2015/02/01 11:59:59
// ARE within an hour of each other, they're not both within the SAME HOUR
// (0 vs. 11).
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayMinus1Second,
  toUnitGranularity: .Hour)
  == .OrderedSame

// This returns false, because while 2015/02/02 00:00:00 and 2015/02/01 11:59:59
// ARE within a day of each other, they're not both within the SAME DAY
// (February 2 vs. February 1).
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayMinus1Second,
  toUnitGranularity: .Day)
  == .OrderedSame

// This returns true, because while 2015/02/02 00:00:00 and 2015/02/01 11:59:59
// ARE in the same month.
userCalendar.compareDate(groundhogDay,
  toDate: groundhogDayMinus1Second,
  toUnitGranularity: .Month)
  == .OrderedSame


// Date arithmetic
// ===============

// The clunky way
// --------------

// Suppose we want to get the date of Groundhog Day plus
// 1 month, 8 days, 6 hours, and 17 minutes
let timeInterval = NSDateComponents()
timeInterval.month = 1
timeInterval.day = 8
timeInterval.hour = 6
timeInterval.minute = 17
// The resulting date should be March 10, 2015, 6:17 a.m.
let resultDate = userCalendar.dateByAddingComponents(timeInterval,
  toDate: groundhogDay,
  options: [])!


// The neat way
// ------------

// First, we define methods that allow us to add and subtract
// NSDateComponents instances

// The addition and subtraction code is nearly the same,
// so we've factored it out into this method
func combineComponents(lhs: NSDateComponents,
  rhs: NSDateComponents,
  _ multiplier: Int = 1)
  -> NSDateComponents
{
  let result = NSDateComponents()
  let undefined = Int(NSDateComponentUndefined)
  
  result.second = ((lhs.second != undefined ? lhs.second : 0) +
    (rhs.second != undefined ? rhs.second : 0) * multiplier)
  result.minute = ((lhs.minute != undefined ? lhs.minute : 0) +
    (rhs.minute != undefined ? rhs.minute : 0) * multiplier)
  result.hour = ((lhs.hour != undefined ? lhs.hour : 0) +
    (rhs.hour != undefined ? rhs.hour : 0) * multiplier)
  result.day = ((lhs.day != undefined ? lhs.day : 0) +
    (rhs.day != undefined ? rhs.day : 0) * multiplier)
  result.month = ((lhs.month != undefined ? lhs.month : 0) +
    (rhs.month != undefined ? rhs.month : 0) * multiplier)
  result.year = ((lhs.year != undefined ? lhs.year : 0) +
    (rhs.year != undefined ? rhs.year : 0) * multiplier)
  return result
}

// With combineComponents defined,
// overloading + and - is simple

func +(lhs: NSDateComponents, rhs: NSDateComponents) -> NSDateComponents
{
  return combineComponents(lhs, rhs: rhs)
}

func -(lhs: NSDateComponents, rhs: NSDateComponents) -> NSDateComponents
{
  return combineComponents(lhs, rhs: rhs, -1)
}

// We'll need to overload unary - so we can negate components
prefix func -(components: NSDateComponents) -> NSDateComponents {
  let result = NSDateComponents()
  let undefined = Int(NSDateComponentUndefined)
  
  if(components.second != undefined) { result.second = -components.second }
  if(components.minute != undefined) { result.minute = -components.minute }
  if(components.hour != undefined) { result.hour = -components.hour }
  if(components.day != undefined) { result.day = -components.day }
  if(components.month != undefined) { result.month = -components.month }
  if(components.year != undefined) { result.year = -components.year }
  return result
}

// Next, we extend Int to bring some Ruby-like magic
// to date components

extension Int {
  
  var seconds: NSDateComponents {
    let components = NSDateComponents()
    components.second = self;
    return components
  }
  
  var second: NSDateComponents {
    return self.seconds
  }
  
  var minutes: NSDateComponents {
    let components = NSDateComponents()
    components.minute = self;
    return components
  }
  
  var minute: NSDateComponents {
    return self.minutes
  }
  
  var hours: NSDateComponents {
    let components = NSDateComponents()
    components.hour = self;
    return components
  }
  
  var hour: NSDateComponents {
    return self.hours
  }
  
  var days: NSDateComponents {
    let components = NSDateComponents()
    components.day = self;
    return components
  }
  
  var day: NSDateComponents {
    return self.days
  }
  
  var weeks: NSDateComponents {
    let components = NSDateComponents()
    components.day = 7 * self;
    return components
  }
  
  var week: NSDateComponents {
    return self.weeks
  }
  
  var months: NSDateComponents {
    let components = NSDateComponents()
    components.month = self;
    return components
  }
  
  var month: NSDateComponents {
    return self.months
  }
  
  var years: NSDateComponents {
    let components = NSDateComponents()
    components.year = self;
    return components
  }
  
  var year: NSDateComponents {
    return self.years
  }
  
}


// Building an NSDateComponents instance that represents
// a time interval is now a lot nicer:
let newTimeInterval = 1.month + 8.days + 6.hours + 17.minutes
// Let's confirm that it works
newTimeInterval.month   // 1
newTimeInterval.day     // 8
newTimeInterval.hour    // 6
newTimeInterval.minute  // 17


// Let's make it easy to add dates and components,
// and subtract components from dates

// Date + component
func +(lhs: NSDate, rhs: NSDateComponents) -> NSDate
{
  return NSCalendar.currentCalendar().dateByAddingComponents(rhs,
    toDate: lhs,
    options: [])!
}

// Component + date
func +(lhs: NSDateComponents, rhs: NSDate) -> NSDate
{
  return rhs + lhs
}

// Date - component
// (Component - date doesn't make sense)
func -(lhs: NSDate, rhs: NSDateComponents) -> NSDate
{
  return lhs + (-rhs)
}

// Look at how easy date arithmetic is now:

// What's the date and time 2 weeks, 1 day, 13 hours, and 57 minutes
// after Groundhog Day 2015?
groundhogDay + 2.weeks + 1.day + 13.hours + 57.minutes
// (Answer: February 17, 2015, 1:57 p.m.)

// Adding dates to date components is quite flexible:
2.weeks + 1.day + 13.hours + 57.minutes + groundhogDay
2.weeks + 1.day + groundhogDay + 13.hours + 57.minutes

// What was the date 1 year, 2 months, and 12 days
// prior to Groundhog Day 2015?
groundhogDay - 1.year - 2.months - 12.days
/// (Answer: November 20, 2013)


// And finally some Ruby on Rails magic, that allows us to create dates
// with code like "2.days.fromNow" and "2.days.ago"

extension NSDateComponents {
  
  var fromNow: NSDate {
    let currentCalendar = NSCalendar.currentCalendar()
    return currentCalendar.dateByAddingComponents(self,
      toDate: NSDate(),
      options: [])!
  }
  
  var ago: NSDate {
    let currentCalendar = NSCalendar.currentCalendar()
    return currentCalendar.dateByAddingComponents(-self,
      toDate: NSDate(),
      options: [])!
  }
  
}

// Let's test the Rails magic!
// (It's August 26, 2015, 23:06 as I execute this code)

// August 28, 2015, 11:06 p.m.
2.days.fromNow
// August 29, 2015, 2:23 a.m.
(2.days + 3.hours + 17.minutes).fromNow
// August 24, 2015, 11:06 p.m.
2.days.ago
// August 24, 2015, 7:49 p.m.
(2.days + 3.hours + 17.minutes).ago

Related articles

dates and times in swift - small

A very brief introduction to date formatting in Swift and iOS: The oversight in a mostly-good book on Swift programming led me down the path of writing articles about dates and times in Swift, starting with this one, where I look atNSDateFormatter.

How to work with dates and times in Swift, part one: An introduction of Cocoa’s date and time classes, and how they work together. This article covers UTC (Coordinated Universal Time), and the key classes: NSDate, NSCalendar, NSDateComponents.

How to work with dates and times in Swift, part two: Calculations with dates: Now that we’ve got the basics, it’s time to do some date arithmetic: comparing two dates to see which one is the earlier and later one, finding out how far apart two dates are, and adding and subtracting from dates.

How to work with dates and times in Swift, part four: A more Swift-like way to get the time interval between two dates: This quick article shows you how to make an operator overload that makes getting the time interval between two dates more like subtraction.

Categories
Uncategorized

T-shirt of the day, and how to clear your (ahem) wizard friend’s browser history

delete my browser history shirt 2

You’d think that a wizard would know about “Privacy Mode” (which I sometimes refer to as “porn mode”), but I suppose one doesn’t pick up that sort of thing when one is on a mission to save Middle Earth.

This T-shirt is available right now for $15 plus free shipping at Woot!

How to delete your wizard friend’s (yeah, right) browser history

delete my browser history shirt

Should a wizard ever ask you to do him this favor, here’s how you do it. The instructions for Chrome, Firefox, and IE come straight from Woot!’s page for the T-shirt, and the instructions for Safari on Mac OS come from Yours Truly.

Instructions for Chrome:

  1. Open your browser
  2. Click the Chrome button in the top-right of the browser window
  3. Select History
  4. Click the Clear browsing data button
  5. From the drop down, select the duration of time you want to delete from your history. To delete all history, select the beginning of time
  6. Check the boxes of the data you would like to delete, including Browsing history and Download history
  7. Click the Clear browsing data button
  8. Slay balrog

For Mozilla Firefox:

  1. Open your browser
  2. Click the menu button in the top-right of the browser window
  3. Select History, then Clear Recent History
  4. Select the time range to clear. To clear all browser history, select Everything
  5. Click Clear Now
  6. Return to Middle Earth as angelic white wizard

For Internet Explorer:

  1. Open your browser
  2. Click the Tools button in the top-right of the browser window
  3. Select Safety, then Delete Browsing History
  4. Check the boxes of the data you would like to delete, including Temporary Internet files and website files, History and Download History
  5. Un-check Preserve favorite website data
  6. Click Delete
  7. Ride forth upon Shadowfax and lead the Rohirrim to victory at Hornburg

For Safari (on Mac OS)

  1. Open your browser
  2. Select Clear History… from the History menu
  3. Click Clear when the Are you sure you want to clear history? dialog appears
  4. Open Preferences by either selecting Preferences… from the Safari menu or typing ⌘, (the “command” and “,” keys simultaneously)
  5. Select the Privacy tab and click Remove all website data…
  6. Click Remove Now when the Are you sure you want to remove all data stored by websites on your computer? dialog appears
  7. Evade the omnipresent glance of the Eye of Sauron
Categories
Uncategorized

How we thought the internet would turn out 20 years ago, and what actually happened

how the internet turned out

Categories
Swift Kick

How to work with dates and times in Swift, part two: Calculations with dates [Updated for Swift 2]

cocoa date time class chart

Click the chart to see it at full size.

Update, August 26, 2015: I’ve updated this article so that its code works with Swift 2. It compiles under the latest version of Xcode 7, beta 6.

swift kick

In the previous installment in this series that looks at working with dates and times in Cocoa, we looked at:

  • The NSDate class, which is the heart of date and time in Cocoa, and represents a single point in time relative to the start of the third millennium (midnight, January 1, 2001)
  • the NSCalendar class, which provides a context for dates and the ability to do date arithmetic,
  • the NSDateComponents class, which represents the parts that make up either a date or a span of time, and
  • the NSDateFormatter class, which turns dates into string representations and vice versa.

We also covered creating dates, converting components into dates and vice versa, and converting dates into strings and vice versa. With this knowledge under our belts, let’s get to this article’s topic: doing date calculations.

Creating a couple of dates to work with

valentines st patricks

Start with a fresh playground, and enter or paste the following code so it looks like this:

// Playground - noun: a place where people can play

import UIKit

let userCalendar = NSCalendar.currentCalendar()

// Let's create an NSDate for Valentine's Day
// using NSDateComponents
let valentinesDayComponents = NSDateComponents()
valentinesDayComponents.year = 2015
valentinesDayComponents.month = 2
valentinesDayComponents.day = 14
let valentinesDay = userCalendar.dateFromComponents(valentinesDayComponents)!

// Let's create an NSDate for St. Patrick's Day
// using NSDateFormatter
let dateMakerFormatter = NSDateFormatter()
dateMakerFormatter.calendar = userCalendar
dateMakerFormatter.dateFormat = "yyyy/MM/dd"
let stPatricksDay = dateMakerFormatter.dateFromString("2015/03/17")!

In the code above, we’re creating two dates in two different ways (which we covered in the previous article):

  • We’re creating Valentine’s Day (February 14 for those of you in places where it’s not celebrated) by setting up date components and then using the user’s calendar to convert the components into a date.
  • We’re creating St. Patrick’s Day (March 17 for those of you in places where it’s not celebrated) by converting a string representing that date into a date by means of a date formatter. You may find that if you need to instantiate a large number of dates in code, you may want to do so this way, as you can do it in far fewer lines than by using date components.

Now that we have a couple of date objects, let’s do some date arithmetic!

Which came first?

which came first

NSDate has two methods, earlierDate and laterDate, which compare one date to another and return the appropriate date. Add the highlighted code below so that your playground looks like this:

// Playground - noun: a place where people can play

import UIKit

let userCalendar = NSCalendar.currentCalendar()

// Let's create an NSDate for Valentine's Day
// using NSDateComponents
let valentinesDayComponents = NSDateComponents()
valentinesDayComponents.year = 2015
valentinesDayComponents.month = 2
valentinesDayComponents.day = 14
let valentinesDay = userCalendar.dateFromComponents(valentinesDayComponents)!

// Let's create an NSDate for St. Patrick's Day
// using NSDateFormatter
let dateMakerFormatter = NSDateFormatter()
dateMakerFormatter.calendar = userCalendar
dateMakerFormatter.dateFormat = "yyyy/MM/dd"
let stPatricksDay = dateMakerFormatter.dateFromString("2015/03/17")!

// Which date comes first? Which comes last?
valentinesDay.earlierDate(stPatricksDay)
valentinesDay.laterDate(stPatricksDay)

This is pretty straightforward: valentinesDay.earlierDate(stPatricksDay) returns the valentinesDay instance, while valentinesDay.laterDate(stPatricksDay) returns stPatricksDay.

NSDate has a compare method that works in a way similar to a lot of other “compare” methods (such as C’s strcmp) that compare a value a and b, where:

  • If a < b, it returns a negative number
  • if a == b, it returns 0
  • if a > b, it returns a positive number

Cocoa comparison methods return values of type NSComparisonResult, so that when you’re comparing two values a and b:

  • If a < b, it returns NSOrderedAscending
  • if a == b, it returns NSOrderedSame
  • if a > b, it returns NSOrderedDescending

Let’s take it out for a spin. Add the highlighted code below so that your playground looks like this:

// Playground - noun: a place where people can play

import UIKit

let userCalendar = NSCalendar.currentCalendar()

// Let's create an NSDate for Valentine's Day
// using NSDateComponents
let valentinesDayComponents = NSDateComponents()
valentinesDayComponents.year = 2015
valentinesDayComponents.month = 2
valentinesDayComponents.day = 14
let valentinesDay = userCalendar.dateFromComponents(valentinesDayComponents)!

// Let's create an NSDate for St. Patrick's Day
// using NSDateFormatter
let dateMakerFormatter = NSDateFormatter()
dateMakerFormatter.calendar = userCalendar
dateMakerFormatter.dateFormat = "yyyy/MM/dd"
let stPatricksDay = dateMakerFormatter.dateFromString("2015/03/17")!

// Which date comes first? Which comes last?
valentinesDay.earlierDate(stPatricksDay)
valentinesDay.laterDate(stPatricksDay)

// Another way to compare dates
let dayOrder = valentinesDay.compare(stPatricksDay)
if dayOrder == .OrderedAscending {
  print("Valentine's Day comes before St. Patrick's Day.")
}
else if dayOrder == .OrderedDescending {
  print("Valentine's Day comes after St. Patrick's Day.")
}
else if dayOrder == .OrderedSame {
  print("They're the same day!")
}
else {
  print("Something weird happened.")
}

Valentine’s Day comes before St. Patrick’s Day, so the result you see the in sidebar should be Valentine’s Day comes before St. Patrick’s Day.

Date arithmetic: How far apart are two dates and times?

countdown clock

NSDate has the timeIntervalSinceDate method, which gives you the difference between 2 dates…in seconds.

// (Previous code goes here)

// Not all that useful
valentinesDay.timeIntervalSinceDate(stPatricksDay)
stPatricksDay.timeIntervalSinceDate(valentinesDay)

Since Valentine’s Day comes before St. Patrick’s Day, the first value is negative, while the second value is positive. Most users won’t find knowing that there are nearly 2.7 million seconds between the two days. How can we find out the number of days between Valentine’s and St. Patrick’s?

That’s where date components come in. I mentioned last time that date components can represent either:

  • A specific point in time, or
  • a duration of time.

We’re going to use date components for the second purpose in this example. We need to do the following:

  • Specify the units of time that we want from the calculation, which in this case is days, and
  • Provide those units of time and the two dates to NSCalendar‘s components method:
// (Previous code goes here)

// How many days between Valentine's Day and St. Patrick's Day?
let dayCalendarUnit: NSCalendarUnit = [.Day]
let stPatricksValentinesDayDifference = userCalendar.components(
  dayCalendarUnit,
  fromDate: valentinesDay,
  toDate: stPatricksDay,
  options: [])
// The result should be 31
stPatricksValentinesDayDifference.day

This version of NSCalendar‘s components method takes the following arguments:

  • unitFlags: An option set of components we want to retrieve. Since we want to know the number of days between Valentine’s and St. Patrick’s, there’s only one option in the set: .Day.
  • fromDate: The start date in the calculation.
  • toDate: The end date in the calculation.
  • options: In most cases, you’ll want this set to [], which means no options. This causes overflows in a unit to carry to the next higher unit. For example, if you specify in unitFlags that you want your result expressed in minutes and seconds, and the calculation’s result is 61 seconds, the result will be changed to 1 minute, 1 second.

Let’s try another calculation: what’s the time difference between 10:45 a.m. and 12:00 noon?

// (Previous code goes here)

// How many hours and minutes between 10:45 a.m. and 12:00 noon?
dateMakerFormatter.dateFormat = "yyyy/MM/dd hh:mm a Z"
let startTime = dateMakerFormatter.dateFromString("2015/01/27 10:45 AM -05:00")!
let endTime = dateMakerFormatter.dateFromString("2015/01/27 12:00 PM -05:00")!
let hourMinuteComponents: NSCalendarUnit = [.Hour, .Minute]
let timeDifference = userCalendar.components(
  hourMinuteComponents,
  fromDate: startTime,
  toDate: endTime,
  options: [])
timeDifference.hour
timeDifference.minute

You should see in the sidebar that timeDifference.hour‘s value is 1 and timeDifference.minute‘s value is 15.

Date addition and subtraction

plus minus dice

If you’re writing some kind of reminder app, you might want to be able to let the user say “give me a reminder in 10 days”, which means you’ll need to calculate what the date and time will be 10 days from now. Since we’re doing date addition with only one unit, we can perform this calculation by using NSCalendar‘s dateByAddingUnit method:

// (Previous code goes here)

// What will the date and time be be ten days from now?
let tenDaysFromNow = userCalendar.dateByAddingUnit(
  [.Day],
  value: 10,
  toDate: NSDate(),
  options: [])!

// What weekday (Sunday through Saturday) will it be ten days from now, and
// which weekday of the month will it be -- the 1st, 2nd, 3rd...?
let weekdayAndWeekdayOrdinal: NSCalendarUnit = [.Weekday, .WeekdayOrdinal]
let tenDaysFromNowComponents = userCalendar.components(
  weekdayAndWeekdayOrdinal,
  fromDate: tenDaysFromNow)
tenDaysFromNowComponents.weekday
tenDaysFromNowComponents.weekdayOrdinal

dateByAddingUnit expects the following parameters:

  • unit: The type of unit to be added to the date. We want to add days, so we’re setting this value to [.Day].
  • value: The number of units to be added to the date. We want to know what the date will be 10 days from now, so we set this to 10.
  • toDate: The date to which we’ll be adding units. We want to add 10 days to today, so we set this to a new NSDate (remember, instantiating an NSDate object without parameters creates an instance that refers to the current date and time).
  • options: In most cases, you’ll want to set this to [].

I ran the code on January 28, 2015 at 11:29 p.m., so the resulting date stored in tenDaysFromNow is displayed in my playground’s sidebar as Feb 7, 2015, 11:29 PM.

I wanted to know what day of the week it would be 10 days from now, and which weekday of the month (the first, second, third…?) so I used the version of NSCalendar‘s components method that takes a single date and use it to extract the weekday and weekdayOrdinal components from tenDaysFromNow. At the time I ran the code, tenDaysFromNowComponents.weekday‘s value was 7 (Saturday) and tenDaysFromNowComponents.weekdayOrdinal‘s value was 1 (meaning that it’s the first Saturday of the month).

Here’s another calculation: what was the date and time 3 days, 10 hours, 42 minutes, and 5 seconds ago? We’re doing date arithmetic with more than one kind of unit — days, hours, minutes, and seconds — so we need NSCalendar‘s dateByAddingComponents method instead:

// (Previous code goes here)

// What was the date and time 3 days, 10 hours, 42 minutes, and 5 seconds ago?
let periodComponents = NSDateComponents()
periodComponents.day = -3
periodComponents.hour = -10
periodComponents.minute = -42
periodComponents.second = -5
let then = userCalendar.dateByAddingComponents(
  periodComponents,
  toDate: NSDate(),
  options: [])!

dateByAddingComponents expects the following parameters:

  • comps: the date components that we want to add to the given date. Note that since we’re performing date subtraction, all the components are expressed as negative numbers.
  • toDate: The date from which we’ll be adding (or in this case, subtracting). We want to subtract from the current date and time, so we set this to a new NSDate object instantiated without parameters (which, as I’ve said before, gives an instance representing the current date and time).
  • options: In most cases, you’ll want to set this to [].

I ran the code on January 28, 2015 at 11:52 p.m., so the resulting date stored in then is displayed in my playground’s sidebar as Jan 25, 2015, 1:10 PM.

In closing

In this article, we covered:

  • Comparing two dates to see which one is the earlier on and which is the later one,
  • finding out how far apart two dates are, and
  • adding and subtracting from dates.

In the next installment, we’ll a closer look at date calculations and see some convenience methods that make date and time calculations simpler.

dates and times in swift - smallRelated articles

A very brief introduction to date formatting in Swift and iOS: The oversight in a mostly-good book on Swift programming led me down the path of writing articles about dates and times in Swift, starting with this one, where I look atNSDateFormatter.

How to work with dates and times in Swift, part one: An introduction of Cocoa’s date and time classes, and how they work together. This article covers UTC (Coordinated Universal Time), and the key classes: NSDate, NSCalendar, NSDateComponents.

How to work with dates and times in Swift, part three: Making date arithmetic more Swift-like: Cocoa’s date and time classes have an Objective-C heritage, which in the Swift context, feel kind of clunky. In this article, I look at ways — and by ways, I mean helper functions and class extensions — to make date calculations feel more like Swift.

How to work with dates and times in Swift, part four: A more Swift-like way to get the time interval between two dates: This quick article shows you how to make an operator overload that makes getting the time interval between two dates more like subtraction.