Trouvez la différence en secondes entre NSDates sous forme d'entier à l'aide de Swift

96

J'écris un morceau de code où je veux chronométrer la durée pendant laquelle un bouton a été maintenu enfoncé. Pour ce faire, j'ai enregistré une NSDate()pression sur le bouton et essayé d'utiliser la timeIntervalSinceDatefonction lorsque le bouton a été relâché. Cela semble fonctionner mais je ne trouve aucun moyen d'imprimer le résultat ou de le changer en entier.

var timeAtPress = NSDate() 

@IBAction func pressed(sender: AnyObject) {
    println("pressed")
    timeAtPress = NSDate()
}

@IBAction func released(sender: AnyObject) {
    println("released")
    var elapsedTime = NSDate.timeIntervalSinceDate(timeAtPress)
    duration = ???
}

J'ai vu quelques questions similaires, mais je ne connais pas C donc j'ai eu du mal à comprendre les réponses données. S'il existe un moyen plus efficace de savoir combien de temps le bouton a été maintenu enfoncé, je suis ouvert aux suggestions. Merci d'avance.

Erik
la source

Réponses:

211

Votre tentative de calcul elapsedTimeest incorrecte. Dans Swift 3, ce serait:

let elapsed = Date().timeIntervalSince(timeAtPress)

Notez ()après la Dateréférence. Le Date()instancie un nouvel objet date, puis timeIntervalSincerenvoie la différence de temps entre celui-ci et timeAtPress. Cela renverra une valeur à virgule flottante (techniquement, a TimeInterval).

Si vous voulez que cela soit tronqué à une Intvaleur, vous pouvez simplement utiliser:

let duration = Int(elapsed)

Et, BTW, votre définition de la timeAtPressvariable n'a pas besoin d'instancier un Dateobjet. Je présume que vous aviez l'intention:

var timeAtPress: Date!

Cela définit la variable comme une Datevariable (une variable implicitement déballée), mais vous différeriez vraisemblablement l'instanciation réelle de cette variable jusqu'à ce qu'elle pressedsoit appelée.


Alternativement, j'utilise souvent CFAbsoluteTimeGetCurrent(), par exemple,

var start: CFAbsoluteTime!

Et quand je veux régler startTime, je fais ce qui suit:

start = CFAbsoluteTimeGetCurrent()

Et quand je veux calculer le nombre de secondes écoulées, je fais ce qui suit:

let elapsed = CFAbsoluteTimeGetCurrent() - start

Il convient de noter que la CFAbsoluteTimeGetCurrentdocumentation nous avertit:

Les appels répétés à cette fonction ne garantissent pas des résultats à augmentation monotone. L'heure système peut diminuer en raison de la synchronisation avec des références de temps externes ou en raison d'un changement d'utilisateur explicite de l'horloge.

Cela signifie que si vous avez la malchance de mesurer le temps écoulé lorsque l'un de ces ajustements a lieu, vous pouvez vous retrouver avec un calcul du temps écoulé incorrect. Ceci est également vrai pour NSDate/ Datecalculs. Il est plus sûr d'utiliser un mach_absolute_timecalcul basé (le plus facilement fait avec CACurrentMediaTime):

let start = CACurrentMediaTime()

et

let elapsed = CACurrentMediaTime() - start

Cela utilise mach_absolute_time, mais évite certaines de ses complexités décrites dans la Q&R technique QA1398 .

Rappelez-vous, cependant, que CACurrentMediaTime/ mach_absolute_timesera réinitialisé lorsque l'appareil sera redémarré. Donc, en fin de compte, si vous avez besoin de calculs précis du temps écoulé pendant qu'une application est en cours d'exécution, utilisez CACurrentMediaTime. Mais si vous allez enregistrer cette heure de début dans un stockage persistant, dont vous vous souviendrez peut-être lorsque l'application sera redémarrée à une date ultérieure, vous devez utiliser Dateou CFAbsoluteTimeGetCurrent, et vivre avec toutes les inexactitudes qui peuvent entraîner.

Rob
la source
4
Wow, merci Rob! Votre réponse était extrêmement utile et facile à comprendre. J'apprécie vraiment le conseil sur CFAbsoluteTime. Je vais certainement l'utiliser!
Erik
quelle est la dimension de timeIntervalSinceDate?
eugene
Il renvoie a TimeInterval, qui est mesuré en secondes.
Rob
22

NSDate () et NSCalendar () semblent être un bon choix. Utilisez le calcul du calendrier et laissez la partie mathématique réelle au cadre. Voici un exemple rapide pour obtenir les secondes entre deuxNSDate()

let startDate = NSDate()
let endDate = NSDate()
let calendar = NSCalendar.currentCalendar()
let dateComponents = calendar.components(NSCalendarUnit.CalendarUnitSecond, fromDate: startDate, toDate: endDate, options: nil)
let seconds = dateComponents.second
println("Seconds: \(seconds)")
Freddy
la source
Pour une raison quelconque, cela renvoie toujours 0 pour moi, même si les dates sont séparées d'une heure.
Markymark
10

Selon la réponse de Freddy, voici la fonction de swift 3:

let start = Date()
let end = Date(timeIntervalSince1970: 100)
let calendar = Calendar.current
let unitFlags = Set<Calendar.Component>([ .second])
let datecomponents = calendar.dateComponents(unitFlags, from: start, to: end)
let seconds = datecomponents.second
print(String(describing: seconds))
Andres Paladines
la source
1
Merci @LagMaster. Heureux de voir que Swift supprime le préfixe NS.
Freddy
4

Swift 5

let differenceInSeconds = Int(endDate.timeIntervalSince(startDate))
iKK
la source
0

Voici comment faire la différence dans la dernière version de Swift 3

let calendar = NSCalendar.current
var compos:Set<Calendar.Component> = Set<Calendar.Component>()
compos.insert(.second)
compos.insert(.minute)
let difference = calendar.dateComponents(compos, from: fromDate, to: toDate)
print("diff in minute=\(difference.minute!)") // difference in minute
print("diff in seconds=\(difference.second!)") // difference in seconds

Référence: obtenir la différence entre deux NSDates en (mois / jours / heures / minutes / secondes)

Rujoota Shah
la source
-1

Swift 5

    let startDate = Date()
    let endDate = Date()
    let calendar = Calendar.current
    let dateComponents = calendar.compare(startDate, to: endDate, toGranularity: .second)
    let seconds = dateComponents.rawValue
    print("Seconds: \(seconds)")
Ahmed Safadi
la source
2
Cela ne fournit pas la différence en secondes dans le code, mais seulement si le début est supérieur ou non.
Arun K
-6

Pour Swift 3 secondes entre 2 heures en "hh: mm"

func secondsIn(_ str: String)->Int{
    var strArr = str.characters.split{$0 == ":"}.map(String.init)
    var sec = Int(strArr[0])! * 3600
    var sec1 = Int(strArr[1])! * 36
    print("sec")
    print(sec+sec1)
    return sec+sec1

}

Usage

var sec1 = secondsIn(shuttleTime)
var sec2 = secondsIn(dateToString(Date()))
print(sec1-sec2)
Rishabh Dugar
la source
Cette réponse est sans rapport avec la réponse de l'op.
Tomasz Nazarenko