Why are the last two lines of the output the same?
Use NSCalendar to calculate the diff between startTime and endTime, find that the diff between @"2008-02-28 00:00:00" and @"2022-02-28 00:00:00" and the diff between @"2008-02-29 00:00:00" and @"2022-02-28 00:00:00" are the same. It looks like a bug of NSCalendar, maybe about leapMonth?
code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self printDiffBetweenStartTime:@"2008-02-27 00:00:00" endTime:@"2022-02-28 00:00:00"];
[self printDiffBetweenStartTime:@"2008-02-28 00:00:00" endTime:@"2022-02-28 00:00:00"];
[self printDiffBetweenStartTime:@"2008-02-29 00:00:00" endTime:@"2022-02-28 00:00:00"];
}
- (void)printDiffBetweenStartTime:(NSString *)startTime endTime:(NSString *)endTime
{
static NSDateFormatter *dateFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
dateFormatter.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
});
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *startDate = [dateFormatter dateFromString:startTime];
NSDate *endDate = [dateFormatter dateFromString:endTime];
NSCalendarUnit unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *components = [calendar components:unitFlags fromDate:startDate toDate:endDate options:0];
NSLog(@"\"%@\" to \"%@\" : %@ year %@ month %@ day %@ hour %@ minute %@ second", startTime, endTime, @(components.year), @(components.month), @(components.day), @(components.hour), @(components.minute), @(components.second));
}
output:
"2008-02-27 00:00:00" to "2022-02-28 00:00:00" : 14 year 0 month 1 day 0 hour 0 minute 0 second
"2008-02-28 00:00:00" to "2022-02-28 00:00:00" : 14 year 0 month 0 day 0 hour 0 minute 0 second
"2008-02-29 00:00:00" to "2022-02-28 00:00:00" : 14 year 0 month 0 day 0 hour 0 minute 0 second
CodePudding user response:
This is expected. There are many ways to do these period calculations, and the one that NSCalendar
uses turns out to not be the one you expected.
The documentation briefly describes what it does:
Some operations can be ambiguous, and the behavior of the computation is calendar-specific, but generally larger components will be computed before smaller components; for example, in the Gregorian calendar a result might be 1 month and 5 days instead of, for example, 0 months and 35 days.
What this means is that it will compute how many years are in between the two dates first, then months, then days, and so on. "Years" is the biggest component you requested.
And NSCalendar
finds that adding 14 years to 2008-02-28 makes exactly 2022-02-28. Adding 14 years to 2008-02-29 is also exactly 2022-02-28, because 2022 is not a leap year. Note that "adding a year" does not mean the same as "adding 12 months" or "adding 365 days".
For a difference to appear in this case, you need to compute the days first. One period has 5114 days, and the other has 5113.
A few more examples:
If you instead compute the year, month, day period between 2008-02-28 and 2022-02-01, and the period between 2008-02-29 and 2022-02-01. You wouldn't see a difference, both are 13 years, 11 months, and 4 days. This is because adding 13 years to both 2008-02-29 and 2008-02-28 gets you to 2021-02-28, then adding 11 months is 2022-01-28. 4 days after that is 2022-02-01.
However, if you only compute months and days, the period between 2008-02-28 and 2022-02-01, and the period between 2008-02-29 and 2022-02-01 are different.
The period between 2008-02-28 and 2022-02-01 is 167 months and 4 days. Adding 167 months to 2008-02-28 is 2022-01-28. 4 days after that is 2022-02-01.
The period between 2008-02-29 and 2022-02-01 is 167 months and 3 days. Adding 167 months to 2008-02-29 is 2022-01-29. 3 days after that is 2022-02-01.
Period calculations are weird, aren't they! But they are consistent in a unique way.