Calculating the difference between two dates in Swift

47,771

Solution 1

I ended up creating a custom operator for Date:

extension Date {

    static func - (lhs: Date, rhs: Date) -> TimeInterval {
        return lhs.timeIntervalSinceReferenceDate - rhs.timeIntervalSinceReferenceDate
    }

}

With this operator I can now compute the difference between two dates on a more abstract level without caring about timeIntervalSinceReferenceDate or what exactly the reference date is – and without losing precision, for example:

let delta = toDate - fromDate

Obviously, I didn't change much, but for me it's a lot more readable and consequent: Swift has the + operator already implemented for a Date and a TimeInterval:

/// Returns a `Date` with a specified amount of time added to it.
public static func + (lhs: Date, rhs: TimeInterval) -> Date

So it's already supporting

Date + TimeInterval = Date

Consequently, it should also support

Date - Date = TimeInterval

in my opinion and that's what I added with the simple implementation of the - operator. Now I can simply write the example function exactly as mentioned in my question:

func computeNewDate(from fromDate: Date, to toDate: Date) -> Date    
     let delta = toDate - fromDate // `Date` - `Date` = `TimeInterval`
     let today = Date()
     if delta < 0 {
         return today
     } else {
         return today + delta // `Date` + `TimeInterval` = `Date`
     }
}

It might very well be that this has some downsides that I'm not aware of at this moment and I'd love to hear your thoughts on this.

Solution 2

You can extension with custom operator, and return tuples

extension Date {

    static func -(recent: Date, previous: Date) -> (month: Int?, day: Int?, hour: Int?, minute: Int?, second: Int?) {
        let day = Calendar.current.dateComponents([.day], from: previous, to: recent).day
        let month = Calendar.current.dateComponents([.month], from: previous, to: recent).month
        let hour = Calendar.current.dateComponents([.hour], from: previous, to: recent).hour
        let minute = Calendar.current.dateComponents([.minute], from: previous, to: recent).minute
        let second = Calendar.current.dateComponents([.second], from: previous, to: recent).second

        return (month: month, day: day, hour: hour, minute: minute, second: second)
    }

}

Using:

let interval = Date() - updatedDate
print(interval.day)
print(interval.month)
print(interval.hour)

Solution 3

Simply toDate.timeIntervalSince(fromDate).

To reimplement your function without adding any extension:

func computeNewDate(from fromDate: Date, to toDate: Date) -> Date  {  
     let delta = toDate.timeIntervalSince(fromDate) 
     let today = Date()
     if delta < 0 {
         return today
     } else {
         return today.addingTimeInterval(delta) 
     }
}

Solution 4

I found a builtin solution to calculate the difference between 2 dates.

let delta = toDate.timeIntervalSince(fromDate)

Solution 5

You can use :

let delta = fromDate.distance(to: toDate)
Share:
47,771
Mischa
Author by

Mischa

Watch my talks on ▶️ The Art of Designing Code ▶️ Auto Layout

Updated on February 14, 2021

Comments

  • Mischa
    Mischa over 3 years

    I've seen many approaches how to compute the difference between two dates in terms of a particular date component, e.g. in days, hours, months etc. (see this answer on Stackoverflow):

    Calendar.current.dateComponents([.hour], from: fromDate, to: toDate).hour
    Calendar.current.dateComponents([.day], from: fromDate, to: toDate).day
    Calendar.current.dateComponents([.month], from: fromDate, to: toDate).month
    

    What I haven't seen is how to make calculations with the actual Date objects. Something like

    func computeNewDate(from fromDate: Date, to toDate: Date) -> Date    
         let delta = toDate - fromDate
         let today = Date()
         if delta < 0 {
             return today
         } else {
             return today + delta
         }
    }
    

    I have seen the DateInterval type which as introduced in iOS 10 but according to the documentation

    [it] does not support reverse intervals i.e. intervals where the duration is less than 0 and the end date occurs earlier in time than the start date.

    That makes it inherently difficult to calculate with dates – especially when you don't know which one is the earlier date.

    Is there any clean and neat approach to compute time differences between Dates directly (and adding them to Date instances again) without computing with their timeIntervalSinceReferenceDate?