Search code examples
iosobjective-ccalendarnscalendar

How to get all days in a month that are a specific weekday?


How do I get the actual date of the month based on a given a day ? For example, I would like to retrieve all the dates in June 2017 which are Saturday. How can I achieve that ? Sample code will be very much appreciated as I have struggled for days on this.


Solution

  • A DateComponents has a weekday property, representing the day of the week. The weekdays are (in Foundation's Gregorian calendar) numbered 1 for Sunday, 2 for Monday, …, and 7 for Saturday.

    A DateComponents also has a weekdayOrdinal property, representing “the position of the weekday within the next larger calendar unit, such as the month. For example, 2 is the weekday ordinal unit for the second Friday of the month.

    So let's initialize a DateComponents for some Saturday in June 2017. It's generally a good idea to specify a time of noon if you don't care about the time, because midnight (the default time of day) can cause problems in some time zones on some days.

    var components = DateComponents(era: 1, year: 2017, month: 06, hour: 12, weekday: 7)
    

    And let's make a calendar.

    var calendar = Calendar.autoupdatingCurrent
    

    Now we can loop over all the possible weekday ordinals. For each, we'll ask the calendar to generate a date. Then we ask the calendar to convert the date back to year, month, and day components.

    In the Gregorian calendar, some months have 5 Saturdays, but most have 4. So when we ask for the 5th Saturday, we'll probably get a date in the following month. When that happens, we want to suppress that date.

    for i in 1 ... 5 {
        components.weekdayOrdinal = i
        let date = calendar.date(from: components)!
        let ymd = calendar.dateComponents([.year, .month, .day], from: date)
        guard ymd.month == components.month else { break }
        print("\(ymd.year!)-\(ymd.month!)-\(ymd.day!)")
    }
    

    Output:

    2017-6-3
    2017-6-10
    2017-6-17
    2017-6-24
    

    Objective-C version:

    NSDateComponents *components = [NSDateComponents new];
    components.era = 1;
    components.year = 2017;
    components.month = 6;
    components.hour = 12;
    components.weekday = 7;
    NSCalendar *calendar = NSCalendar.autoupdatingCurrentCalendar;
    
    for (NSInteger i = 1; i <= 5; ++i) {
        components.weekdayOrdinal = i;
        NSDate *date = [calendar dateFromComponents:components];
        NSDateComponents *ymd = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:date];
        if (ymd.month != components.month) { break; }
        NSLog(@"%ld-%ld-%ld", (long)ymd.year, (long)ymd.month, (long)ymd.day);
    }