【问题标题】:Strideable Date可跨越的日期
【发布时间】:2020-07-03 15:46:53
【问题描述】:

我正在寻找很酷的方法来跨越 Date 范围以不同的增量(秒又名 TimeIntervalDateComponents 又名 .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'

问题的第二部分是我也想要类似的东西:

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

【问题讨论】:

标签: swift range sequence date-range


【解决方案1】:

正如@Alexander 已经提到的,您不应该尝试将 Date 与 Strideable 保持一致,但您可以实现自己的方法来生成接下来的 n 天、小时或分钟:

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
    }()
}

游乐场测试:

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

【讨论】:

  • 看起来很方便。尤其是nth 部分。
【解决方案2】:

distance(to:) 的实现存在冲突,已在 Date:Date.distance(to:) 上声明。您定义的实现具有相同的名称,但类型不同。这个其他重载可以工作,但不幸的是,Date 已经声明了一个名为Stride 的类型别名,它设置为TimeInterval(只是Double 的别名)。

我认为遵循DateStrideable 是个坏主意。以Double 为例,它有意 不符合Strideable:没有明确的普遍最佳步幅定义。它应该上升0.1 吗? 0.01? 3.14?这并不明显。 stride(from:to:by:) 存在 正是出于这个原因,以便您明确指出自己的步伐。

let errDateArr: [Date] = Array(now ... dayAfterNow) 肯定会高分"WTFs/m" points

【讨论】:

  • Stride 类型不是Int 的边界之间生成默认序列(没有明确的步骤)确实是个坏主意。然而Double 确实符合Stridable,否则不可能用它创建stride 序列。
  • 啊,我对 Strideable 的目的理解有误。 ... 的能力不仅仅取决于与 Strideable 的一致性,它要求 StrideSignedInteger
【解决方案3】:

对于问题的第二部分,我创建了一个简单的序列,其中考虑了夏令时设置和其他内容。在某些特定情况下可能会有所帮助。

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

值得注意的是,Calendar.current.enumerateDates() 可能总是会帮助完成类似的任务。但有时最好有一个顺序。在早期版本的 Swift 中,NSCalendar 提供了一个可跨步的 DateRange 类型(正是我想要的),但现在标准库中没有类似的东西。

【讨论】:

  • 你为什么要通过第 0 分钟?您没有设置要添加的组件。
  • 这很容易出错。如果您传递等于 1 的日组件并立即开始,它将返回 7/14/20、3:32 PM 7/15/20、3:32 PM 7/16/20、3:32 PM 7/17/20、 20 年 7 月 18 日下午 3:32,20 年 7 月 19 日下午 3:32,20 年 7 月 20 日下午 3:32,20 年 7 月 21 日下午 3:32,20 年 7 月 22 日下午 3:32, 20 年 7 月 23 日下午 3:32,下午 3:32
  • 如果开始日期是“20 年 6 月 30 日,上午 11:45”,并且您通过天组件 = 1,它将导致 20 年 6 月 11 日上午 11:45 6/12/ 20, 11:45 AM 6/13/20, 11:45 AM 6/14/20, 11:45 AM 6/15/20, 11:45 AM 6/16/20, 11:45 AM 6/17/ 20 日上午 11:45 20 年 6 月 18 日上午 11:45 20 年 6 月 19 日上午 11:45 20 年 6 月 20 日上午 11:45
  • 我不确定,但看起来您正在尝试实现类似stackoverflow.com/a/62665165/2303865
  • 但仍有一些问题。尝试let start = DateComponents(calendar: .current, year: 2020, month: 6, day: 30, hour: 23, minute: 45).date! for d in dateIterator(start: start, by: .init(month: 1)).prefix(10) { print(Date.formatter.string(from: d)) } 并检查二月后返回的日期
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-20
  • 2018-04-09
  • 1970-01-01
  • 2021-12-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多