Search code examples
iosobjective-cnsdatenscalendarnsdatecomponents

NSDateComponents getting dates between 2 dates has incorrect day property


I am using NSDateComponents and NSCalendar to get the dates between two dates.

// Get dates between before and after
NSDateComponents *betweenDateComponents = [calendar components:componentFlags
                                                        fromDate:afterDate
                                                          toDate:beforeDate
                                                         options:0];

I then have a simple for loop that is based on the components' day property:

  for (int i = 1; i < betweenDateComponents.day; ++i) {

This works perfectly when I am working with all of the days for a week, or a month. If I filter by week, the day property logs as 6. If I filter by month the day property is 29, but for some reason when I filter by year, the day property logs as 29 instead of 364.

Why is the day property always wrong when I'm trying to work with a year?

Here is all of my code up until the for loop I included above:

// Set the date components according to the type of filter we're doing
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [NSDateComponents new];
    if (filterType == CommentFilterTypeWeeklyByDay) {
        [components setDay:-6];
    } else if (filterType == CommentFilterTypeMonthlyByDay) {
        [components setDay:-29];
    } else if (filterType == CommentFilterTypeYearlyByMonth) {
        [components setDay:-364];
    }

    NSUInteger componentFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;

    // Zero out before and after dates
    NSDate *beforeDate = [NSDate date];
    NSDateComponents *zeroComponents = [[NSCalendar currentCalendar] components:componentFlags fromDate:beforeDate];
    beforeDate = [calendar dateFromComponents:zeroComponents];

    NSDate *afterDate = [calendar dateByAddingComponents:components toDate:beforeDate options:0];
    zeroComponents = [[NSCalendar currentCalendar] components:componentFlags fromDate:afterDate];
    afterDate = [calendar dateFromComponents:zeroComponents];

    NSMutableArray *dates = [NSMutableArray new];
    [dates addObject:afterDate];

    // Get dates between before and after
    NSDateComponents *betweenDateComponents = [calendar components:componentFlags
                                                          fromDate:afterDate
                                                            toDate:beforeDate
                                                           options:0];
    NSLog(@"%d", betweenDateComponents.day);

    for (int i = 1; i < betweenDateComponents.day; ++i) {

Solution

  • Because your components include NSYearCalendarUnit and NSMonthCalendarUnit, I suspect you'll have to use month and year instead of just day. The components are cumulative, not independent.

    You will always be able to get just the number of days (regardless of how many months or years are between your dates) by changing your components:

    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [NSDateComponents new];
    
    switch (filterType) {
      case CommentFilterTypeWeeklyByDay: [components setDay:-6]; break;
      case CommentFilterTypeMonthlyByDay: [components setDay:-29]; break;
      case CommentFilterTypeYearlyByMonth: [components setDay:-364]; break;
      case CommentFilterTypeCustom: [components setDay:-590]; break;
      default: break;
    }
    
    NSUInteger componentFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
    NSDate *beforeDate = [NSDate date];
    NSDateComponents *zeroComponents = [[NSCalendar currentCalendar] components:componentFlags fromDate:beforeDate];
    beforeDate = [calendar dateFromComponents:zeroComponents];
    
    NSDate *afterDate = [calendar dateByAddingComponents:components toDate:beforeDate options:0];
    zeroComponents = [[NSCalendar currentCalendar] components:componentFlags fromDate:afterDate];
    afterDate = [calendar dateFromComponents:zeroComponents];
    
    NSDateComponents *betweenDateComponents = [calendar components:NSDayCalendarUnit
                                                          fromDate:afterDate
                                                            toDate:beforeDate
                                                           options:0];
    
    NSLog(@"%d", betweenDateComponents.day);
    

    The NSLog here prints

    6 for CommentFilterTypeWeeklyByDay

    29 for CommentFilterTypeMonthlyByDay

    364 for CommentFilterTypeYearlyByMonth

    590 for CommentFilterTypeCustom