Simple date arithmetic with Swift

Rails got a few things right.

Jun 4, 2014 • by Axel Schlueter

Using date and time operations is a hard thing to do in most programming languages. On iOS and OS X it has always been especially laborious. Not only are there a number of different classes involved but the necessary methods also make it nearly impossible to write concise date and time calculations.

The not-so-good old way

Take for example this piece of code to calculate the date two days from now:

NSDateComponents *twoDays = [[NSDateComponents alloc] init];
twoDays.day = 2;

NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *inTwoDays = [cal dateByAddingComponents:twoDays
                                         toDate:[NSDate date]
                                        options:0];

NSLog(@"two days from now: %@", nextDate);

And now let’s have a look at how a Ruby on Rails developer would have written those lines:

puts 2.days.from_now

Wouldn’t it be nice to be able to write such concise expressions in your iOS app, too?

Swift to the rescue

With the new programming language called Swift that was introduced at WWDC 2014 Apple gives developers a number of new idioms to use. Two of those idioms are class extensions and computed properties:

extension Int {
  var days: NSDateComponents {
    let comps = NSDateComponents()
    comps.day = self;
    return comps
  }
}

This snippet extends the Int class with a new computed property called days. Now we can easily convert integers into instances of NSDateComponents:

println("3 days: \(3.days)")
// outputs "3 days: <NSDateComponents: 0x7fda2042c5b0> Day: 3"

Another new possibility introduced with Swift are operator functions. Operator functions allow us to override regular operators like + and - to work with custom classes. Let’s add an operator function that adds two instances of NSDateComponents:

@infix func +(left: NSDateComponents,
              right: NSDateComponents) -> NSDateComponents
{
  let comps = NSDateCompoents()
  comps.seconds = left.seconds + right.seconds
  // ...
  comps.years = left.years + right.years
  return comps;
}

Now we can combine various date intervals into a single result:

println("2 days, 5 hours: \(2.days + 5.hours)")
// outputs "2 days, 5 hours: <NSDateComponents: 0x743f8c4> Day: 2, Hour: 5"

Finally Swift gives us the power to not only enhance other Swift objects but also plain old Objective C classes. We can augment NSDateComponents with a function that lets us convert our time intervals back into a real date:

extension NSDateComponents {
  var fromNow: NSDate {
    let cal = NSCalendar.currentCalendar()
    return cal.dateByAddingComponents(self, toDate: NSDate.date(), options: nil)
  }
}

The result

If we combine those three techniques we can now write date arithmetics the same way as shown in the Rails example at the beginning of this post:

println("now:                   \(NSDate.date())")
println("5 days, 3 month later: \((5.days + 3.months).fromNow)")
// outputs:
// now:                   2014-06-04 21:45:20 +0000
// 5 days, 3 month later: 2014-09-09 21:45:20 +0000

You can find the complete example XCode project at GitHub. Make sure you have the Xcode 6 beta version installed (in Mavericks 10.9.3 or the Yosemite 10.10 beta) to try it yourself.