Question

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?

Était-ce utile?

La solution 3

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!

Autres conseils

I created a simple project with two UIDatePickers in a single ViewController. I initialized each UIDatePicker to the reference date and set an action method to calculate the difference between the dates of the two UIDatePickers each time either one of them changed. I inserted your code into that method and then tested it out in the iPhone Simulator. The result is that your code outputs exactly 10 years:

2011-08-15 11:18:44.225 test[1004:b303] Debug start date = 2001-01-01 00:00:00 +0000
2011-08-15 11:18:44.225 test[1004:b303] Debug end date = 2011-01-01 00:00:00 +0000
2011-08-15 11:18:44.226 test[1004:b303] Debug year = 10
2011-08-15 11:18:44.226 test[1004:b303] Debug month = 0
2011-08-15 11:18:44.227 test[1004:b303] Debug day = 0
2011-08-15 11:18:44.227 test[1004:b303] Debug hours = 0
2011-08-15 11:18:44.227 test[1004:b303] Debug minutes = 0
2011-08-15 11:18:44.228 test[1004:b303] Debug seconds = 0

Have you initialized the UIDatePickers to the same time before using them? By default, the UIDatePickers are initialized to the date when they were created (this includes hours, minutes, seconds, etc, even if you are only showing year, month, and day in the picker).

Is it possible that you are introducing this discrepancy some other way? Perhaps you are doing some other calculations or manipulations on the date?

EDIT:

In the code that you added, you should reference enddate from startdate, rather than from another date object. I know that you said it didn't make a difference, but it is better form to use startdate rather than assume that today and startdate are going to have the same value.

Another thing to consider would be your setStartDate: and setEndDate: methods. In those setter methods, I would recommend rounding to 0 any precision that you don't need. If you don't allow the user to set anything more precise than the day, then set anything higher precision to 0. This way you won't have any left over hours, minutes, or seconds between any two dates.

I can't reproduce this on 10.7. What SDK are you building against?

2011-08-15 13:51:44.262 test[9420:707] Debug start date = 2011-08-15 15:55:07 +0000
2011-08-15 13:51:44.263 test[9420:707] Debug end date = 2021-08-15 15:55:07 +0000
2011-08-15 13:51:44.263 test[9420:707] Debug year = 10
2011-08-15 13:51:44.264 test[9420:707] Debug month = 0
2011-08-15 13:51:44.264 test[9420:707] Debug day = 0
2011-08-15 13:51:44.265 test[9420:707] Debug hours = 0
2011-08-15 13:51:44.265 test[9420:707] Debug minutes = 0
2011-08-15 13:51:44.266 test[9420:707] Debug seconds = 0

Note that there's no reason to extract day, hour, minute and second here. Also, if datecomponent isn't year, this is going to return 0 in your case. I assume you actually mean for it to return 120? Or are you using this a different way?

Here is my full program (for 10.7, command-line template):

int main (int argc, const char * argv[]) {
  @autoreleasepool {
    // Initialize variable to store calendar
    NSDate *startDate = [NSDate dateWithString:@"2011-08-15 15:55:07 +0000"];
    NSDate *endDate = [NSDate dateWithString:@"2021-08-15 15:55:07 +0000"];

    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 = %ld",[components year]);
    NSLog(@"Debug month = %ld",[components month]);
    NSLog(@"Debug day = %ld", [components day]);
    NSLog(@"Debug hours = %ld",[components hour]);
    NSLog(@"Debug minutes = %ld", [components minute]);
    NSLog(@"Debug seconds = %ld", [components second]);

//    [gregorian release];
    return 0;
  }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top