5

我正在为 iPhone 编写一个 GTD 应用程序。对于到期任务,我想显示“明天到期”或“昨天到期”或“7 月 18 日到期”之类的内容。显然,即使距离任务不到 24 小时,我也需要显示“明天”(例如,用户在周六晚上 11 点检查并看到周日早上 8 点有任务)。所以,我写了一个方法来获取两个日期之间的天数。这是代码...

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd-HH-mm"];

NSDate *nowDate = [dateFormatter dateFromString:@"2010-01-01-15-00"];
NSDate *dueDate = [dateFormatter dateFromString:@"2010-01-02-14-00"];

NSLog(@"NSDate *nowDate = %@", nowDate);
NSLog(@"NSDate *dueDate = %@", dueDate);

NSCalendar *calendar = [NSCalendar currentCalendar];

NSDateComponents *differenceComponents = [calendar components:(NSDayCalendarUnit)
                                                     fromDate:nowDate
                                                       toDate:dueDate
                                                      options:0];

NSLog(@"Days between dates: %d", [differenceComponents day]);

...这是输出:

NSDate *nowDate = 2010-01-01 15:00:00 -0700
NSDate *dueDate = 2010-01-02 14:00:00 -0700
Days between dates: 0

如您所见,该方法返回不正确的结果。它应该返回 1 作为两天之间的天数。我在这里做错了什么?

编辑:我写了另一种方法。我还没有进行广泛的单元测试,但到目前为止它似乎工作:

+ (NSInteger)daysFromDate:(NSDate *)fromDate inTimeZone:(NSTimeZone *)fromTimeZone untilDate:(NSDate *)toDate inTimeZone:(NSTimeZone *)toTimeZone {

    NSCalendar *calendar = [NSCalendar currentCalendar];
    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;

    [calendar setTimeZone:fromTimeZone];
    NSDateComponents *fromDateComponents = [calendar components:unitFlags fromDate:fromDate];

    [calendar setTimeZone:toTimeZone];
    NSDateComponents *toDateComponents = [calendar components:unitFlags fromDate:toDate];

    [calendar setTimeZone:[NSTimeZone defaultTimeZone]];
    NSDate *adjustedFromDate = [calendar dateFromComponents:fromDateComponents];
    NSDate *adjustedToDate = [calendar dateFromComponents:toDateComponents];

    NSTimeInterval timeIntervalBetweenDates = [adjustedToDate timeIntervalSinceDate:adjustedFromDate];
    NSInteger daysBetweenDates = (NSInteger)(timeIntervalBetweenDates / (60.0 * 60.0 * 24.0));

    NSDateComponents *midnightBeforeFromDateComponents = [[NSDateComponents alloc] init];
    [midnightBeforeFromDateComponents setYear:[fromDateComponents year]];
    [midnightBeforeFromDateComponents setMonth:[fromDateComponents month]];
    [midnightBeforeFromDateComponents setDay:[fromDateComponents day]];

    NSDate *midnightBeforeFromDate = [calendar dateFromComponents:midnightBeforeFromDateComponents];
    [midnightBeforeFromDateComponents release];

    NSDate *midnightAfterFromDate = [[NSDate alloc] initWithTimeInterval:(60.0 * 60.0 * 24.0)
                                                               sinceDate:midnightBeforeFromDate];

    NSTimeInterval timeIntervalBetweenToDateAndMidnightBeforeFromDate = [adjustedToDate timeIntervalSinceDate:midnightBeforeFromDate];
    NSTimeInterval timeIntervalBetweenToDateAndMidnightAfterFromDate = [adjustedToDate timeIntervalSinceDate:midnightAfterFromDate];

    if (timeIntervalBetweenToDateAndMidnightBeforeFromDate < 0.0) {

        // toDate is before the midnight before fromDate

        timeIntervalBetweenToDateAndMidnightBeforeFromDate -= daysBetweenDates * 60.0 * 60.0 * 24.0;

        if (timeIntervalBetweenToDateAndMidnightBeforeFromDate < 0.0)
            daysBetweenDates -= 1;
    }
    else if (timeIntervalBetweenToDateAndMidnightAfterFromDate >= 0.0) {

        // toDate is after the midnight after fromDate

        timeIntervalBetweenToDateAndMidnightAfterFromDate -= daysBetweenDates * 60.0 * 60.0 * 24.0;

        if (timeIntervalBetweenToDateAndMidnightAfterFromDate >= 0.0)
            daysBetweenDates += 1;
    }

    [midnightAfterFromDate release];

    return daysBetweenDates;
}
4

7 回答 7

3

从文档中components:fromDate:toDate:options:

如果没有要求足够小的单位来保持差异的完整精度,则结果是有损的。

由于差异小于一整天,因此它正确返回 0 天的结果。

于 2010-06-19T11:55:01.207 回答
2

您可以使用此相关问题的非常好的答案轻松解决

于 2010-10-22T14:55:17.320 回答
2

如果您只关心明天或昨天与特定日期的对比,那么您可以节省大量工作,只需测试日期是否仅相隔一个日历日。

为此,请比较日期以找出哪个较早和哪个较晚(如果它们比较相等,则使用该结果退出),然后测试较早日期后 1 天是否产生具有相同年、月和日期作为较晚的日期。


如果您确实想确切知道从一个日期到另一个日期有多少个日历日:

  1. 向日历发送components:fromDate:消息以获取第一个日期的年月日。
  2. 与 #1 相同,但为第二个日期。
  3. 如果这两个日期在同一年和同一月,则从另一个减去一个月中的一天并传递给abs(参见abs(3))以获取绝对值。
  4. 如果它们不在同一年份和月份,则测试它们是否在相邻月份(例如,2010 年 12 月至 2011 年 1 月,或 2010 年 6 月至 2010 年 7 月)。如果是,则将较早日期的月份中的天数(您可以通过向日历发送rangeOfUnit:inUnit:forDate:消息,分别传递NSDayCalendarUnitNSMonthCalendarUnit获得)添加到较晚日期的月份,然后将该结果与较早日期进行比较日期的日期。

    例如,在比较 2010-12-31 和 2011-01-01 时,您将首先确定这些是在相邻月份,然后将 31(2010-12 年的天数)添加到 1(2011 年的月份-01-01),然后从该总和中减去 31(2010 年 12 月 31 日的日期)。由于差值为 1,因此较早日期是较晚日期的前一天。

    在比较 2010-12-30和 2011-01-02时,您会确定它们在相邻的月份,然后将31(2010-12 中的天数)添加到 2(2011-01-02 的日期)然后从该总和中减去 30(2010 年 12 月 30 日的月份)。33 减去 30 是 3,因此这些日期相隔三个日历日。


无论哪种方式,我强烈建议至少为此代码编写单元测试。我发现日期处理代码是最有可能出现细微错误的代码之一,例如一年两次。

于 2010-06-19T12:26:25.047 回答
2

您可能会尝试的一件事是rangeOfUnit:将开始和结束日期的小时、分钟和秒归零。

NSCalendar *calendar = [NSCalendar currentCalendar];
NSCalendarUnit range = NSDayCalendarUnit;
NSDateComponents *comps = [[NSDateComponents alloc] init];
NSDate *start = [NSDate date];
NSDate *end;

[comps setDay:1];
[calendar rangeOfUnit:range startDate:&start interval:nil forDate:start];

end = [calendar dateByAddingComponents:comps toDate:start options:0];

在这个例子中,开始是2010-06-19 00:00:00 -0400,结束是2010-06-20 00:00:00 -0400。我想这会更好地使用 NSCalendar 的比较方法,尽管我自己没有测试过。

于 2010-06-19T15:30:17.953 回答
1

我正在使用这段代码,它运行良好:

- (NSInteger)daysToDate:(NSDate*)date
{
    if(date == nil) {
        return NSNotFound;
    }
    NSUInteger otherDay = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay   inUnit:NSCalendarUnitEra forDate:date];
    NSUInteger currentDay = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:self];
    return (otherDay-currentDay);
}
于 2015-01-14T10:28:06.563 回答
0

这是我过去使用的函数,它在 NSDate 的一个类别中定义

- (int) daysToDate:(NSDate*) endDate
{
    //dates needed to be reset to represent only yyyy-mm-dd to get correct number of days between two days.
    NSDateFormatter *temp = [[NSDateFormatter alloc] init];
    [temp setDateFormat:@"yyyy-MM-dd"];
    NSDate *stDt = [temp dateFromString:[temp stringFromDate:self]];
    NSDate *endDt =  [temp dateFromString:[temp stringFromDate:endDate]];
    [temp release]; 
    unsigned int unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;
    NSCalendar *gregorian = [[NSCalendar alloc]
                             initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *comps = [gregorian components:unitFlags fromDate:stDt  toDate:endDt  options:0];
    int days = [comps day];
    [gregorian release];
    return days;
}
于 2010-06-29T15:49:24.277 回答
0
 -(NSInteger)daysBetweenTwoDates:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime

{

NSDate *fromDate;
NSDate *toDate;

NSCalendar *calendar = [NSCalendar currentCalendar];

[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];

}

于 2012-12-19T04:26:27.883 回答