【问题标题】:iPhone - get number of days between two datesiPhone - 获取两个日期之间的天数
【发布时间】:2011-03-05 18:12:03
【问题描述】:

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

【问题讨论】:

    标签: iphone cocoa cocoa-touch nsdate nsdatecomponents


    【解决方案1】:

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

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

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

    【讨论】:

    • 好吧,换个说法,我如何检查我的两个日期之间有多少个午夜?
    【解决方案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 年 1 月 1 日时,您将首先确定这些是在相邻月份,然后将 31(2010 年 12 月的天数)加到 1(天数) 2011 年 1 月 1 日的月份),然后从该总和中减去 31(2010 年 12 月 31 日的月份)。由于差为 1,因此较早的日期比较晚的日期早一天。

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


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

    【讨论】:

    • 您的建议听起来很复杂。我写了另一种方法,看看我对原帖的编辑。我不确定它是否在所有情况下都能正常工作,但对我来说到目前为止。
    • Tim:我的两个建议都没有你在问题中写的那么复杂——尤其是第一个建议,应该不超过一打陈述。
    【解决方案3】:

    您可以尝试的一件事是使用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 的比较方法一起使用会更好,尽管我自己没有测试过。

    【讨论】:

      【解决方案4】:

      您可以使用这个related question 的非常好的答案轻松解决问题

      【讨论】:

        【解决方案5】:

        我正在使用这段代码,效果很好:

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

        【讨论】:

          【解决方案6】:

          这是我过去使用的功能 它在 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;
          }
          

          【讨论】:

            【解决方案7】:
             -(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];
            

            }

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-05-10
              相关资源
              最近更新 更多