Am I missing something here? It seems like the method provided by Apple only works for UTC, regardless of the timezone default of the machine, or what you set it to.
Here's the output I get:
Output:
2013-02-01 10:41:24.152 Scratch[17640:c07] cal=gregorian, cal.timeZone=America/Los_Angeles (PST) offset -28800
2013-02-01 10:41:24.154 Scratch[17640:c07] date_Feb1_1400PST=2013-02-01 14:00 -0800
2013-02-01 10:41:24.156 Scratch[17640:c07] date_Feb2_1200PST=2013-02-02 12:00 -0800
2013-02-01 10:41:24.157 Scratch[17640:c07] midnights between=1
2013-02-01 10:41:24.158 Scratch[17640:c07] and then...
2013-02-01 10:41:24.159 Scratch[17640:c07] date_Feb1_2000PST=2013-02-01 22:00 -0800
2013-02-01 10:41:24.161 Scratch[17640:c07] date_Feb2_1000PST=2013-02-02 10:00 -0800
2013-02-01 10:41:24.161 Scratch[17640:c07] midnights between=0
What I really want to know is "how many midnights" (i.e., how many calendar days diff) between two days for a given timezone (local or otherwise, and not necessarily UTC)
This seems like such a common and reasonably simple question that I'm surprised to see how messy and difficult to figure out.
I'm not looking for an answer that involves "mod 86400" or something filthy like that. The framework should be able to tell me this, seriously.
- (void)doDateComparisonStuff {
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
cal.timeZone = [NSTimeZone timeZoneWithName:@"America/Los_Angeles"];
NSLog(@"cal=%@, cal.timeZone=%@", cal.calendarIdentifier, cal.timeZone);
NSDate *date_Feb1_1400PST = [self dateFromStr:@"20130201 1400"];
NSLog(@"date_Feb1_1400PST=%@", [self stringFromDate:date_Feb1_1400PST]);
NSDate *date_Feb2_1200PST = [self dateFromStr:@"20130202 1200"];
NSLog(@"date_Feb2_1200PST=%@", [self stringFromDate:date_Feb2_1200PST]);
NSLog(@"midnights between=%d", [self daysWithinEraFromDate:date_Feb1_1400PST toDate:date_Feb2_1200PST usingCalendar:cal]);
NSLog(@"and then...");
NSDate *date_Feb1_2000PST = [self dateFromStr:@"20130201 2200"];
NSLog(@"date_Feb1_2000PST=%@", [self stringFromDate:date_Feb1_2000PST]);
NSDate *date_Feb2_1000PST = [self dateFromStr:@"20130202 1000"];
NSLog(@"date_Feb2_1000PST=%@", [self stringFromDate:date_Feb2_1000PST]);
NSLog(@"midnights between=%d", [self daysWithinEraFromDate:date_Feb1_2000PST toDate:date_Feb2_1000PST usingCalendar:cal]);
}
// based on "Listing 13" at
// https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DatesAndTimes/Articles/dtCalendricalCalculations.html#//apple_ref/doc/uid/TP40007836-SW1
- (NSInteger)daysWithinEraFromDate:(NSDate *)startDate toDate:(NSDate *)endDate usingCalendar:(NSCalendar *)cal
{
NSInteger startDay=[cal ordinalityOfUnit:NSDayCalendarUnit
inUnit: NSEraCalendarUnit forDate:startDate];
NSInteger endDay=[cal ordinalityOfUnit:NSDayCalendarUnit
inUnit: NSEraCalendarUnit forDate:endDate];
return endDay-startDay;
}
- (NSDate *)dateFromStr:(NSString *)dateStr {
NSDateFormatter *df = nil;
df = [[NSDateFormatter alloc] init];
df.timeZone = [NSTimeZone timeZoneWithName:@"America/Los_Angeles"];
df.dateFormat = @"yyyyMMdd HHmm";
return [df dateFromString:dateStr];
}
- (NSString *)stringFromDate:(NSDate *)date {
NSDateFormatter *df = nil;
df = [[NSDateFormatter alloc] init];
df.timeZone = [NSTimeZone timeZoneWithName:@"America/Los_Angeles"]; // native timezone here
df.dateFormat = @"yyyy-MM-dd HH:mm Z";
return [df stringFromDate:date];
}
Using this answer as a starting point, I simply added the calendar as an additional argument (you could just pass a timezone instead of the calendar if you want):
- (NSInteger)daysBetweenDate:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime usingCalendar:(NSCalendar *)calendar
{
NSDate *fromDate;
NSDate *toDate;
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate
interval:NULL forDate:fromDateTime];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate
interval:NULL forDate:toDateTime];
NSDateComponents *difference = [calendar components:NSDayCalendarUnit
fromDate:fromDate toDate:toDate options:0];
return [difference day];
}
This will use the calendar that you pass to determine the number of midnights between fromDateTime
and toDateTime
and return it as a NSInteger
. Make sure that you set the timezone appropriately.
Using your examples above, this is the output:
2013-03-07 17:23:54.619 Testing App[69968:11f03] cal=gregorian, cal.timeZone=America/Los_Angeles (PST) offset -28800
2013-03-07 17:23:54.621 Testing App[69968:11f03] date_Feb1_1400PST=20130201 1400
2013-03-07 17:23:54.621 Testing App[69968:11f03] date_Feb2_1200PST=20130202 1200
2013-03-07 17:23:54.622 Testing App[69968:11f03] midnights between=1
2013-03-07 17:23:54.622 Testing App[69968:11f03] and then...
2013-03-07 17:23:54.623 Testing App[69968:11f03] date_Feb1_2000PST=20130201 2200
2013-03-07 17:23:54.624 Testing App[69968:11f03] date_Feb2_1000PST=20130202 1000
2013-03-07 17:23:54.624 Testing App[69968:11f03] midnights between=1
So yes, I stand by my voting to close this question as a duplicate as it is VERY close to what you are doing. :-)
Here's how I'd go about it:
// pick a random timezone
// obviously you'd replace this with your own desired timeZone
NSArray *timeZoneNames = [NSTimeZone knownTimeZoneNames];
NSTimeZone *randomZone = [NSTimeZone timeZoneWithName:[timeZoneNames objectAtIndex:(arc4random() % [timeZoneNames count])]];
// create a copy of the current calendar
// (because you should consider the +currentCalendar to be immutable)
NSCalendar *calendar = [[NSCalendar currentCalendar] copy];
// change the timeZone of the calendar
// this causes all computations to be done relative to this timeZone
[calendar setTimeZone:randomZone];
// your start and end dates
// obviously you'd replace this with your own dates
NSDate *startDate = [NSDate dateWithTimeIntervalSinceReferenceDate:1234567890.0];
NSDate *endDate = [NSDate dateWithTimeIntervalSinceReferenceDate:1234890567.0];
// compute the midnight BEFORE the start date
NSDateComponents *midnightComponentsPriorToStartDate = [calendar components:NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:startDate];
NSDate *midnightPriorToStartDate = [calendar dateFromComponents:midnightComponentsPriorToStartDate];
// this will keep track of how many midnights there are
NSUInteger numberOfMidnights = 0;
// loop F.O.R.E.V.E.R.
while (1) {
// compute the nth midnight
NSDateComponents *dayDiff = [[NSDateComponents alloc] init];
[dayDiff setDay:numberOfMidnights+1];
NSDate *nextMidnight = [calendar dateByAddingComponents:dayDiff toDate:midnightPriorToStartDate options:0];
// if this midnight is after the end date, we stop looping
if ([endDate laterDate:nextMidnight] == nextMidnight) {
// this next midnight is after the end date
break; // ok, maybe not forever
} else {
// this midnight is between the start and end date
numberOfMidnights++;
}
}
NSLog(@"There are %lu midnights between %@ and %@", numberOfMidnights, startDate, endDate);
I've written a small category to find the number of days between two NSDate-objects. If I understand your question correctly, that's what you want.
NSDate+DaysDifference.h
#import <Foundation/Foundation.h>
@interface NSDate (DaysDifference)
- (NSInteger)differenceInDaysToDate:(NSDate *)otherDate;
@end
NSDate+DaysDifference.m
#import "NSDate+DaysDifference.h"
@implementation NSDate (DaysDifference)
- (NSInteger)differenceInDaysToDate:(NSDate *)otherDate {
NSCalendar *cal = [NSCalendar autoupdatingCurrentCalendar];
NSUInteger unit = NSDayCalendarUnit;
NSDate *startDays, *endDays;
[cal rangeOfUnit:unit startDate:&startDays interval:NULL forDate:self];
[cal rangeOfUnit:unit startDate:&endDays interval:NULL forDate:otherDate];
NSDateComponents *comp = [cal components:unit fromDate:startDays toDate:endDays options:0];
return [comp day];
}
@end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With