6

我在这里错过了什么吗?似乎 Apple 提供的方法仅适用于 UTC,无论机器的默认时区如何,或者您将其设置为什么。

这是我得到的输出:

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

我真正想知道的是给定时区(本地或其他地方,不一定是UTC)的两天之间的“多少个午夜”(即多少个日历日差异)

这似乎是一个如此常见且相当简单的问题,以至于我惊讶地发现它是多么混乱和难以弄清楚。

我不是在寻找涉及“mod 86400”或类似脏东西的答案。框架应该能够认真地告诉我这一点。

- (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];
}
4

6 回答 6

3

使用此答案作为起点,我只是将日历添加为附加参数(如果需要,您可以只传递时区而不是日历):

- (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];
}

这将使用您传递的日历来确定和之间的午夜数,fromDateTime并将toDateTime其作为NSInteger. 确保正确设置时区。

使用上面的示例,这是输出:

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

所以是的,我支持我的投票,将这个问题作为重复问题结束,因为它非常接近你正在做的事情。:-)

于 2013-03-07T22:25:58.830 回答
2

以下是我的做法:

// 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);
于 2013-02-02T04:58:37.063 回答
1

这是我能想到的最好的。

无论当前日历是什么和一天中的时间,它都可以工作。

- (NSInteger)daysApartFrom:(NSDate *)startDate toDate:(NSDate *)endDate usingCalendar:(NSCalendar *)localizedTZCal {

    static NSTimeZone *timeZoneUtc;
    static NSCalendar *utcCal;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        timeZoneUtc = [NSTimeZone timeZoneWithName:@"UTC"];

        utcCal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];        
        utcCal.timeZone = timeZoneUtc;
    });

    NSInteger componentFlags = NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
    NSDateComponents *componentsStartDate = [localizedTZCal components:componentFlags fromDate:startDate];
    NSDateComponents *componentsEndDate = [localizedTZCal components:componentFlags fromDate:endDate];

    NSDate *utcStartDate = [utcCal dateFromComponents:componentsStartDate];
    NSDate *utcEndDate = [utcCal dateFromComponents:componentsEndDate];

    NSInteger utcDaysDiff = [self daysWithinEraFromDate:utcStartDate toDate:utcEndDate usingCalendar:utcCal];
    return utcDaysDiff;
}

- (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;
}
于 2013-03-04T07:50:54.757 回答
1

我写了一个小类别来查找两个 NSDate 对象之间的天数。如果我正确理解你的问题,那就是你想要的。

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
于 2013-02-06T15:34:31.690 回答
0

这似乎运作良好:

这是一个类别,用于查找两个 NSDate 对象之间的午夜数。

NSDate+DaysDifference.h

#import <Foundation/Foundation.h>

@interface NSDate (DaysDifference)

+ (int) midnightsBetweenDate:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime;

@end

NSDate+DaysDifference.m

#import "NSDate+DaysDifference.h"

@implementation NSDate (DaysDifference)

+ (int) midnightsBetweenDate:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime;
{
      NSDate* sourceDate = [NSDate date];
      NSTimeZone* destinationTimeZone = [NSTimeZone systemTimeZone];
      NSInteger timeZoneOffset = [destinationTimeZone secondsFromGMTForDate:sourceDate];

      NSCalendar *calendar = [NSCalendar currentCalendar];
      NSInteger startDay = [calendar ordinalityOfUnit:NSDayCalendarUnit
                                               inUnit:NSEraCalendarUnit
                                             forDate:[fromDateTime dateByAddingTimeInterval:timeZoneOffset]];

      NSInteger endDay = [calendar ordinalityOfUnit:NSDayCalendarUnit
                                             inUnit:NSEraCalendarUnit
                                            forDate:[toDateTime dateByAddingTimeInterval:timeZoneOffset]];
      return endDay - startDay;
}

@end
于 2014-07-07T08:35:18.477 回答
-1

这是日历的 Swift 3 扩展,可以解决问题

extension Calendar
{
    /**
     Calculates the number og midnights between two date-times
     - parameter firstDateTime: the date to start counting, defaults to today
     - parameter lastDateTime: the date to end counting
     - returns: the number of midnights between the given date-times
     - note: If firstDateTime is after lastDateTime the result may be negative
     */
    public func midnights(from firstDateTime: Date = Date(), until lastDateTime: Date) -> Int
    {
        let firstStartOfDay = startOfDay(for: firstDateTime)
        let lastStartOfDay = startOfDay(for: lastDateTime)

        return Int(lastStartOfDay.timeIntervalSince(firstStartOfDay)/(60*60*24))
    }
}
于 2017-01-03T12:19:47.817 回答