Search code examples
iphonecocoa-touchnsdatenscalendarnsdatecomponents

NSDateComponents is missing 1 second from difference of 2 dates


Got a really frustrating problem that doesn't seem to make any sense. I'm trying to get the number of years between 2 dates. Here is my code.

// Initialize variable to store calendar
NSCalendar *gregorian = [[NSCalendar alloc]  initWithCalendarIdentifier:NSGregorianCalendar];

// Break out date to year component
NSDateComponents *Components = [gregorian components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit
                                            fromDate:startDate
                                              toDate:endDate
                                             options:0];

// Debugging code
NSLog(@"Debug start date = %@",startDate);
NSLog(@"Debug end date = %@", endDate);
NSLog(@"Debug year = %d",[Components year]);
NSLog(@"Debug month = %d",[Components month]);
NSLog(@"Debug day = %d", [Components day]);
NSLog(@"Debug hours = %d",[Components hour]);
NSLog(@"Debug minutes = %d", [Components minute]);
NSLog(@"Debug seconds = %d", [Components second]);

[gregorian release];

// Check which component to extract and return value accordingly
// Defaults to month for now
if ([datecomponent isEqualToString:@"year"]) {
    return [Components year];
}
else {
    return [Components month];
}

The start and end dates are set by UIDatePickers elsewhere. They would default to being 10 years apart. Once I go back to the UIDatePicker that controls the end date and move that up to 1 year earlier. The problem will start appearing. This is an example of the NSLog I will see after I move the end date back to the original date. I should see 10 years and 0 everywhere else but I'm missing 1 second in time.

Debug start date = 2011-08-15 15:55:07 +0000
Debug end date = 2021-08-15 15:55:07 +0000
Debug year = 9
Debug month = 11
Debug day = 30
Debug hours = 23
Debug minutes = 59
Debug seconds = 59

The start and end dates look identical saved for the 10 years between them to me but for some reason, I'm missing 1 second of time. Does anyone know why?

Thanks in advance!

ADDED

Here is the way I have initialized and stored the 2 dates.

The start date is in the viewWillAppear method.

- (void)viewWillAppear:(BOOL)animated {
    if ([managedObject valueForKeyPath:self.keypath] != nil)
        [self.datePicker setDate:[managedObject
                                  valueForKeyPath:keypath] animated:YES];
    else
        [self.datePicker setDate:[NSDate date] animated:YES];
    [self.tableView reloadData];

    [super viewWillAppear:animated];

}

The same class is also used for my end date however I have also added the following code in my custom subclass of NSManagedObject:-

- (void)awakeFromInsert {
    [super awakeFromInsert];

    // Set default date of registration to be today's date
    self.startdate = [NSDate date];

    NSDate *today = [[NSDate alloc] init]; // I also tried to equate this to self.startdate but there is no difference
    NSCalendar *gregorian = [[NSCalendar alloc]     initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
    [offsetComponents setYear:10];
    self.enddate = [gregorian dateByAddingComponents:offsetComponents toDate:today     options:0];

}

Would this have caused any problems? If yes, why would the debug show the start and end dates including their times as exactly the same?


Solution

  • After quite a bit of tinkering with various scenarios, this is the way which I solved the issue.

    1) Since the code to find the difference between the dates checks out ok by several independent parties, I have eliminated it as a source of problem.

    2) I have removed any code that attempts to modify the dates in my app which should not have been there in the first place.

    3) I now initialize the time as well when I create the start and end dates so as to ensure that they are not some arbitrary time based on when I create the entry in database.

    Here's the code snippet to initialize the start date.

    NSDate *now = [NSDate date];
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *settomidnight = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:now];
    [settomidnight setHour:0];
    [settomidnight setMinute:0];
    [settomidnight setSecond:0];
    startdate = [calendar dateFromComponents:settomidnight];
    

    With this, I see from the NSLog that the time is now set as 16:00 +0000 (which is midnight for me after converting to my timezone). The problem has now gone away and I'm no longer losing my 1 second.

    Thanks to all who have helped guide me on the right path!