How to work with dates and times in Swift 3, part 4: Adding Swift syntactic magic

by Joey deVilla on August 30, 2016

In this article, we’ll expand on material covered in the three previous articles in this series:

A more readable way to work with Dates and DateComponents

Suppose we want to find out what the date and time will be 2 months, 3 days, 4 hours, 5 minutes, and 6 seconds from now will be. If you recall what we covered in the last installment in this series, you’d probably use code like this:

In the code above, we did the following:

  • We created an instance of a DateComponents struct.
  • We set its properties so that it would represent a time interval of 2 months, 3 days, 4 hours, 5 minutes, and 6 seconds.
  • We then used Calendar‘s date(byAdding:to:) method to add the time interval to a Date.

This code wouldn’t look out of place in a lot of other programming languages, but we can do better in Swift. What if I told you that by defining a few helper functions, you can turn the code above into the code below?

Or this code?

I’d much rather write the code above. This article will cover the code necessary to make this kind of syntactic magic possible.

Overloading + and - so that we can add and subtract DateComponents

First, let’s write some code that allows us to add and subtract DateComponents. Start a new playground and enter the following code into it:

In the code above, we’ve overloaded the + and – operators so that we can add and subtract DateComponents. 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.

The addition and subtraction operations are so similar and so tedious, which is a sign that there’s an opportunity to DRY up the code. I factored out the duplicate code from both the + and - overloads and put it into its own method, combineComponents, which does the actual DateComponents addition and subtraction.

You may have noticed a lot of ?? operators in the code for combineComponents. ?? is referred to as the nil coalescing operator, and it’s a clever bit of syntactic shorthand. For the expression below:

let finalValue = someOptionalValue ?? fallbackValue

  • If someOptionalValue is not nil, finalValue is set to someOptionalValue‘s value.
  • If someOptionalValue is nil, finalValue is set to fallbackValue‘s value.

Let’s confirm that our code works. Try out the following code:

Overloading - so that we can negate DateComponents

Now that we can add and subtract DateComponents, let’s overload the unary minus so that we can negate DateComponents:

With this overload defined, we can now use the unary minus to negate DateComponents:

Overloading + and - so that we can add Dates and DateComponents and subtract DateComponents from Dates

With the unary minus defined, we can now define the following operations:

  • Date + DateComponents
  • DateComponents + Date
  • DateDateComponents

Note that we didn’t define an overload for subtracting Date from DateComponents — such an operation doesn’t make any sense.

With these overloads defined, a lot of Date/DateComponents arithmetic in Swift becomes much easier to enter and read:

Extending Date so that creating dates is simpler

Creating Dates in Swift is a roundabout process. Usually, you end up creating them in one of two ways:

  • Instantiating a DateComponents struct and then using it to create a Date using Calendar‘s date(from:) method, or
  • Creating a String representation of the Date and then using it to create a Date using DateFormatter‘s date(from:) method.

Let’s simplify things by extending the Date struct with a couple of convenient init method overloads:

With these methods, initializing Dates is a lot more simple:

Overloading - so that we can use it to find the difference between two Dates

When we’re trying to determine the time between two given Dates, what we’re doing is finding the difference between them. Wouldn’t it be nice if we could use the - operator to find the difference between Dates, just as we can use it to find the difference between numbers?

Let’s code an overload to do just that:

Let’s test it in action:

Extending Int to add some syntactic magic to date components

We’ve already got some syntactic niceties, but the real Swift magic happens when we add this code to the mix:

This additions to Int allow us to convert Ints to DateComponents in an easy-to-read way, and with our overloads to add and subtract DateComponents to and from each other, and to add Dates to DateComponents, we can now perform all sorts of syntactic magic like this:

Extending DateComponents to add even more syntactic magic: fromNow and ago

And finally, a couple of additions to the DateComponents struct to make Date/DateComponent calculations even more concise and readable:

Here are these additions in action:

Wrapping it all up

Here’s the playground containing all the code we just worked with:

 

{ 5 comments… read them below or add one }

1 Chris January 12, 2017 at 10:28 am

That is so AMAZING! In other languages I use, it is much easier to deal with dates. But with your “magic”, it is pretty easy in Swift, too. :-)

2 Daniel February 7, 2017 at 10:10 am

Great tutorial. Thanks!

3 marius March 8, 2017 at 6:44 am

Nice work. Thx!

4 Patrick March 12, 2017 at 8:42 am

Thank you … very nice Tutorial! I’m deeply impressed :)

5 Kostis Magaliow July 4, 2017 at 2:26 am

Hello there. I ve been reading you “How to work with dates and times in swift” article and I have a question.
I am able to compare for example the current date and my birth date and get an answer like this
“You are 12822 days old” or
“You are 35 years old”

What should i do if i want an answer like

“You are 35 years 1 month 25 days and 15 minutes old”?

Thanks!

Leave a Comment

{ 1 trackback }

Previous post:

Next post: