3

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];
AstroCB
  • 12,337
  • 20
  • 57
  • 73

3 Answers3

4

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)
Jon
  • 3,208
  • 1
  • 19
  • 30
  • 1
    @MatthewBahr I'm unsure about that. Swift is the preferred language for iOS development now. This question was asked in Dec 2013, before Swift was released. If the question were asked now there would be a high likelihood that it would be tagged as Swift. iOS developers should be able to quickly convert between Obj-C & Swift syntax. – Jon Dec 12 '17 at 17:16
  • 1
    Just because it's the "preferred" language doesn't mean that it's helpful. I have 2 libraries that are obj-c only for maintenance reasons and getting swift answers on obj c questions isn't helpful. – Matthew Bahr Dec 12 '17 at 18:04
  • 1
    @MatthewBahr It is quite typical on SO. I recommend flagging my post for review if you think it is inappropriate. Best. – Jon Dec 13 '17 at 03:28
  • @MatthewBahr Regardless of the preferred language, the answer to the question is which API's of `NSCalendar`/`Calendar` to use. And to convert that back and fourth beteen ObjC and Swift is rather easy. – netdigger Jan 27 '19 at 11:32
1

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
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
0

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];
Dko
  • 820
  • 6
  • 12