Strideable Date

非 Y 不嫁゛ 提交于 2021-01-29 08:41:38

问题


I'm looking for cool ways to stride through Date ranges with different increments (either seconds aka TimeInterval or with DateComponents aka .hour, .minute)

import Foundation

extension Date: Strideable {
    // typealias Stride = SignedInteger // doesn't work (probably because declared in extension
    public func advanced(by n: Int) -> Date {
        self.addingTimeInterval(TimeInterval(n))
    }

    public func distance(to other: Date) -> Int {
        return Int(self.distance(to: other))
    }
}
let now = Date()

let dayAfterNow = Date().addingTimeInterval(86400)
let dateRange = now ... dayAfterNow
let dateArr : [Date] = Array(stride(from: now, to: dayAfterNow, by: 60)) // Solves my task but  not exactly how I wanted.
let formatter: DateFormatter = {
    let df = DateFormatter()
    df.timeStyle = .short
    return df }()
print (dateArr.prefix(7).map { formatter.string(from: $0) })

/// This hoever doesn't work
// There must be a way to make it work but couldn't figure it out for now
let errDateArr: [Date] = Array(dateRange)
// Error: Initializer 'init(_:)' requires that 'Date.Stride' (aka 'Double') conform to 'SignedInteger'

The second part of the question is that also i'd like to have something like:

var components = DateComponents()
components.hour = 8
components.minute = 0
let date = Calendar.current.date(from: components)
let dateByComponentArr : [Date] = Array(stride(from: now, to: dayAfterNow, by: components))

回答1:


As already mentionerd by @Alexander you shouldn't try to conform Date to Strideable but you can implement your own methods to generate the next n days, hours or minutes:

extension Date {
    func year(using calendar: Calendar = .current) -> Int { calendar.component(.year, from: self) }
    func month(using calendar: Calendar = .current) -> Int { calendar.component(.month, from: self) }
    func day(using calendar: Calendar = .current) -> Int { calendar.component(.day, from: self) }
    func hour(using calendar: Calendar = .current) -> Int { calendar.component(.hour, from: self) }
    func minute(using calendar: Calendar = .current) -> Int { calendar.component(.minute, from: self) }
    func nextDays(n: Int, nth: Int = 1, using calendar: Calendar = .current) -> [Date] {
        let year  = self.year(using: calendar)
        let month = self.month(using: calendar)
        let day   = self.day(using: calendar)
        var days: [Date] = []
        for x in 0..<n where x.isMultiple(of: nth) {
            days.append(DateComponents(calendar: calendar, year: year, month: month, day: day + x, hour: 12).date!)

        }
        return days
    }
    func nextHours(n: Int, nth: Int = 1, using calendar: Calendar = .current) -> [Date] {
        let year  = self.year(using: calendar)
        let month = self.month(using: calendar)
        let day   = self.day(using: calendar)
        let hour   = self.hour(using: calendar)
        var hours: [Date] = []
        for x in 0..<n where x.isMultiple(of: nth) {
            hours.append(DateComponents(calendar: calendar, year: year, month: month, day: day, hour: hour + x).date!)

        }
        return hours
    }
    func nextMinutes(n: Int, nth: Int = 1, using calendar: Calendar = .current) -> [Date] {
        let year  = self.year(using: calendar)
        let month = self.month(using: calendar)
        let day   = self.day(using: calendar)
        let hour   = self.hour(using: calendar)
        let minute   = self.minute(using: calendar)
        var minutes: [Date] = []
        for x in 0..<n where x.isMultiple(of: nth) {
            minutes.append(DateComponents(calendar: calendar, year: year, month: month, day: day, hour: hour, minute: minute + x).date!)

        }
        return minutes
    }
}

extension Date {
    static let formatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.timeStyle = .short
        return formatter
    }()
}

Playgournd testing:

let days = Date().nextDays(n: 10)
for day in days {
    print(Date.formatter.string(from: day))
}

let hours = Date().nextHours(n: 10)
for hour in hours {
    print(Date.formatter.string(from: hour))
}

let minutes = Date().nextMinutes(n: 10)
for minute in minutes {
    print(Date.formatter.string(from: minute))
}



回答2:


There's a conflicting implementation of distance(to:) already declared on Date: Date.distance(to:). The implementation you defined has the same name, but a different type. This other overload could work, but unfortunately for you, Date already declares a typealias called Stride, which it sets to TimeInterval (just an alias for Double).

I think conforming to Date to Strideable is a bad idea. Take Double for example, which intentionally doesn't conform to Strideable: there's no clear universally-best stride definition. Should it go up by 0.1? 0.01? 3.14? It's not obvious. stride(from:to:by:) exists precisely for this reason, for you to be explicit by how much you're striding by.

let errDateArr: [Date] = Array(now ... dayAfterNow) would definitely score high "WTFs/m" points




回答3:


For the second part of the question I created a simple sequence that takes daylight saving settings and other stuff in account. It might be helpful in some specific cases.

let dayAfterNow = Date().addingTimeInterval(86400)
var components = DateComponents()
components.hour = 8
components.minute = 0

func dateIterator(start: Date = Date(), by components: DateComponents, wrappingComponents: Bool = false) -> AnyIterator<Date> {
    var state = start
    return AnyIterator {
        
        let nextDate = Calendar.current.date(byAdding: components, to: state, wrappingComponents: wrappingComponents)
        state = nextDate ?? state
        return state
    }    
}


let dateCompSequence = AnySequence(dateIterator(by: components))
let dateArray = Array(dateCompSequence.prefix(10))
dateArray.map{print($0.description)}
print("starting for loop...")

for d in dateCompSequence.prefix(10) {
    print(d.description)
}

It is worth noting that Calendar.current.enumerateDates() will likely always help with similar tasks. But having a sequence is sometimes preferable. In earlier version of Swift there seemed to be a strideable DateRange type provided by NSCalendar (just the thing I wanted), but now there is nothing like it in standard library.



来源:https://stackoverflow.com/questions/62718768/strideable-date

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!