Search code examples
swift3nsdatecomponentsdateformatter

Create a list of Time Interval Swift 3


I would like to create a tuple list of times like so; every hour going from 0 to 23.

tuple = [(2017-03-24 00:00:00 +0000, 0.0), (2017-03-24 00:01:00 +0000, 0.0), (2017-03-24 00:02:00 +0000, 0.0), (2017-03-24 00:03:00 +0000, 0.0)]

I have tried to use this code which will give me the hours and minutes, however I cannot seem to figure out how to either add the current month, day and year. Or adjust the date format in the Struct.

Thank you for your help. Here is the Struct that I'm using...

import Foundation

struct TimeDay {

let start: TimeInterval
let end: TimeInterval
let interval: TimeInterval

init(start: TimeInterval, interval: TimeInterval, end: TimeInterval) {
    self.start = start
    self.interval = interval
    self.end = end
}

init(startHour: TimeInterval, intervalMinutes: TimeInterval, endHour: TimeInterval) {
    self.start = startHour * 60 * 60
    self.end = endHour * 60 * 60
    self.interval = intervalMinutes * 60
}

var timeRepresentations: [String] {
    let dateComponentFormatter = DateComponentsFormatter()
    dateComponentFormatter.unitsStyle = .positional
    dateComponentFormatter.allowedUnits = [.minute, .hour]

    let dateComponent = NSDateComponents()
    return timeIntervals.map { timeInterval in
        dateComponent.second = Int(timeInterval)
        return dateComponentFormatter.string(from: dateComponent as DateComponents)!
    }
}

var timeIntervals: [TimeInterval]{
    return Array(stride(from: start, through: end, by: interval))
}
}

Solution

  • A month can have between 28 to 31 days. You can't convert it into an exact number of seconds. Use DateComponents to store your step:

    struct TimeDay {
        var startDate: Date
        var endDate: Date
        var step: DateComponents
    
        var calendar = Calendar.autoupdatingCurrent
        var dateFormatter: DateFormatter = {
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            return formatter
        }()
    
        init(startDate: Date, endDate: Date, step: DateComponents) {
            self.startDate = startDate
            self.endDate   = endDate
            self.step      = step
        }
    
        var timeIntervals : [Date] {
            guard self.startDate <= self.endDate else {
                return []
            }
    
            var result = [self.startDate]
            var date = self.startDate
            while date < self.endDate {
                date = self.calendar.date(byAdding: step, to: date)!
                result.append(date)
            }
    
            return result
        }
    
        var timeRepresentation : [String] {
            return self.timeIntervals.map { self.dateFormatter.string(from: $0) }
        }
    }
    

    Example 1: iterate by hour

    let startDate1 = DateComponents(calendar: .current, year: 2017, month: 3, day: 24).date!
    let endDate1   = DateComponents(calendar: .current, year: 2017, month: 3, day: 25).date!
    
    let t1 = TimeDay(startDate: startDate1, endDate: endDate1, step: DateComponents(hour: 1))
    print(t1.timeRepresentation)
    

    Example 2: iterate by month

    // Iterate by month
    let startDate2 = DateComponents(calendar: .current, year: 2017, month: 1, day: 1).date!
    let endDate2   = DateComponents(calendar: .current, year: 2017, month: 12, day: 31).date!
    
    let t2 = TimeDay(startDate: startDate2, endDate: endDate2, step: DateComponents(month: 1))
    print(t2.timeRepresentation)
    

    Example 3: you can also mix up the time units and iterate by 1 month and 5 days:

    let t3 = TimeDay(startDate: startDate2, endDate: endDate2, step: DateComponents(month: 1, day: 5))
    print(t3.timeRepresentation)
    

    Edit:

    If you want to find the closest hour: (1) move forward to the next hour; (2) move backward to the last hour; (3) select the one that's closest to the specified date

    let date = Date()    
    let closestHour = ([.backward, .forward] as [Calendar.SearchDirection])
        .flatMap { Calendar.current.nextDate(after: date, matching: DateComponents(minute: 0), matchingPolicy: .nextTime, repeatedTimePolicy: .first, direction: $0) }
        .min { abs($0.timeIntervalSince(date)) <  abs($1.timeIntervalSince(date)) }