Reputation: 4580
I'm trying to test one of my methods can compare two times correctly. It works fine unless I add a timezone to my component as follow:
_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);
}
Firs way:
-(NSComparisonResult) compareWithoutTimeTo:(NSDate *) otherDate {
NSDate *this = [self copy];
NSDate *that = [otherDate copy];
return [[NSCalendar currentCalendar] compareDate:this toDate:that toUnitGranularity:NSCalendarUnitDay];;
}
Second way:
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];
These dates are in same day and it must return same but it is returning it is different days?!
Upvotes: 1
Views: 395
Reputation: 131408
This is quite confusing and not very well documented.
@RobMayoff told you what was wrong, but it bears further explanation.
Here's my understanding of how it works. You create NSDateComponents
and give them a time zone. You then convert them to NSDates, which discards the time zone info and expresses those dates as an offset from the iOS epoch time, ALWAYS in UTC. (NSDate
objects don't have a time zone They represent an instant in time anywhere on Earth.)
Finally, you ask a calendar object to compare the 2 dates. The calendar object converts the provided dates to its time zone before doing the comparison. If the 2 dates fall on different days in the calendar's current time zone, you'll get unequal days.
The solution is to create a calendar and set it's time zone to UTC before doing the comparison.
So simply add a second line to the code you posted:
_calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
_calendar.timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; //This is the fix
As rmaddy points out in his comment, there's no requirement that you compare dates in UTC. You just need to make sure you create the dates and compare them using the same time zone. Since you create your input dates using NSDateComponents with a time zone of UTC, you need to compare them with a calendar that is also set to UTC.
Upvotes: 3
Reputation: 385540
You're not setting the time zone of the calendar, so it's using the device's time zone setting, which is probably not UTC. Those two NSDate
values fall on different calendar days in many time zones.
Upvotes: 1