Date arithmetic, which make it possible to add time intervals to Dates, find the difference in time between two Dates, and compare Dates.

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:

Swift

1

2

3

4

5

6

7

vartimeInterval=DateComponents()

timeInterval.month=2

timeInterval.day=3

timeInterval.hour=4

timeInterval.minute=5

timeInterval.second=6

letfutureDate=Calendar.current.date(byAdding:timeInterval,to:Date())!

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?

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:

Swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

func+(_lhs:DateComponents,_rhs:DateComponents)->DateComponents{

returncombineComponents(lhs,rhs)

}

func-(_lhs:DateComponents,_rhs:DateComponents)->DateComponents{

returncombineComponents(lhs,rhs,multiplier:-1)

}

funccombineComponents(_lhs:DateComponents,

_rhs:DateComponents,

multiplier:Int=1)

->DateComponents{

varresult=DateComponents()

result.second=(lhs.second??0)+(rhs.second??0)*multiplier

result.minute=(lhs.minute??0)+(rhs.minute??0)*multiplier

result.hour=(lhs.hour??0)+(rhs.hour??0)*multiplier

result.day=(lhs.day??0)+(rhs.day??0)*multiplier

result.weekOfYear=(lhs.weekOfYear??0)+(rhs.weekOfYear??0)*multiplier

result.month=(lhs.month??0)+(rhs.month??0)*multiplier

result.year=(lhs.year??0)+(rhs.year??0)*multiplier

returnresult

}

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.

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?

// At the time of writing, this value was a Date with the following properties:

// - year: 47

// - month: 1

// - day: 9

// - hour: 22

// - minute: 14

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:

Swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

// (Previous code goes here)

extensionInt{

varsecond: DateComponents{

varcomponents=DateComponents()

components.second=self;

returncomponents

}

varseconds: DateComponents{

returnself.second

}

varminute: DateComponents{

varcomponents=DateComponents()

components.minute=self;

returncomponents

}

varminutes: DateComponents{

returnself.minute

}

varhour: DateComponents{

varcomponents=DateComponents()

components.hour=self;

returncomponents

}

varhours: DateComponents{

returnself.hour

}

varday: DateComponents{

varcomponents=DateComponents()

components.day=self;

returncomponents

}

vardays: DateComponents{

returnself.day

}

varweek: DateComponents{

varcomponents=DateComponents()

components.weekOfYear=self;

returncomponents

}

varweeks: DateComponents{

returnself.week

}

varmonth: DateComponents{

varcomponents=DateComponents()

components.month=self;

returncomponents

}

varmonths: DateComponents{

returnself.month

}

varyear: DateComponents{

varcomponents=DateComponents()

components.year=self;

returncomponents

}

varyears: DateComponents{

returnself.year

}

}

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:

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”