Search code examples
objective-cxcodensdateformatter

Xcode - Set week of month, then get the week of year


Currently trying to use NSDateFormatter and NSDateComponents to set the week number for a month, then find the week of year that would be.

Let’s say we have a date: 20/05/2020, then set the week number to 0, or 1, or 2, etc.. then find out the week of year from that.

So far with NSDateComponents I’ve tried doing:

NSDate *date = self.date; // 20/05/2020
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSCalendarUnitMonth | NSCalendarUnitYear fromDate:date];

[components setWeekOfMonth:1];
NSDate *newDate = [calendar dateFromComponents:components];

NSInteger weekOfYear = [calendar components:NSCalendarUnitWeekOfYear fromDate:newDate].weekOfYear;

I'm always getting the output 18.

Expected output:

Change week of month to: 1 | output: 19 (4th)
Change week of month to: 2 | output: 20 (11th)
Change week of month to: 3 | output: 21 (18th)
Change week of month to: 4 | output: 22 (25th)

Solution

  • The problem is setting setWeekOfMonth in the date components has no effect.

    Instead set the weekday and weekdayOrdinal

    NSDate *date = self.date; // 20/05/2020
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [calendar components:NSCalendarUnitMonth | NSCalendarUnitYear fromDate:date];
    components.weekday = 2;
    components.weekdayOrdinal = 3;
    NSDate *newDate = [calendar dateFromComponents:components];
    
    NSInteger weekOfYear = [calendar components:NSCalendarUnitWeekOfYear fromDate:newDate].weekOfYear;
    

    This is a different approach:

    It calculates the first monday in the current month, then it enumerates the next mondays until the end of the month.

    - (NSArray *)yearOfWeeksInCurrentMonth
    {
        NSCalendar *calendar = [NSCalendar currentCalendar];
        NSDateComponents *components = [calendar components: NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
        components.weekday = 2;
        components.weekdayOrdinal = 1;
    
        NSDate *firstMonday = [calendar dateFromComponents:components];
        NSInteger weekOfYear = [calendar component:NSCalendarUnitWeekOfYear fromDate:firstMonday];
    
        __block NSMutableArray *result = [NSMutableArray array];
        [result addObject:@[firstMonday, @(weekOfYear)]];
        NSDateComponents *weekComponents = [calendar components: NSCalendarUnitWeekday fromDate:firstMonday];
        [calendar enumerateDatesStartingAfterDate:firstMonday matchingComponents:weekComponents options:NSCalendarMatchNextTime usingBlock:^(NSDate * _Nullable date, BOOL exactMatch, BOOL * _Nonnull stop) {
            if (date != nil) {
                if ( [calendar component:NSCalendarUnitMonth fromDate:date] > components.month ) {
                    *stop = YES;
                } else {
                    NSInteger weekOfYear = [calendar component:NSCalendarUnitWeekOfYear fromDate:date];
                    [result addObject:@[date, @(weekOfYear)]];
                }
            }
        }];
        return [result copy];
    }
    

    The return value is an array containing the date (NSDate) and the corresponding weekOfYear (NSNumber)

    (
            (
            "2020-05-03 22:00:00 +0000",
            19
        ),
            (
            "2020-05-10 22:00:00 +0000",
            20
        ),
            (
            "2020-05-17 22:00:00 +0000",
            21
        ),
            (
            "2020-05-24 22:00:00 +0000",
            22
        )
    )