Find NSDate for the next closest specific day of week for any date
Asked Answered
P

3

8

Let's suppose that today is Wednesday. I can break an NSDate down into NSDateComponents, but I need to find the NSDate with the next upcoming Monday. If today is Monday, then the next upcoming Monday is today. What's the right way to achieve this?

Punctate answered 31/7, 2015 at 7:24 Comment(4)
You could take a look at github.com/mysterioustrousers/MTDates , and it has a mt_startOfNextWeek; method.Weaponeer
I need a solution without any third part librariesDogooder
Do you mean the next upcoming Monday, or just the closest (which could be in the past or in the future) ?Locklear
I updated the question.Dogooder
W
27

You can use nextDateAfterDate: method on NSCalendar object to achieve this,

let now = Date() // today

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

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

Here, is a simple method to find next monday. If today is Monday the following function returns today or else the closest next Monday. Note that it uses en_POSIX_US such that the days can be matched. When the locale is en_POSIX_US, weekdays symbols become,

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

And, here is how this days could be used,

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!)
Weinstein answered 31/7, 2015 at 7:47 Comment(5)
this is the best solution I think:)Dogooder
It's amazing that NSCalendar's iOS 8 methods are still undocumented on the reference library.Lighting
Yes, they seem to have added many new apis to NSCalendar and siblings. It would be really good to have them documented.Weinstein
"Monday" will not work for other languages, Monday in polish is Poniedziałek for instance. I think that searching by name is not the best solution since calendars in devices are automatically localised.Dogooder
That is the reason that locale is used for the calendar with en_US_POSIX, see that it uses the system calendar but sets locale to en_US_POSIX, just to extract english name.Weinstein
M
2

for target iOS >= 8 use GeneratorOfOne's solution

else you can use this

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)
Madlynmadman answered 31/7, 2015 at 8:2 Comment(0)
G
-4

You can get the week day by using NSDateComponents and calculate the interval days, then use dateByAddingTimeInterval from NSDate like so:

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)
Goldofpleasure answered 31/7, 2015 at 7:40 Comment(3)
Don't use 24*60*60 seconds as the duration of a day (think of daylight saving time transitions).Locklear
@MartinR sorry. I don't follow. Can you explain this again?Goldofpleasure
days can be 23, 24, 25 hours long, due to Daylight Saving TimesMadlynmadman

© 2022 - 2024 — McMap. All rights reserved.