iosswifttimensdatenstimeinterval

Calculate average time between an array of dates


I have an array of objects which the app gets from a WebService, each object has a createdTime and objects are created randomly from 6 in the morning to midnight.

I want to know what is the average time between each object creation. What is the best and most efficient way to implement it?

The dates are in this format: "CreatedTime": "2019-02-18T22:06:30.523"


Solution

  • The average date interval is the time elapsed between the first and last date and divide by n-1, the number of intervals. That’s going to be most efficient.

    This works because the average is equal to the sum of the intervals divided by the number of intervals. But the sum of all the intervals is equal to the difference between the first and last date.

    Assuming your date strings are already in order, just grab the first and last, calculate the difference and divide.

    let dateStrings = ["2019-02-18T18:06:30.523", "2019-02-18T19:06:30.523", "2019-02-18T21:06:30.523"]
    
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
    dateFormatter.locale = Locale(identifier: "en_US_POSIX")
    dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)           // I’m going to assume it’s GMT; what is it really?
    
    guard dateStrings.count > 1,
        let lastDateString = dateStrings.last,
        let lastDate = dateFormatter.date(from: lastDateString),
        let firstDateString = dateStrings.first,
        let firstDate = dateFormatter.date(from: firstDateString) else { return }
    
    let average = lastDate.timeIntervalSince(firstDate) / Double(dateStrings.count - 1)
    

    That’s in seconds. If you’d like a nice string format and don’t care about milliseconds, the DateComponentsFormatter is convenient for localized strings:

    let dateComponentsFormatter = DateComponentsFormatter()
    dateComponentsFormatter.allowedUnits = [.hour, .minute, .second]
    dateComponentsFormatter.unitsStyle = .full
    let string = dateComponentsFormatter.string(from: average)
    

    That produces:

    "1 hour, 30 minutes"


    Or you can, less efficiently, build the dates array:

    let dateStrings = ["2019-02-18T18:06:30.523", "2019-02-18T19:06:30.523", "2019-02-18T21:06:30.523"]
    
    guard dateStrings.count > 1 else { return }
    
    let dates = dateStrings.map { dateFormatter.date(from: $0)! }
    

    Then you could build an array of intervals between those dates:

    var intervals: [TimeInterval] = []
    for index in 1 ..< dates.count {
        intervals.append(dates[index].timeIntervalSince(dates[index-1]))
    }
    

    And then average them:

    let average = intervals.reduce(0.0, +) / Double(intervals.count)
    

    And format to taste:

    let dateComponentsFormatter = DateComponentsFormatter()
    dateComponentsFormatter.allowedUnits = [.hour, .minute, .second]
    dateComponentsFormatter.unitsStyle = .full
    let string = dateComponentsFormatter.string(from: average)