Encontre a diferença em segundos entre NSDates como inteiros usando Swift

96

Estou escrevendo um trecho de código onde desejo cronometrar por quanto tempo um botão foi mantido pressionado. Para fazer isso, gravei um NSDate()quando o botão foi pressionado e tentei usar a timeIntervalSinceDatefunção quando o botão foi solto. Isso parece funcionar, mas não consigo encontrar nenhuma maneira de imprimir o resultado ou alterná-lo para um número inteiro.

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á vi algumas perguntas semelhantes, mas não sei C, então tive dificuldade em entender as respostas dadas. Se houver uma maneira mais eficiente de descobrir por quanto tempo o botão ficou pressionado, estou aberto a sugestões. Desde já, obrigado.

Erik
fonte

Respostas:

211

Sua tentativa de calcular elapsedTimeestá incorreta. Em Swift 3, seria:

let elapsed = Date().timeIntervalSince(timeAtPress)

Observe o ()depois da Datereferência. O Date()instancia um novo objeto de data e, em seguida, timeIntervalSinceretorna a diferença de tempo entre esse e timeAtPress. Isso retornará um valor de ponto flutuante (tecnicamente, a TimeInterval).

Se quiser que seja truncado em um Intvalor, você pode apenas usar:

let duration = Int(elapsed)

E, BTW, sua definição da timeAtPressvariável não precisa instanciar um Dateobjeto. Eu presumo que você pretendia:

var timeAtPress: Date!

Isso define a variável como uma Datevariável (uma variável implicitamente desembrulhada), mas você presumivelmente adia a instanciação real dessa variável até que pressedseja chamada.


Como alternativa, costumo usar CFAbsoluteTimeGetCurrent(), por exemplo,

var start: CFAbsoluteTime!

E quando quero definir startTime, faço o seguinte:

start = CFAbsoluteTimeGetCurrent()

E quando quero calcular o número de segundos decorridos, faço o seguinte:

let elapsed = CFAbsoluteTimeGetCurrent() - start

Vale ressaltar que a CFAbsoluteTimeGetCurrentdocumentação nos alerta:

Chamadas repetidas para esta função não garantem resultados monotonicamente crescentes. A hora do sistema pode diminuir devido à sincronização com referências de hora externas ou devido a uma mudança explícita do relógio pelo usuário.

Isso significa que, se você tiver a infelicidade de medir o tempo decorrido quando um desses ajustes ocorrer, poderá terminar com o cálculo incorreto do tempo decorrido. Isso também é válido para NSDate/ Datecalculations. É mais seguro usar um mach_absolute_timecálculo baseado (mais facilmente feito com CACurrentMediaTime):

let start = CACurrentMediaTime()

e

let elapsed = CACurrentMediaTime() - start

Isso usa mach_absolute_time, mas evita algumas de suas complexidades descritas nas Perguntas e respostas técnicas QA1398 .

Lembre-se, porém, de que CACurrentMediaTime/ mach_absolute_timeserá reiniciado quando o dispositivo for reiniciado. Portanto, se você precisar de cálculos precisos de tempo decorrido enquanto um aplicativo está em execução, use CACurrentMediaTime. Mas se você vai economizar esse tempo de início no armazenamento persistente, que pode ser lembrado quando o aplicativo for reiniciado em alguma data futura, você terá que usar Dateou CFAbsoluteTimeGetCurrent, e apenas conviver com quaisquer imprecisões que possam acarretar.

Roubar
fonte
4
Uau, obrigado Rob! Sua resposta foi extremamente útil e fácil de entender. Agradeço muito a dica sobre CFAbsoluteTime. Definitivamente vou usar isso!
Erik
qual é a dimensão de timeIntervalSinceDate?
Eugene
Ele retorna um TimeInterval, que é medido em segundos.
Rob
22

NSDate () e NSCalendar () parecem uma boa escolha. Use o cálculo do calendário e deixe a parte matemática real para a estrutura. Aqui está um exemplo rápido de como obter os segundos entre doisNSDate()

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
fonte
Por algum motivo, isso sempre retorna 0 para mim, mesmo que as datas tenham uma hora de diferença.
Markymark
10

De acordo com a resposta de Freddy, esta é a função no 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
fonte
1
Obrigado @LagMaster. Fico feliz em ver que o swift está acabando com o prefixo NS.
Freddy
4

Swift 5

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

É assim que você pode obter a diferença na última versão do 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

Referência: Obtendo a diferença entre dois NSDates em (meses / dias / horas / minutos / segundos)

Rujoota Shah
fonte
-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
fonte
2
Isso não fornece a diferença em segundos no código, mas apenas se o início é maior ou não.
Arun K
-6

Para Swift 3 segundos entre 2 tempos em "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

}

Uso

var sec1 = secondsIn(shuttleTime)
var sec2 = secondsIn(dateToString(Date()))
print(sec1-sec2)
Rishabh Dugar
fonte
Essa resposta é irrelevante para a resposta da op.
Tomasz Nazarenko,