问题
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 IndexSet
have 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