为什么在 UTC 时区比较两个 NSDate 总是失败?

Why comparing two NSDate in UTC timezone always fails?

我正在尝试测试我的一种方法是否可以正确比较两次。它工作正常,除非我按如下方式向我的组件添加时区:

_calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];

NSDateComponents *components = [[NSDateComponents alloc] init];


components.second = 31;
components.minute = 21;
components.hour = 5;
components.day = 30;
components.month = 3;
components.year = 2016;
components.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
_mar30_2016Morning = [_calendar  dateFromComponents:components];


components.second = 01;
components.minute = 00;
components.hour = 22;
components.day = 30;
components.month = 3;
components.year = 2016;
components.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
_mar30_2016Evening = [_calendar  dateFromComponents:components];



//Test two dates are equal
-(void) testCompareWithoutTimeTo
{
    XCTAssertEqual([_mar30_2016Morning compareWithoutTimeTo:_mar30_2016Evening],NSOrderedSame);
}

第一种方式:

-(NSComparisonResult) compareWithoutTimeTo:(NSDate *) otherDate {
    NSDate *this = [self copy];
    NSDate *that = [otherDate copy];
    return [[NSCalendar currentCalendar] compareDate:this toDate:that toUnitGranularity:NSCalendarUnitDay];;
}

第二种方式:

NSCalendar* calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
[[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay startDate:&this interval:NULL forDate:this];
[[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay startDate:&that interval:NULL forDate:that];
NSComparisonResult result = [this compare:that];

这些日期是同一天,它必须 return 相同但它 return 是不同的日子?!

您没有设置日历的时区,因此它使用的是设备的时区设置,这可能不是 UTC。这两个 NSDate 值落在许多时区的不同日历日。

这很令人困惑,而且没有很好的记录。

@RobMayoff 告诉你出了什么问题,但还需要进一步解释。

这是我对其工作原理的理解。您创建 NSDateComponents 并给他们一个时区。然后将它们转换为 NSDates,它会丢弃时区信息并将这些日期表示为与 iOS 纪元时间的偏移量,始终采用 UTC。 (NSDate 物体没有时区 它们代表地球上任何地方的瞬间。)

最后,您要求一个日历对象比较这两个日期。日历对象在进行比较之前将提供的日期转换为其时区。如果这 2 个日期落在日历当前时区的不同日期,您将得到不相等的天数。

解决方法是创建一个日历并将其时区设置为 UTC,然后再进行比较。

所以只需在您发布的代码中添加第二行:

_calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
_calendar.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];  //This is the fix

编辑:

正如 rmaddy 在他的评论中指出的那样,不要求您比较 UTC 日期。您只需要确保创建日期并使用相同时区比较它们。由于您使用时区为 UTC 的 NSDateComponents 创建输入日期,因此您需要将它们与也设置为 UTC 的日历进行比较。