web-dev-qa-db-fra.com

Calcul de la différence entre deux dates dans Swift

J'ai vu de nombreuses approches comment calculer la différence entre deux dates en termes d'un composant de date particulier, par exemple en jours, heures, mois etc. (voir cette réponse sur 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

Ce que je n'ai pas vu, c'est comment faire des calculs avec les objets Date réels. Quelque chose comme

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
     }
}

J'ai vu le type DateInterval tel qu'introduit dans iOS 10 mais selon la documentation

[il] ne prend pas en charge les intervalles inverses, c'est-à-dire les intervalles où la durée est inférieure à 0 et la date de fin se produit plus tôt dans le temps que la date de début.

Cela rend intrinsèquement difficile le calcul avec les dates - en particulier lorsque vous ne savez pas laquelle est la date antérieure.

Existe-t-il une approche claire et nette pour calculer directement les différences de temps entre Dates (et les ajouter à nouveau aux instances Date ) sans calculer avec leur timeIntervalSinceReferenceDate?

10
Mischa

J'ai fini par créer un opérateur personnalisé pour Date:

extension Date {

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

}

Avec cet opérateur, je peux maintenant calculer la différence entre deux dates à un niveau plus abstrait sans me soucier de timeIntervalSinceReferenceDate ou de la date de référence exacte - et sans perdre en précision, par exemple:

let delta = toDate - fromDate

Évidemment, je n'ai pas beaucoup changé, mais pour moi c'est beaucoup plus lisible et conséquent: Swift a le + opérateur déjà implémenté pour un Date et un TimeInterval:

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

Il soutient donc déjà

Date + TimeInterval = Date

En conséquence, il devrait également soutenir

Date - Date = TimeInterval

à mon avis et c'est ce que j'ai ajouté avec la simple mise en œuvre du - opérateur. Maintenant, je peux simplement écrire l'exemple de fonction exactement comme mentionné dans ma 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`
     }
}

Il se pourrait très bien que cela comporte des inconvénients que je ne connais pas en ce moment et j'aimerais entendre vos réflexions à ce sujet.

11
Mischa

Simplement toDate.timeIntervalSince(fromDate) .

Pour réimplémenter votre fonction sans ajouter aucune 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) 
     }
}
5
cakraww

Vous pouvez étendre avec un opérateur personnalisé et renvoyer des 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)
    }

}

En utilisant:

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

Que diriez-vous de quelque chose comme…

func computeNewDate(from fromDate: Date, to toDate: Date) -> Date {
    let delta = Calendar.current.dateComponents([.second], from: fromDate, to: toDate).second!
    return Calendar.current.date(byAdding: .second, value: delta, to: Date())!
}
0
Ashley Mills