iPhone - 获取两个日期之间的天数

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;
}
6个回答

3

components:fromDate:toDate:options:的文档中可以得知:

如果请求的最小单位无法容纳差异的完整精度,则结果会有损失。

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


换句话说,我该如何检查在我的两个日期之间有多少个午夜? - Tim

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的比较方法会更好,尽管我自己没有测试过。

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,因此这些日期相隔三个日历日。
无论哪种方式,我强烈建议至少为此代码编写单元测试。我发现处理日期的代码是最容易出现微妙错误的代码之一,只有两次一年才会显现。

你提出的建议听起来相当复杂。我写了另一种方法,请看我对原帖的编辑。我不确定它在所有情况下是否都能正常工作,但至少对我来说是有效的。 - Tim
Tim: 我的两个建议比你在问题中写的要简单得多,特别是第一个建议,不应超过十几个语句。 - Peter Hosey

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);
}

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

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

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接