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

时间:2010-06-19 11:45:17

标签: iphone cocoa cocoa-touch nsdate nsdatecomponents

我正在为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;
}

7 个答案:

答案 0 :(得分:3)

来自components:fromDate:toDate:options:的文档:

  

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

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

答案 1 :(得分:2)

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

要做到这一点,比较日期,找出哪个更早,哪个更晚(如果他们比较相等,挽救该结果),然后测试早期日期后1天是否产生同一年的日期,月份和日期作为更晚的日期。


如果您确实想知道从一个日期到另一个日期的确切日历天数:

  1. 发送日历components:fromDate:消息,以获取第一个日期的年,月和日。
  2. 与#1相同,但第二次约会。
  3. 如果两个日期是在相同的年份和月份,减去从其他的一个一天的一个月,并传递给abs(见abs(3))取绝对值。
  4. 如果他们不在同一年份和月份,请测试他们是否在相邻月份(例如,2010年12月至2011年1月,或2010年6月至2010年7月)。如果是这样,添加在较早日期的月日至day-(您可以通过发送日历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,所以这些日期相隔三个日历日。


  5. 无论哪种方式,我强烈建议至少为此代码编写单元测试。我发现日期处理代码是最容易产生微妙错误的代码之一,每年只会出现两次。

答案 2 :(得分: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的比较方法更好地工作,虽然我自己没有测试过。

答案 3 :(得分:2)

您可以使用对此related question

的非常好的答案轻松解决问题

答案 4 :(得分: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);
}

答案 5 :(得分: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;
}

答案 6 :(得分: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];

}