Erik
Erik

Reputation: 2409

Find difference in seconds between NSDates as integer using Swift

I'm writing a piece of code where I want to time how long a button was held down. To do that I recorded an NSDate() when the button was pressed, and tried using the timeIntervalSinceDate function when the button was released. That seems to work but I can't find any way to print the result or switch it to an integer.

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

I've seen a few similar questions, but I don't know C so I had a hard time understanding the answers given. If there is a more efficient way to find out how long the button was held down I'm open to suggestions.

Upvotes: 112

Views: 96934

Answers (6)

Rob
Rob

Reputation: 437432

Your attempt to calculate elapsedTime is incorrect. In Swift 3, it would be:

let elapsed = Date().timeIntervalSince(timeAtPress)

Note the () after the Date reference, which calls Date.init.

Alternatively, nowadays (e.g., iOS 15+, macOS 12+), we might prefer to use now, which does precisely the same thing, but makes the functional intent explicit through its name:

let elapsed = Date.now.timeIntervalSince(timeAtPress)

The Date()/Date.init()/Date.now instantiates a new date object, and then timeIntervalSince returns the time difference between that and timeAtPress. That will return a floating point value (technically, a TimeInterval).

If you want that as truncated to a Int value, you can just use:

let duration = Int(elapsed)

There are a few alternatives:

  1. Nowadays (iOS 16+, macOS 13+), we might use a Clock, e.g., a ContinuousClock or a SuspendingClock:

    let clock = ContinuousClock()
    let start = clock.now
    
    // do something
    
    let elapsed = .now - start
    

    Or we can measure the amount of time something takes:

    // for async routines
    
    let elapsed = try await ContinuousClock().measure {
        // something asynchronous with `await`
    }
    
    // for synchronous routines
    
    let elapsed = ContinuousClock().measure {
        // something synchronous
    }
    

    These clocks are introduced about 6 minutes into WWDC 2022 video Meet Swift Async Algorithms.

  2. Sometimes, we just want the number of elapsed seconds, e.g., with CFAbsoluteTimeGetCurrent():

    let start = CFAbsoluteTimeGetCurrent()
    
    // do something
    
    let elapsed = CFAbsoluteTimeGetCurrent() - start
    
  3. It's worth noting that the CFAbsoluteTimeGetCurrent documentation warns us:

    Repeated calls to this function do not guarantee monotonically increasing results. The system time may decrease due to synchronization with external time references or due to an explicit user change of the clock.

    This means that if you're unfortunate enough to measure elapsed time when one of these adjustments take place, you can end up with incorrect elapsed time calculation. This is true for NSDate/Date calculations too. It's safest to use a mach_absolute_time based calculation (most easily done with CACurrentMediaTime):

    let start = CACurrentMediaTime()
    
    // do something
    
    let elapsed = CACurrentMediaTime() - start
    

    This uses mach_absolute_time, but avoids some of its complexities outlined in Technical Q&A QA1398.

    Remember, though, that CACurrentMediaTime/mach_absolute_time will be reset when the device is rebooted. So, bottom line, if you need accurate elapsed time calculations while an app is running, use CACurrentMediaTime. But if you're going to save this start time in persistent storage which you might recall when the app is restarted at some future date, then you have to use Date or CFAbsoluteTimeGetCurrent, and just live with any inaccuracies that may entail.

Upvotes: 248

iKK
iKK

Reputation: 7012

Swift 5

let differenceInSeconds = Int(endDate.timeIntervalSince(startDate))

Upvotes: 36

Andres Paladines
Andres Paladines

Reputation: 1218

According with the Freddy's answer, this is the function in 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))

Upvotes: 11

Freddy
Freddy

Reputation: 2279

NSDate() and NSCalendar() sound like a good choice. Use calendar calculation and leave the actual math part to the framework. Here is a quick example of getting the seconds between two NSDate()

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)")

Upvotes: 24

Rujoota Shah
Rujoota Shah

Reputation: 1321

This is how you can get the difference in latest version of 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

Reference: Getting the difference between two NSDates in (months/days/hours/minutes/seconds)

Upvotes: 0

Rishabh Dugar
Rishabh Dugar

Reputation: 606

For Swift 3 Seconds between 2 time in "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)

Upvotes: -6

Related Questions