Найти NSDate для следующего ближайшего определенного дня недели для любой даты

Предположим, что сегодня среда. Я могу разбить NSDate на NSDateComponents, но мне нужно найти NSDate в следующий предстоящий понедельник. Если сегодня понедельник, то следующий предстоящий понедельник будет сегодня. Каков правильный способ добиться этого?


person Bartłomiej Semańczyk    schedule 31.07.2015    source источник
comment
Вы можете взглянуть на github.com/mysterioustrousers/MTDates и там есть метод mt_startOfNextWeek;.   -  person Dániel Nagy    schedule 31.07.2015
comment
Мне нужно решение без каких-либо сторонних библиотек   -  person Bartłomiej Semańczyk    schedule 31.07.2015
comment
Вы имеете в виду следующий предстоящий понедельник или просто ближайший (который может быть в прошлом или в будущем)?   -  person Martin R    schedule 31.07.2015
comment
Я обновил вопрос.   -  person Bartłomiej Semańczyk    schedule 31.07.2015


Ответы (3)


Для этого вы можете использовать метод nextDateAfterDate: для объекта NSCalendar,

let now = Date() // today

var matchingComponents = DateComponents()
matchingComponents.weekday = 2 // Monday

let comingMonday =  Calendar.current.nextDate(after: now,
                                              matching: matchingComponents,
                                              matchingPolicy:.nextTime)

Вот простой способ найти следующий понедельник. Если сегодня понедельник, следующая функция возвращает значение сегодня или ближайший следующий понедельник. Обратите внимание, что он использует en_POSIX_US, чтобы можно было сопоставить дни. Если локаль en_POSIX_US, символы дней недели становятся

["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

И вот как можно было бы использовать эти дни,

func findNext(_ day: String, afterDate date: Date) -> Date? {

    var calendar = Calendar.current
    calendar.locale = Locale(identifier: "en_US_POSIX")

    let weekDaySymbols = calendar.weekdaySymbols
    let indexOfDay = weekDaySymbols.index(of: day)

    assert(indexOfDay != nil, "day passed should be one of \(weekDaySymbols), invalid day: \(day)")

    let weekDay = indexOfDay! + 1

    let components = calendar.component(.weekday, from: date)

    if components == weekDay {
        return date
    }

    var matchingComponents = DateComponents()
    matchingComponents.weekday = weekDay // Monday

    let nextDay = calendar.nextDate(after: date,
                                    matching: matchingComponents,
                                    matchingPolicy:.nextTime)
    return nextDay!
}


let nextMonday = findNext("Monday", afterDate: Date())
let mondayAfterThat = findNext("Monday", afterDate: nextMonday!)

let thursday = findNext("Thursday", afterDate: mondayAfterThat!)
person Sandeep    schedule 31.07.2015
comment
это лучшее решение, я думаю :) - person Bartłomiej Semańczyk; 31.07.2015
comment
Удивительно, что методы NSCalendar для iOS 8 до сих пор не задокументированы в справочной библиотеке. - person Desdenova; 31.07.2015
comment
Да, кажется, они добавили много новых API в NSCalendar и его собратьев. Было бы очень хорошо, если бы они были задокументированы. - person Sandeep; 31.07.2015
comment
Monday не будет работать для других языков, например, Monday на польском языке будет Poniedziałek. Я думаю, что поиск по названию — не лучшее решение, так как календари в устройствах автоматически локализуются. - person Bartłomiej Semańczyk; 31.07.2015
comment
По этой причине локаль используется для календаря с en_US_POSIX, обратите внимание, что он использует системный календарь, но устанавливает локаль en_US_POSIX, просто чтобы извлечь английское имя. - person Sandeep; 31.07.2015
comment
Великолепно! Большое спасибо! - person Frédéric Adda; 01.12.2016

для целевой iOS >= 8 используйте решение GeneratorOfOne

иначе вы можете использовать это

let now = NSDate()
var lastMonday:NSDate?
var nextMonday:NSDate?
var start:NSDate?        

var interval:NSTimeInterval = 0   // holds the length of a week. can differ for Daylight Saving Time
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)! // have a own calendar object for this calculation to guarantee that the next line does not influence other calculations 
cal.firstWeekday = 2              // make sure first day of week is Monday. 


cal.rangeOfUnit(NSCalendarUnit.CalendarUnitWeekOfMonth, startDate: &lastMonday, interval:&interval, forDate: now)      // monday of current week
nextMonday = lastMonday?.dateByAddingTimeInterval(interval)  // add a weeks length to the last weeks's monday
println(nextMonday)
person vikingosegundo    schedule 31.07.2015

Вы можете получить день недели с помощью NSDateComponents и вычислить дни интервала, а затем использовать dateByAddingTimeInterval из NSDate следующим образом:

let now = NSDate()
let calendar: NSDateComponents = NSCalendar.currentCalendar().components(NSCalendarUnit.CalendarUnitWeekday, fromDate: now)
let weekday = calendar.weekday  // 1 = Sunday, 2 = Monday

// You get input the weekday here and calculate the interval

// exmaple for moving to the next day
let expectedDate = now.dateByAddingTimeInterval(1 * 24 * 60 * 60)

// exmaple for moving to yesterday
let yesterday = now.dateByAddingTimeInterval(-1 * 24 * 60 * 60)
person Lucas Huang    schedule 31.07.2015
comment
Не используйте 24*60*60 секунд в качестве продолжительности дня (подумайте о переходе на летнее время). - person Martin R; 31.07.2015
comment
@MartinR извини. Я не понимаю. Можете ли вы объяснить это снова? - person Lucas Huang; 31.07.2015
comment
дни могут длиться 23, 24, 25 часов из-за перехода на летнее время. - person vikingosegundo; 31.07.2015