NSDateComponents / NSDate : get the last sunday

3k views Asked by At

I know there are some questions similar to this one, but I just can't find what's wrong with my code.

Basically what I want is if today is not sunday, currentDate is set to the last sunday. If today is sunday I currentDate is set to the sunday before.

Here's how I'm trying to do it.

  NSDateComponents *components = [[NSCalendar currentCalendar] components:  NSWeekCalendarUnit | NSYearCalendarUnit | NSWeekdayCalendarUnit fromDate:currentDate];

    if (components.weekday == 1) { //Its sunday. We just need to subtract a week
        [components setWeek: -1];
        currentDate = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:currentDate options:0];

    }else{ //If its not sunday we just go to sunday
        [components setWeekday: 1];
        currentDate = [[NSCalendar currentCalendar] dateFromComponents:components];

    }

The first time this part of the code is executed I get the right answer. After the first time I get weird dates, like 02 Dec. 4026, and the year keeps going up.

Here's the code that made it work:

   NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [calendar components: ( NSWeekCalendarUnit | NSWeekdayCalendarUnit | NSYearCalendarUnit) fromDate:currentDate];
    int weekday = components.weekday;

    if (weekday == 1) {
        [components setWeek:components.week -1];
    }else{
        [components setWeekday:1];
    }
    currentDate = [calendar dateFromComponents:components];
3

There are 3 answers

4
Jon On BEST ANSWER

Use nextDateAfterDate with the .SearchBackwards option if you are using iOS 8.0+

let calendar = NSCalendar.currentCalendar()
let options: NSCalendarOptions = [.MatchPreviousTimePreservingSmallerUnits, .SearchBackwards]
calendar.nextDateAfterDate(NSDate(), matchingUnit: .Weekday, value: 1, options: options)
2
vikingosegundo On

Try this category on NSCalendar

@interface NSCalendar (LastSunday)
-(NSDate *)previousSundayForDate:(NSDate *)date;
@end

@implementation NSCalendar (LastSunday)

-(NSDate *)previousSundayForDate:(NSDate *)date
{
    static NSUInteger SUNDAY = 1;
    static NSUInteger MONDAY = 2;

    NSDate *startOfWeek;
    [self rangeOfUnit:NSWeekCalendarUnit
            startDate:&startOfWeek
             interval:NULL
              forDate:date];

    if(self.firstWeekday == SUNDAY){

        NSDate *beginningOfDate;
        [self rangeOfUnit:NSDayCalendarUnit
                startDate:&beginningOfDate
                 interval:NULL forDate:date];
        if ([startOfWeek isEqualToDate:beginningOfDate]) {
            startOfWeek = [self dateByAddingComponents:(
                                                        {
                                                            NSDateComponents *comps = [[NSDateComponents alloc] init];
                                                            comps.day = -7;
                                                            comps;
                                                        })
                                                toDate:startOfWeek
                                               options:0];
        }
        return startOfWeek;
    }
    if(self.firstWeekday == MONDAY)
        return [self dateByAddingComponents:(
                                             {
                                                 NSDateComponents *comps = [[NSDateComponents alloc] init];
                                                 comps.day = -1;
                                                 comps;
                                             })
                                     toDate:startOfWeek
                                    options:0];

    return nil;

}

@end

use it as:

NSDate *lastSunday = [calendar previousSundayForDate:[NSDate date]];

PS:

a version for "one-method-one-return-statement"- fetishists:

@implementation NSCalendar (LastSunday)

-(NSDate *)previousSundayForDate:(NSDate *)date
{
    NSDate *resultDate = nil;
    static NSUInteger SUNDAY = 1;
    static NSUInteger MONDAY = 2;

    NSDate *startOfWeek;
    [self rangeOfUnit:NSWeekCalendarUnit
            startDate:&startOfWeek
             interval:NULL
              forDate:date];

    if(self.firstWeekday == SUNDAY){

        NSDate *beginningOfDate;
        [self rangeOfUnit:NSDayCalendarUnit
                startDate:&beginningOfDate
                 interval:NULL forDate:date];

        if ([startOfWeek isEqualToDate:beginningOfDate]) {
            startOfWeek = [self dateByAddingComponents:(
                                                        {
                                                            NSDateComponents *comps = [[NSDateComponents alloc] init];
                                                            comps.day = -7;
                                                            comps;
                                                        })
                                                toDate:startOfWeek
                                               options:0];
        }
        resultDate = startOfWeek;
    }
    if(self.firstWeekday == MONDAY)
        resultDate = [self dateByAddingComponents:(
                                                   {
                                                       NSDateComponents *comps = [[NSDateComponents alloc] init];
                                                       comps.day = -1;
                                                       comps;
                                                   })
                                           toDate:startOfWeek
                                          options:0];

    return resultDate;

}

@end
0
Dko On

The reason you got 4026 is because of this line:

NSDateComponents *components = [[NSCalendar currentCalendar] components:  NSWeekCalendarUnit | NSYearCalendarUnit | NSWeekdayCalendarUnit fromDate:currentDate];

It sets the year component to the current year, 2013. It also initializes the week and weekday components. Then the components are added to the current date, which doubles the year to 4026, here:

[components setWeek: -1];
currentDate = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:currentDate options:0];

If you want to subtract only one week, do it like this:

NSDateComponents *components = [[NSDateComponents alloc] init];
[components setWeek: -1];
currentDate = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:currentDate options:0];
[components release];