【问题标题】:What is the most effective way to iterate over a date range in Swift?在 Swift 中迭代日期范围的最有效方法是什么?
【发布时间】:2017-10-20 21:10:45
【问题描述】:

我正在构建一个 Swift 习惯养成应用程序。在某个时刻,用户选择他们想要养成的习惯的持续时间(以天为单位)。用户还可以选择他们想在一周中的哪几天练习它(周日、周一、周二等)。开始日期是他们养成习惯的那一天。

我目前有开始日期和结束日期(基于用户选择的天数)。但是,在该日期范围内,我需要提取用户选择的特定日期的日期。

我的想法是,对于时间间隔(startDate、endDate)内的每个工作日值“x”,返回日历日期。但是,我尝试创建一个 DateInterval,但我无法对其进行迭代,因为它不符合协议“序列”。

有没有一种有效的方法来迭代这个日期范围并提取这些其他日期?有没有更好的方法我还没有想到?

下面是我当前的日期起始代码:

var date = Date()
var duration = 10
let calendar = Calendar.current

let dateEnding = Calendar.current.date(byAdding: .day, value: duration, to: date as Date)!

//Returns a number representing day of the week.  i.e. 1 = Sunday
let weekDay = calendar.component(.weekday, from: date)


//Initializing a time interval, based on user selected duration and start date
let timeInterval = DateInterval.init(start: date, end: dateEnding)

感谢您的帮助...

【问题讨论】:

    标签: ios swift


    【解决方案1】:

    我们可以利用Strideable协议的力量:

    extension Date: Strideable {
        public func distance(to other: Date) -> TimeInterval {
            return other.timeIntervalSinceReferenceDate - self.timeIntervalSinceReferenceDate
        }
    
        public func advanced(by n: TimeInterval) -> Date {
            return self + n
        }
    }
    

    然后在你的函数中:

    // Iterate by 1 day
    // Feel free to change this variable to iterate by week, month etc.
    let dayDurationInSeconds: TimeInterval = 60*60*24
    for date in stride(from: startDate, to: endDate, by: dayDurationInSeconds) {
        print(date)
    }
    

    输出:

    2018-07-19 12:18:07 +0000
    2018-07-20 12:18:07 +0000
    2018-07-21 12:18:07 +0000
    2018-07-22 12:18:07 +0000
    2018-07-23 12:18:07 +0000
    ....
    

    代码取自this proposal

    【讨论】:

    • 此选项与 Swift 4 和 Xcode 10 完美配合
    • 不幸的是,当当天是 1 月 29 日、1 月 30 日或 31 日并且您尝试迭代超过 40 天时,我遇到了无限循环问题。不清楚为什么存在这种边缘情况,但 for 循环永远不会终止
    • 谢谢@Dan。我已经用更好的解决方案更新了我的答案。它不依赖Calendar,而是直接操作TimeIntervals。
    • 这适用于夏令时吗?例如。日期间隔何时包含小时变化的那一天?
    • 我收到此错误:无法使用类型为“(来自:Date,to:Date,by:Int)”的参数列表调用“stride”。需要一个类型为“(从:T,到:T,由:T.Stride)”的参数列表
    【解决方案2】:

    如果我正确理解您的问题,用户将在某些工作日检查并提供持续时间(天数)。

    假设你有一个数组中选择的工作日和持续时间,你可以得到匹配日期的列表,如下所示:

    // User selected weekdays (1 = Sunday, 7 = Saturday)
    var selectedWeekdays = [2, 4, 6] // Example - Mon, Wed, Fri
    var duration = 10 // Example - 10 days
    
    let calendar = Calendar.current
    var today = Date()
    let dateEnding = calendar.date(byAdding: .day, value: duration, to: today)!
    
    var matchingDates = [Date]()
    // Finding matching dates at midnight - adjust as needed
    let components = DateComponents(hour: 0, minute: 0, second: 0) // midnight
    calendar.enumerateDates(startingAfter: today, matching: components, matchingPolicy: .nextTime) { (date, strict, stop) in
        if let date = date {
            if date <= dateEnding {
                let weekDay = calendar.component(.weekday, from: date)
                print(date, weekDay)
                if selectedWeekdays.contains(weekDay) {
                    matchingDates.append(date)
                }
            } else {
                stop = true
            }
        }
    }
    
    print("Matching dates = \(matchingDates)")
    

    【讨论】:

    • @OwenGodfrey 以什么方式被弃用?所有这些 API 仍在使用中。
    【解决方案3】:

    试试这个

    let calendar = NSCalendar.currentCalendar()
    let startDate = ...
    let endDate = ...
    let dateRange = calendar.dateRange(startDate: startDate,
                                         endDate: endDate,
                                       stepUnits: .Day,
                                       stepValue: 1)
    
    for date in dateRange {
        print("It's \(date)!")
    }
    

    【讨论】:

    • 这是 Swift 2 代码。为什么? Swift 2 早已不复存在。 Swift 4 是当前版本。
    • 不幸的是 dateRange 已被弃用,不再适用于 Swift 4。
    猜你喜欢
    • 2013-01-08
    • 2017-11-21
    • 2019-08-31
    • 1970-01-01
    • 2020-07-18
    • 2015-04-06
    • 2016-05-25
    • 2023-01-11
    • 2013-09-18
    相关资源
    最近更新 更多