---
title: "Date and Time Calculations in Swift, Part 2"
description: "Improve your Swift date and time calculations with syntactic magic."
authors:
  - name: "Joey deVilla"
    url: "https://auth0.com/blog/authors/joey-devilla/"
date: "Feb 20, 2024"
category: "Developers,Tutorial,iOS"
tags: ["mobile", "swift", "date", "time"]
url: "https://auth0.com/blog/date-time-calculations-swift-2/"
---

# Date and Time Calculations in Swift, Part 2

In this part of the tutorial, we’ll expand on what you learned in [the previous part](../date-time-calculations-swift-1) by taking Swift’s date and time calculation methods and adding syntactic magic to them.

> 👀 Look for the 🛠 emoji if you’d like to skim through the content while focusing on the build and execution steps.
> 
> 💻 You can find an Xcode playground with all the code in this article in [this GitHub repository](https://github.com/auth0-blog/swift-date-time-calculations) — it’s `date-time-calculations-swift-2.playground`.


## Let’s Make Date Calculations in Swift More Elegant

Suppose you 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.

First, you would create a `DateComponents` instance representing an interval of 2 months, 3 days, 4 hours, 5 minutes, and 6 seconds.

🛠️ Open Xcode, start a new blank macOS playground (I find them more reliable and less crash-prone than iOS playgrounds) and add the following code to it:

```swift
import Cocoa


// Locale, calendar, and date formatter
var userLocale = Locale.autoupdatingCurrent
var gregorianCalendar = Calendar(identifier: .gregorian)
gregorianCalendar.locale = userLocale
let dateFormatter = DateFormatter()

// Time interval of 2 months, 3 days, 4 hours, 
// 5 minutes, and 6 seconds
var timeInterval = DateComponents(
  month: 2,
  day: 3,
  hour: 4,
  minute: 5,
  second: 6
)
```

You would then add that time interval to the current date using `Calendar`’s `date(byAdding:to:)` method.

🛠️ Add this code to the playground and run it:

```swift
let futureDate = gregorianCalendar.date(
    byAdding: timeInterval,
    to: Date()
)!
print("\n2 months, 3 days, 4 hours, 5 minutes, and 6 seconds from now is \(futureDate.description).")
```

The code works, but we can do better in Swift. Wouldn’t it be cool if you could turn the code above into the code below...

```swift
let coolFutureDate = Date() + 2.months + 3.days + 4.hours + 5.minutes + 6.seconds
```

...or perhaps this code?

```swift
let coolerFutureDate = (2.months + 3.days + 4.hours + 5.minutes + 6.seconds).fromNow
```

I’d much rather write the cool code in the examples above. Swift won’t let you do this “out of the box,” but with the code in this article, you can use this syntactic magic.


## Extending Date for Simpler Date Creation and Debugging

There are two commonly-used approaches for creating a `Date` instance for a specific date and time:

1. Creating the `DateComponents` instance with the components that make up the date (the year, month, day, hour, and so on) and a `Calendar` instance to give context to convert the `DateComponents` into a `Date`.
2. Using a `DateFormatter` — or my preferred version, an `ISO8601DateFormatter` — to convert a date in string form into a `Date`.

Swift’s (or more accurately, [Foundation’s](https://developer.apple.com/documentation/foundation)) date and time objects make it possible to work with a wide array of calendar systems and date and time formats, but most of the time, you’ll just want to create dates and times for the Gregorian calendar. I’ve always wished that there was a way to instantiate a `Date` in a single line of code.

Another issue with the `Date` class is the `description` property, which returns a terse representation of the `Date` value expressed in terms of the UTC time zone. I’ve always wanted a property that fully represents the `Date` in my preferred time zone.

The extension below takes items from my wishlist and makes them real.

🛠️ Add this extension to the playground:
	
```
extension Date {
    
    // 1
    init(
        year: Int,
        month: Int,
        day: Int,
        hour: Int? = nil,
        minute: Int? = nil,
        second: Int? = nil,
        timeZoneIdentifier: String = "UTC"
    ) {
        let components = DateComponents(
            timeZone: TimeZone(identifier: timeZoneIdentifier),
            year: year,
            month: month,
            day: day,
            hour: hour,
            minute: minute,
            second: second
        )
        self = Calendar(identifier: .gregorian).date(from: components)!
    }
    
    // 2
    init(
        year: Int,
        month: Int,
        day: Int,
        hour: Int? = nil,
        minute: Int? = nil,
        second: Int? = nil,
        timeZoneAbbreviation: String = "UTC"
    ) {
        let components = DateComponents(
            timeZone: TimeZone(abbreviation: timeZoneAbbreviation),
            year: year,
            month: month,
            day: day,
            hour: hour,
            minute: minute,
            second: second
        )
        self = Calendar(identifier: .gregorian).date(from: components)!
    }
    
    // 3
    init(iso8601Date: String) {
        self = ISO8601DateFormatter().date(from: iso8601Date)!
    }
    
    // 4
    var desc: String {
      get {
        let PREFERRED_LOCALE = "en_US" // Use whatever locale you prefer!
        return self.description(with: Locale(identifier: PREFERRED_LOCALE))
      }
    }
}
```

Here’s a quick explanation of the new additions to `Date`. Each item corresponds to the numbered comments in the code above:

1. This initializer creates a `Date` instance given a year, month, day, hour, minute, second, and time zone identifier. I prefer using time zone identifiers, which specify time zones in “Continent/City” format, because it frees me from figuring out if it’s under Standard Time or Daylight Saving Time at that moment. The `TimeZone.knownTimeZoneIdentifiers` property contains an array of all the recognized time zone identifier strings.
2. This initializer is like the first one, except it takes a three-letter time zone abbreviation instead of a time zone identifier.
3. This initializer takes a date string in ISO8601 format.
4. The `desc` property displays the `Date` as a full date and time in the preferred format. I’ve set it to the format I tend to use — `en_US` — but you can set it to the format that works for you.

Let’s take these new initializers and property for a test.

🛠️ Add this code to the playground and run it:

```swift
// Creating dates with these new initializers
print("Dates:")

// The moment that Steve Jobs told MacWorld 2007 attendees that their
// new “iPod, phone, and internet communicator” was one thing,
// and it was called the iPhone: January 9, 2007, 10:03 a.m. PST.
let iPhoneIntroDate = Date(
    year: 2007,
    month: 01,
    day: 09,
    hour: 10,
    minute: 3,
    timeZoneIdentifier: "America/Los_Angeles"
)
print("• iPhoneIntroDate: \(iPhoneIntroDate.desc)")

// The moment when the Swift programming language was announced
// at WWDC 2014: June 2, 2014, 11:45 PDT.
let swiftIntroDate = Date(
    year: 2014,
    month: 06,
    day: 02,
    hour: 11,
    minute: 45,
    timeZoneAbbreviation: "PDT"
)
print("• swiftIntroDate: \(swiftIntroDate.desc)")

// The moment when Apple Silicon was announced at a special
// WWDC online event: June 22, 2020, 11:27 a.m. PDT.
let appleSiliconIntroDate = Date(iso8601Date: "2020-06-22T11:27:00-07:00")
print("• appleSiliconIntroDate: \(appleSiliconIntroDate.desc)")
```

Here‘s the output from my computer:

```
Dates:
• iPhoneIntroDate: Tuesday, January 9, 2007 at 1:03:00 PM Eastern Standard Time
• swiftIntroDate: Monday, June 2, 2014 at 2:45:00 PM Eastern Daylight Time
• appleSiliconIntroDate: Monday, June 22, 2020 at 2:27:00 PM Eastern Daylight Time
```


## Overloading “+” And “-” For Date and Time Calculations

While `Calendar`’s `date(byAdding:to:)` method works just fine, it might be more elegant if date and time calculations could be performed with `+` and `-` operators. Let’s give them date and time functionality!

### Adding and Subtracting DateComponents

The first step in adding `+` and `-` to date and time calculations is overloading them so that you can add and subtract `DateComponents` instances.

🛠️ Add the following code to the playground:

```swift
// The overloads of “+” and “-” rely on this function
func combineComponents(
  _ lhs: DateComponents,
  _ rhs: DateComponents,
  multiplier: Int = 1
) -> DateComponents {
  var result = DateComponents()
  result.nanosecond = (lhs.nanosecond ?? 0) + (rhs.nanosecond ?? 0) * multiplier
  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
  return result
}

// Overload “+” so that you can use it to calculate
// DateComponents + DateComponents
func +(_ lhs: DateComponents, _ rhs: DateComponents) -> DateComponents {
  return combineComponents(lhs, rhs)
}

// Overload “-” so that you can use it to calculate
// DateComponents - DateComponents
func -(_ lhs: DateComponents, _ rhs: DateComponents) -> DateComponents {
  return combineComponents(lhs, rhs, multiplier: -1)
}
}
```

In the code above, you’ve overloaded the `+` and `-` operators so that `DateComponents` can be added to and subtracted from each other. I derived these functions from [Axel Schlueter’s SwiftDateTimeExtensions library](https://github.com/schluete/SwiftDateTimeExtensions/blob/master/SwiftDateTimeExtensions/timeDateExtensions.swift). He wrote them when Swift was still in beta; I updated them to compile with the current version and added a couple of tweaks.

The addition and subtraction operations are so similar and so tedious, which is a sign that there’s an opportunity to [DRY](https://docs.getdbt.com/terms/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 addition and subtraction of `DateComponents`.

You may have noticed many `??` operators in the code for `combineComponents()`. `??` is referred to as the [_nil coalescing operator_](https://sarunw.com/posts/what-does-nil-coalescing-operator-means-in-swift/), and it’s a clever bit of syntactic shorthand. 

Consider this expression:

```swift
let finalValue = someOptionalValue ?? fallbackValue
```

In the code above,

* 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 the new operator overloads work. 

🛠️ Add the following to the playground and run it:

```swift
// Testing the “+” and “-” overloads
let threeDaysTenHoursThirtyMinutes = DateComponents(
  day: 3,
  hour: 10,
  minute: 30
)
let oneDayFiveHoursTenMinutes = DateComponents(
  day: 1,
  hour: 5,
  minute: 10
)

print("\nOverloaded + and - for DateComponents:")

// 3 days, 10 hours, and 30 minutes + 1 day, 5 hours, and 10 minutes
let additionResult = threeDaysTenHoursThirtyMinutes + oneDayFiveHoursTenMinutes
print("• 3 days, 10 hours, and 30 minutes + 1 day, 5 hours, and 10 minutes = \(additionResult.day!) days, \(additionResult.hour!) hours, and \(additionResult.minute!) minutes.")

// 3 days, 10 hours, and 30 minutes - 1 day, 5 hours, and 10 minutes
let subtractionResult = threeDaysTenHoursThirtyMinutes - oneDayFiveHoursTenMinutes
print("• 3 days, 10 hours, and 30 minutes - 1 day, 5 hours, and 10 minutes = \(subtractionResult.day!) days, \(subtractionResult.hour!) hours, and \(subtractionResult.minute!) minutes.")
```

You should see the following output:

```
Overloaded + and - for DateComponents:
• 3 days, 10 hours, and 30 minutes + 1 day, 5 hours, and 10 minutes = 4 days, 15 hours, and 40 minutes.
• 3 days, 10 hours, and 30 minutes - 1 day, 5 hours, and 10 minutes = 2 days, 5 hours, and 20 minutes.
```

### Negating DateComponents

Now that you can add and subtract `DateComponents`, let’s overload `-` — the _unary minus_.

🛠️ Add this code to the playground:

```swift
// Overload unary “-” so that you negate DateComponents
prefix func -(components: DateComponents) -> DateComponents {
  var result = DateComponents()
  if components.nanosecond != nil { result.nanosecond = -components.nanosecond! }
  if components.second   != nil { result.second   = -components.second! }
  if components.minute   != nil { result.minute   = -components.minute! }
  if components.hour   != nil { result.hour   = -components.hour! }
  if components.day  != nil { result.day  = -components.day! }
  if components.weekOfYear != nil { result.weekOfYear = -components.weekOfYear! }
  if components.month  != nil { result.month  = -components.month! }
  if components.year   != nil { result.year   = -components.year! }
  return result
}
```

This code negates every non-`nil` numeric component inside the `DateComponent`. With this overload defined, we can now use the unary minus to negate `DateComponents`. 

🛠️ Add this code to the playground and run it:

```swift
// Testing the unary “-” overload
// - (1 day, 5 hours, and 10 minutes)
print("\nOverloaded unary - for DateComponents:")
let negativeTime = -oneDayFiveHoursTenMinutes
print("• Negating 1 day, 5 hours, and 10 minutes turns it into \(negativeTime.day!) days, \(negativeTime.hour!) hours, and \(negativeTime.minute!) minutes.")
```

You’ll see this output:

```
Overloaded unary - for DateComponents:
• Negating 1 day, 5 hours, and 10 minutes turns it into -1 days, -5 hours, and -10 minutes.
```


### Adding and Subtracting Dates and DateComponents

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

* `Date + DateComponents`, which makes it easier to answer questions like “What time will it be 1 day, 5 hours, and 10 minutes from now?”
* `DateComponents + Date`, which should be possible because addition is commutative (which is just a fancy way of saying that _a + b_ and _b + a_ should give you the same result).
* `Date - DateComponents`, which makes it easier to answer questions like “What time was it 3 days, 10 hours, and 30 minutes ago?”

🛠️ Add this code to the playground:

```swift
// Overload “+” so that you can use it to calculate
// Date + DateComponents
func +(_ lhs: Date, _ rhs: DateComponents) -> Date
{
  return Calendar.current.date(byAdding: rhs, to: lhs)!
}

// Overload “+” so that you can use it to calculate
// DateComponents + Dates
func +(_ lhs: DateComponents, _ rhs: Date) -> Date
{
  return rhs + lhs
}

// Overload “-” so that you can use it to calculate
// Date - DateComponents
func -(_ lhs: Date, _ rhs: DateComponents) -> Date
{
  return lhs + (-rhs)
}
```

Note that we didn’t define an overload for calculating `DateComponents ` - `Date` — 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. 

🛠️ Add the following to the playground and run it:

```swift
// What time will it be 1 day, 5 hours, and 10 minutes from now?
print("\nOverloaded + and - for Date/DateComponent calculations:")

// Here's the standard way of finding out:
let futureDate0 = Calendar.current.date(
  byAdding: oneDayFiveHoursTenMinutes,
  to: Date()
)

// With our overloads and function definitions, we can now do it this way:
print("• Date() + oneDayFiveHoursTenMinutes = \((Date() + oneDayFiveHoursTenMinutes).desc).")

// This will work as well:
print("• oneDayFiveHoursTenMinutes + Date() = \((oneDayFiveHoursTenMinutes + Date()).desc).")

// Let’s see subtraction in action:
print("• Date() - threeDaysTenHoursThirtyMinutes = \((Date() - threeDaysTenHoursThirtyMinutes).desc).")
```

### Calculating the Difference between Two Dates

When we’re trying to determine the time between two given `Date`s, 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 `Date`s, just as we can use it to find the difference between numbers?

Let’s code an overload to do just that. 

🛠️ Add this code to the playground and run it:

```swift
func -(_ lhs: Date, _ rhs: Date) -> DateComponents
{
  return Calendar.current.dateComponents(
    [.year, .month, .weekOfYear, .day, .hour, .minute, .second, .nanosecond],
    from: rhs,
    to: lhs)
}
```

The code above overloads `-` when both operands are `Date`s so that it uses Calendar’s `dateComponents(_:from:to:)` method, which calculates the date components for an interval between two given dates.

🛠️ Add the following to the playground and run it:

```swift
print("\nOverloaded - for Date subtraction:")

// What’s the time difference between:
// - The date Apple Silicon was introduced (June 22, 2020, 11:27 a.m. UTC-7) and
// - The date the iPhone was introduced (January 9, 2007, 10:03 a.m. UTC-8)?
let introInterval = appleSiliconIntroDate - iPhoneIntroDate
print("• The interval between the introduction of the iPhone and the introduction of Apple Silicon is \(introInterval.year!) years, \(introInterval.month!) months, \(introInterval.day!) days, \(introInterval.hour!) hours, and \(introInterval.minute!) minutes.")
```

You can now perform date calculations using `+` and `-`!


## Adding Syntactic Magic to DateComponents

You’ve already got some syntactic niceties for date calculations, but you’re not done yet!


### Extending Int to Make DateComponent Creation More Readable

🛠️ Add the code below to the playground:

```swift
extension Int {

  var second: DateComponents {
    var components = DateComponents()
    components.second = self;
    return components
  }
  
  var seconds: DateComponents {
    return self.second
  }
  
  var minute: DateComponents {
    var components = DateComponents()
    components.minute = self;
    return components
  }
  
  var minutes: DateComponents {
    return self.minute
  }
  
  var hour: DateComponents {
    var components = DateComponents()
    components.hour = self;
    return components
  }
  
  var hours: DateComponents {
    return self.hour
  }
  
  var day: DateComponents {
    var components = DateComponents()
    components.day = self;
    return components
  }
  
  var days: DateComponents {
    return self.day
  }
  
  var week: DateComponents {
    var components = DateComponents()
    components.weekOfYear = self;
    return components
  }
  
  var weeks: DateComponents {
    return self.week
  }
  
  var month: DateComponents {
    var components = DateComponents()
    components.month = self;
    return components
  }
  
  var months: DateComponents {
    return self.month
  }
  
  var year: DateComponents {
    var components = DateComponents()
    components.year = self;
    return components
  }
  
  var years: DateComponents {
    return self.year
  }
  
}
```

These additions to `Int` allow you to convert `Int`s to `DateComponents` in an easy-to-read way.  Each property in this extension has a singular and plural version, which allows you to write grammatically correct expressions such as `1.second` and `2.seconds`.

With the overloads to add and subtract `DateComponents` to and from each other, and the overloads to add `Date`s to `DateComponents`, you can now perform all sorts of syntactic magic.

🛠️ Add the following to the playground and run it:

```swift
// A quick test of some future dates
print("One hour from now is: \((Date() + 1.hour).desc)")
print("One day from now is: \((Date() + 1.day).desc)")
print("One week from now is: \((Date() + 1.week).desc)")
print("One month from now is: \((Date() + 1.month).desc)")
print("One year from now is: \((Date() + 1.year).desc)")

// What was the date 10 years, 9 months, 8 days, 7 hours, and 6 minutes ago?
let aLittleWhileBack = Date() - 10.years - 9.months - 8.days - 7.hours - 6.minutes
print("10 years, 9 months, 8 days, 7 hours, and 6 minutes ago, it was: \(aLittleWhileBack.desc)")
```

### Adding Even More Syntactic Magic with “fromNow” And “ago”

And finally, a couple of additions to `DateComponents` to make `Date`/`DateComponent` calculations even more concise and readable. 

🛠️ Add this code to the playground:

```swift
extension DateComponents {
  
  var fromNow: Date {
    return Calendar.current.date(
        byAdding: self,
        to: Date()
    )!
  }
  
  var ago: Date {
    return Calendar.current.date(
        byAdding: -self,
        to: Date()
    )!
  }
  
}
```

🛠️ To see the syntactic magic in action, add the following to the playground and run it:

```swift
// Test “fromNow” and “ago”
print("\nSome serious syntactic magic:")
print("• 2.weeks.fromNow: \(2.weeks.fromNow.desc)")
print("• 3.months.fromNow: \(3.months.fromNow.desc)")

// What date/time will it be 2 months, 3 days, 4 hours,
// 5 minutes, and 6 seconds from now?
let futureDate3 = (2.months + 3.days + 4.hours + 5.minutes + 6.seconds).fromNow
print("• (2.months + 3.days + 4.hours + 5.minutes + 6.seconds).fromNow: \(futureDate3.desc).")

// What date/time was it 2 months, 3 days, 4 hours,
// 5 minutes, and 6 seconds ago?
let pastDate2 = (2.months + 3.days + 4.hours + 5.minutes + 6.seconds).ago
print("• (2.months + 3.days + 4.hours + 5.minutes + 6.seconds).ago: \(pastDate2.desc)")
```


## Summary

> 💻 Once again, you can find an Xcode playground with all the code in this article in [this GitHub repository](https://github.com/auth0-blog/swift-date-time-calculations) — it’s `date-time-calculations-swift-2.playground`.

In this tutorial, you covered a lot of ground:

* Determining if a given time zone is under Standard Time or Daylight Saving Time on a given date 
* `Date` comparisons, including comparisons at more “human” levels of granularity
* Sorting `Date`s
* Find the difference between two `Date`s in seconds, minutes, hours, days, months, years, and any combination of these
* Adding a time interval to a `Date` to create a new `Date`
* Calculating the next occurrence of a specific date or time
* Extending `Date` with new initializers and a new property to make date instantiation easier
* Overloading `+` and `-` to work with `Date`s and `DateComponents`
* Adding syntactic magic to `DateComponents`

Use the material in this tutorial to expand your understanding of working with dates and times in Swift, and to write your own date and time utilities. I recommend that you consult these other pages to expand your knowledge:

* [_Dates and Times_](https://developer.apple.com/documentation/foundation/dates_and_times) from Apple’s documentation
* [_Swift Date_](https://iharishsuthar.github.io/posts/swift-date/) by Harish Suthar
* [_Time Steering in Swift 5.5_](https://medium.com/geekculture/time-steering-in-swift-5-5-ae39fd9ccd09) by Sai Durga Mahesh
* [_Why is Programming with Dates So Hard?_](https://levelup.gitconnected.com/why-is-programming-with-dates-so-hard-7477b4aeff4c) by Dave Taubler
* [_Falsehoods programmers believe about time_](https://gist.github.com/timvisee/fcda9bbdff88d45cc9061606b4b923ca)

Here’s some bonus reading: [_The Hummingbird Effect: How Galileo Invented Timekeeping and Forever Changed Modern Life_](https://www.themarginalian.org/2014/10/20/how-we-got-to-know-steven-johnson-hummingbird-effect-time/). It’s not _directly_ connected to Swift’s date- and time-related objects, but Galileo’s observations of a swinging altar lamp in the cathedral in Pisa led to a revolution in timepieces and timekeeping that lives on in today’s iOS (and watchOS!) devices.