Swift - Get next date from a array of weekdays

南笙酒味 提交于 2019-12-12 02:14:31

问题


Context

I'm making an app where I have tasks with due dates and once they are done I want to set a new due date for according to a new date according to a day of the week repeat pattern chosen by the user.

Im saving due dates as Date. Im saving the repeat pattern as Int32 (1 for Sunday, 2 for Monday, 4 for Tuesday...) but I can easily get it to an array of Strings or numbers representing each day

Problem

How do I get the next due date as Date (so I can repeat the task)?

Example

if I have a task that is completed on a Saturday and has repeat pattern of every Monday and Wednesday I want it to be set to the next Monday. If it is them completed on Monday or Tuesday I want to set the next Wednesday.

Photo of the repeat pattern choosing

Month is never selected when other days are selected. I know how to deal with the Month. The problem is just with the days of the week


回答1:


Never use 86400 for date math, Calendar and IndexSethave powerful methods to do that:

// Put your weekday indexes in an `IndexSet`
let weekdaySet = IndexSet([1, 2, 4, 7]) // Sun, Mon, Wed and Sat

// Get the current calendar and the weekday from today
let calendar = Calendar.current
var weekday =  calendar.component(.weekday, from: Date())

// Calculate the next index
if let nextWeekday = weekdaySet.integerGreaterThan(weekday) {
    weekday = nextWeekday
} else {
    weekday = weekdaySet.first!
}

// Get the next day matching this weekday
let components = DateComponents(weekday: weekday)
calendar.nextDate(after: Date(), matching: components, matchingPolicy: .nextTime)



回答2:


I hope this snippet could be helpful

let SecondsPerDay: Int = (24 * 60 * 60)

enum DayOfWeek: Int
{
    case sunday = 1
    case monday = 2
    case tuesday = 3
    case wendnesday = 4
    case thrusday = 5
    case friday = 6
    case saturday = 7

    /**
        Convert a date to a enumeration member
    */
    static func day(from date: Date) -> DayOfWeek
    {
        let formatter: DateFormatter = DateFormatter()
        formatter.dateFormat = "e"


        let day_number: Int = Int(formatter.string(from: date))!

        return DayOfWeek(rawValue: day_number)
    }
}


/**
    Calculate the `TimeInterval` between days contained
    in user preferences.

    - Parameter days: Period selected by user
    - Returns: Interval between dates days
*/
func calculatePattern(withDays days: [DayOfWeek]) -> [TimeInterval]
{
    var pattern: [TimeInterval] = [TimeInterval]()

    for (index, day) in days.enumerated()
    {
        let distance: Int = (index == (days.count - 1)) ?  (7 - (day.rawValue - days[0].rawValue)) : days[index + 1].rawValue - day.rawValue

        let interval: TimeInterval = TimeInterval(SecondsPerDay * distance)

        pattern.append(interval)
    }

    return pattern
}

func nextDay(from days: [DayOfWeek]) -> DayOfWeek
{
    let today: DayOfWeek = DayOfWeek.day(from: Date())

    guard let days = days.filter({ $0.rawValue > today.rawValue }) else
    {
        return days.first!
    }

    return days.first!
}



let selectedDays: [DayOfWeek] = [ .monday, .wendnesday, .sunday, .saturday ]

// Pass the array sorted by day in week
let intervals: [TimeInterval] = calculatePattern(withDays: selectedDays.sorted(by: { $0.rawValue < $1.rawValue}))

for interval in intervals
{
    print(interval)
}

TimeInterval can be converted in Date using one of the Date class initializers.




回答3:


func getdateArryFromWeekdays(_ date: Date, onWeekdaysForNotify weekdays:[Int]) -> [Date]{
    var correctedDate: [Date] = [Date]()
    let now = Date()
    let calendar = Calendar(identifier: Calendar.Identifier.gregorian)
    let flags: NSCalendar.Unit = [NSCalendar.Unit.weekday, NSCalendar.Unit.weekdayOrdinal, NSCalendar.Unit.day]
    let dateComponents = (calendar as NSCalendar).components(flags, from: date)
    let weekday:Int = dateComponents.weekday!

    //no repeat
    if weekdays.isEmpty{
        //scheduling date is eariler than current date
        if date < now {
            //plus one day, otherwise the notification will be fired righton
            correctedDate.append((calendar as NSCalendar).date(byAdding: NSCalendar.Unit.day, value: 1, to: date, options:.matchNextTime)!)
        }
        else { //later
            correctedDate.append(date)
        }
        return correctedDate
    }
        //repeat
    else {
        let daysInWeek = 7
        correctedDate.removeAll(keepingCapacity: true)
        for wd in weekdays {

            var wdDate: Date!
            //schedule on next week
            if compare(weekday: wd, with: weekday) == .before {
                wdDate =  (calendar as NSCalendar).date(byAdding: NSCalendar.Unit.day, value: wd+daysInWeek-weekday, to: date, options:.matchStrictly)!
            }
            else { //after
                wdDate =  (calendar as NSCalendar).date(byAdding: NSCalendar.Unit.day, value: wd-weekday, to: date, options:.matchStrictly)!
            }

            //fix second component to 0
            wdDate = correctSecondComponent(date: wdDate, calendar: calendar)
            correctedDate.append(wdDate)
        }
        return correctedDate
     }

}
func correctSecondComponent(date: Date, calendar: Calendar = Calendar(identifier: Calendar.Identifier.gregorian))->Date {
    let second = calendar.component(.second, from: date)
    let d = (calendar as NSCalendar).date(byAdding: NSCalendar.Unit.second, value: -second, to: date, options:.matchStrictly)!
    return d
}
enum weekdaysComparisonResult {
    case before
    case same
    case after
}


来源:https://stackoverflow.com/questions/42153018/swift-get-next-date-from-a-array-of-weekdays

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