【问题标题】:Number of days between two NSDates [duplicate]两个 NSDate 之间的天数 [重复]
【发布时间】:2011-06-11 23:42:51
【问题描述】:

如何确定两个 NSDate 值之间的天数(同时考虑时间)?

NSDate 的值可以是 [NSDate date] 采用的任何形式。

具体来说,当用户在我的 iPhone 应用程序中进入非活动状态时,我会存储以下值:

exitDate = [NSDate date];

当他们打开应用程序备份时,我会得到当前时间:

NSDate *now = [NSDate date];

现在我想实现以下内容:

-(int)numberOfDaysBetweenStartDate:exitDate andEndDate:now

【问题讨论】:

    标签: ios objective-c cocoa-touch nsdate


    【解决方案1】:

    这是我找到的最佳解决方案。似乎使用 Apple 批准的方法来确定 NSDates 之间的任何数量的单位。

    - (NSInteger)daysBetween:(NSDate *)dt1 and:(NSDate *)dt2
    {
        NSUInteger unitFlags = NSCalendarUnitDay;
        NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
        NSDateComponents *components = [calendar components:unitFlags fromDate:dt1 toDate:dt2 options:0];
        return [components day] + 1;
    }
    

    例如如果你也想要几个月,那么你可以将 'NSMonthCalendarUnit' 包含为一个 unitFlag。

    为了感谢原始博主,我在这里找到了这个信息(尽管我在上面修复了一个小错误): http://cocoamatic.blogspot.com/2010/09/nsdate-number-of-days-between-two-dates.html?showComment=1306198273659#c6501446329564880344

    【讨论】:

    • 这是 Apple 文档中的方法。这应该被接受
    • 日历的声明在哪里?为什么不将其作为一个类别来实现?
    • return [components day]+1; 应该是 return ABS([components day])+1; 以防止错误的日期计数为负值
    • 它工作不正确。对于相同的日期和相差 1 天的日期,它给出了相同的结果。 components.day 在这种情况下为 0。
    • 我想要一个方法,它为过去的 toDate 返回负值,为未来的 toDate 返回正值,如果 toDate 与 fromDate 是同一天,则返回 0。如果我删除最后一行代码中的 +1,此方法会为我做到这一点。
    【解决方案2】:

    Swift 3.0 更新

    extension Date {
    
        func differenceInDaysWithDate(date: Date) -> Int {
            let calendar = Calendar.current
    
            let date1 = calendar.startOfDay(for: self)
            let date2 = calendar.startOfDay(for: date)
    
            let components = calendar.dateComponents([.day], from: date1, to: date2)
            return components.day ?? 0
        }
    }
    

    Swift 2.0 更新

    extension NSDate {
    
        func differenceInDaysWithDate(date: NSDate) -> Int {
            let calendar: NSCalendar = NSCalendar.currentCalendar()
    
            let date1 = calendar.startOfDayForDate(self)
            let date2 = calendar.startOfDayForDate(date)
    
            let components = calendar.components(.Day, fromDate: date1, toDate: date2, options: [])
            return components.day
        }
    
    }
    

    原始解决方案

    Swift 中的另一种解决方案。

    如果您的目的是获取两个日期之间的确切天数,您可以像这样解决这个问题:

    // Assuming that firstDate and secondDate are defined
    // ...
    
    var calendar: NSCalendar = NSCalendar.currentCalendar()
    
    // Replace the hour (time) of both dates with 00:00
    let date1 = calendar.startOfDayForDate(firstDate)
    let date2 = calendar.startOfDayForDate(secondDate)
    
    let flags = NSCalendarUnit.DayCalendarUnit
    let components = calendar.components(flags, fromDate: date1, toDate: date2, options: nil)
    
    components.day  // This will return the number of day(s) between dates
    

    【讨论】:

    • 比使用rangeOfUnit 更短更甜一些。但是(目前)需要注意的是,这需要 iOS 8。
    • 唯一对我有用的答案! +1 calendar.startOfDayForDate。谢谢。
    • 我认为您需要在日历上设置一个 TimeZone 否则将使用设备时区,这会影响结果。
    • @Leon 如果您计算日期之间的天数,我看不出时区会影响结果的任何原因。无论是哪个时区,它都会给出相同的结果。我没有深入思考,但这是我最初的想法。
    • @EminBuğraSaral 我在英国,在测试中,当某个日期从 22:59 到 23:00 发生变化时,我得到了不同的天数。 DateFormatter 已经设置为 UTC,所以这不是问题。当我更改Calendar 的时区时,我得到了正确答案。
    【解决方案3】:

    我用它作为 NSDate 类的分类方法

    // returns number of days (absolute value) from another date (as number of midnights beween these dates)
    - (int)daysFromDate:(NSDate *)pDate {
        NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
        NSInteger startDay=[calendar ordinalityOfUnit:NSCalendarUnitDay
                                               inUnit:NSCalendarUnitEra
                                              forDate:[NSDate date]];
        NSInteger endDay=[calendar ordinalityOfUnit:NSCalendarUnitDay
                                             inUnit:NSCalendarUnitEra
                                            forDate:pDate];
        return abs(endDay-startDay);
    }
    

    【讨论】:

    • 这是最佳且正确工作的解决方案。
    • 你为什么讨厌 currentCalendar?
    • 否决,因为此方法在某些情况下可能会返回不正确的差异,因为它不包括时区偏移
    • 同样被否决,因为此方法在某些情况下可能会返回不正确的差异,因为它不包括时区偏移
    【解决方案4】:

    这是我用来确定两个日期之间的日历天数的实现:

    + (NSInteger)daysBetweenDate:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime
    {
        NSDate *fromDate;
        NSDate *toDate;
    
        NSCalendar *calendar = [NSCalendar currentCalendar];
    
        [calendar rangeOfUnit:NSCalendarUnitDay startDate:&fromDate
            interval:NULL forDate:fromDateTime];
        [calendar rangeOfUnit:NSCalendarUnitDay startDate:&toDate
            interval:NULL forDate:toDateTime];
    
        NSDateComponents *difference = [calendar components:NSCalendarUnitDay
            fromDate:fromDate toDate:toDate options:0];
    
        return [difference day];
    }
    

    编辑:

    上面的解决方案很棒,下面是 Swift 版本作为 NSDate 的扩展:

    extension NSDate {
      func numberOfDaysUntilDateTime(toDateTime: NSDate, inTimeZone timeZone: NSTimeZone? = nil) -> Int {
        let calendar = NSCalendar.currentCalendar()
        if let timeZone = timeZone {
          calendar.timeZone = timeZone
        }
    
        var fromDate: NSDate?, toDate: NSDate?
    
        calendar.rangeOfUnit(.Day, startDate: &fromDate, interval: nil, forDate: self)
        calendar.rangeOfUnit(.Day, startDate: &toDate, interval: nil, forDate: toDateTime)
    
        let difference = calendar.components(.Day, fromDate: fromDate!, toDate: toDate!, options: [])
        return difference.day
      }
    }
    

    根据您的用例,您可能希望移除一些强制展开。

    上述解决方案也适用于当前时区以外的时区,非常适合显示世界各地信息的应用。

    【讨论】:

    • 你为什么不使用,NSDateComponents *difference = [日历组件:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];仅限。
    • @karim 如果您只使用该功能,则差异不会出现在“日历日”中。
    • @karim:只是提供一个例子,以澄清 João 所说的话:如果 fromDateTime = Feb 1st 11:00pm and toDateTime = Feb 2nd 01:00am 结果应该是 1(即使它只有 2小时,但这是另一个日期)。如果不去除日期的时间部分(通过调用 rangeOfUnit:... 完成),结果将为 0(因为 2h
    • Karim - 不错的解决方案,这就是我想要的。
    • 我添加了“[calendar setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];”在“rangeOfUnit”方法之前,现在它对我来说可以正常工作(运行环境的本地时区设置为 +4:30 GMT)!
    【解决方案5】:

    为什么不只是:

    int days = [date1 timeIntervalSinceDate:date2]/24/60/60;
    

    【讨论】:

    • 因为这没有考虑夏令时或时区差异。您应该使用 NSCalendar 进行此数学运算。
    【解决方案6】:

    为什么要注意使用下面的 NSDate 方法:

    - (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate
    

    这将返回两个日期之间的秒数,您可以除以 86,400 得到天数!!

    【讨论】:

    • 这个答案已经给two years earlier,包括cmets回答“为什么不”
    • 抱歉,我错过了这个答案
    【解决方案7】:

    这是 Brian 的函数在 Swift 中的实现:

    class func daysBetweenThisDate(fromDateTime:NSDate, andThisDate toDateTime:NSDate)->Int?{
    
        var fromDate:NSDate? = nil
        var toDate:NSDate? = nil
    
        let calendar = NSCalendar.currentCalendar()
    
        calendar.rangeOfUnit(NSCalendarUnit.DayCalendarUnit, startDate: &fromDate, interval: nil, forDate: fromDateTime)
    
        calendar.rangeOfUnit(NSCalendarUnit.DayCalendarUnit, startDate: &toDate, interval: nil, forDate: toDateTime)
    
        if let from = fromDate {
    
            if let to = toDate {
    
                let difference = calendar.components(NSCalendarUnit.DayCalendarUnit, fromDate: from, toDate: to, options: NSCalendarOptions.allZeros)
    
                return difference.day
            }
        }
    
        return nil
    }
    

    【讨论】:

      【解决方案8】:

      我已经发布了一个开源类/库来做这件事。

      看看RelativeDateDescriptor,获取时差的方法如下……

      RelativeDateDescriptor *descriptor = [[RelativeDateDescriptor alloc] initWithPriorDateDescriptionFormat:@"%@ ago" postDateDescriptionFormat:@"in %@"];
      
      // date1: 1st January 2000, 00:00:00
      // date2: 6th January 2000, 00:00:00
      [descriptor describeDate:date2 relativeTo:date1]; // Returns '5 days ago'
      [descriptor describeDate:date1 relativeTo:date2]; // Returns 'in 5 days'
      

      【讨论】:

        【解决方案9】:

        只是为那些试图在 Swift 中访问此页面的人添加一个答案。方法几乎相同。

        private class func getDaysBetweenDates(startDate:NSDate, endDate:NSDate) -> NSInteger {
        
            var gregorian: NSCalendar = NSCalendar.currentCalendar();
            let flags = NSCalendarUnit.DayCalendarUnit
            let components = gregorian.components(flags, fromDate: startDate, toDate: endDate, options: nil)
        
            return components.day
        }
        

        这个答案是在here找到的,在以下方法的讨论部分:

        components(_:fromDate:toDate:options:)
        

        【讨论】:

          【解决方案10】:

          @布莱恩

          Brian 的回答虽然不错,但仅计算 24 小时块的天数差异,而不是日历天数差异。例如,12 月 24 日的 23:59 距离圣诞节只有 1 分钟的路程,对于许多应用程序来说,这仍然是一天。 Brian 的 daysBetween 函数将返回 0。

          借用 Brian 的原始实现和一天的开始/结束,我在我的程序中使用以下内容: (NSDate beginning of day and end of day)

          - (NSDate *)beginningOfDay:(NSDate *)date
          {
              NSCalendar *cal = [NSCalendar currentCalendar];
              NSDateComponents *components = [cal components:( NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
              [components setHour:0];
              [components setMinute:0];
              [components setSecond:0];
              return [cal dateFromComponents:components];
          }
          
          - (NSDate *)endOfDay:(NSDate *)date
          {
              NSCalendar *cal = [NSCalendar currentCalendar];
              NSDateComponents *components = [cal components:( NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
              [components setHour:23];
              [components setMinute:59];
              [components setSecond:59];
              return [cal dateFromComponents:components];
          }
          
          - (int)daysBetween:(NSDate *)date1 and:(NSDate *)date2 {
              NSDate *beginningOfDate1 = [self beginningOfDay:date1];
              NSDate *endOfDate1 = [self endOfDay:date1];
              NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
              NSDateComponents *beginningDayDiff = [calendar components:NSDayCalendarUnit fromDate:beginningOfDate1 toDate:date2 options:0];
              NSDateComponents *endDayDiff = [calendar components:NSDayCalendarUnit fromDate:endOfDate1 toDate:date2 options:0];
              if (beginningDayDiff.day > 0)
                  return beginningDayDiff.day;
              else if (endDayDiff.day < 0)
                  return endDayDiff.day;
              else {
                  return 0;
              }
          }
          

          【讨论】:

          • 实际上我的解决方案的全部目的是计算日历天数的差异 - 试试看:-)。时间戳的时间部分被丢弃,因此只返回天数差(因此 12 月 24 日 23:59:59 和 12 月 25 日被认为相隔 1 天)。
          【解决方案11】:

          另一种方法:

          NSDateFormatter* dayFmt = [[NSDateFormatter alloc] init];
          [dayFmt setTimeZone:<whatever time zone you want>];
          [dayFmt setDateFormat:@"g"];
          NSInteger firstDay = [[dayFmt stringFromDate:firstDate] integerValue];    
          NSInteger secondDay = [[dayFmt stringFromDate:secondDate] integerValue];
          NSInteger difference = secondDay - firstDay;
          

          timeIntervalSince... 方案相比具有可以考虑时区的优势,并且一天的间隔短或长几秒没有歧义。

          而且比 NSDateComponents 方法更紧凑,更不容易混淆。

          【讨论】:

            【解决方案12】:

            我需要两个日期之间的天数,包括开始日。 例如14-2-2012 和 16-2-2012 之间的天数将产生 3 的结果。

            + (NSInteger)daysBetween:(NSDate *)dt1 and:(NSDate *)dt2 {
                    NSUInteger unitFlags = NSDayCalendarUnit;
                    NSCalendar* calendar = [NSCalendar currentCalendar];
                    NSDateComponents *components = [calendar components:unitFlags fromDate:dt1 toDate:dt2 options:0];
                    NSInteger daysBetween = abs([components day]);
                return daysBetween+1;
            }
            

            请注意,您提供日期的顺序并不重要。它总是返回一个正数。

            【讨论】:

              【解决方案13】:

              有一个,不确定它是否正是你想要的,但它可以帮助你们中的一些人,(帮助我!!)

              我的目标是知道在两个日期之间(相差不到 24 小时)我是否有一个“加班”日+1:

              我做了以下事情(我承认有点过时)

              NSDate *startDate = ...
              NSDate *endDate = ...
              

              NSDate 已经被另一个 NSDateFormatter 格式化(这个只是为了这个目的:)

              NSDateFormatter *dayFormater = [[NSDateFormatter alloc]init];
              [dayFormater setDateFormat:@"dd"];
              
              int startDateDay = [[dayFormater stringFromDate:startDate]intValue];
              
              int endDateDay = [[dayFormater stringFromDate:dateOn]intValue];
              
              if (endDateDay > startDateDay) {
                  NSLog(@"day+1");
              } else {
                  NSLog(@"same day");
              }
              

              也许这样的东西已经存在,但没有找到

              提姆

              【讨论】:

                【解决方案14】:

                我找到的解决方案是:

                +(NSInteger)getDaysDifferenceBetween:(NSDate *)dateA and:(NSDate *)dateB {
                
                  if ([dateA isEqualToDate:dateB]) 
                    return 0;
                
                  NSCalendar * gregorian = 
                        [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
                
                
                
                  NSDate * dateToRound = [dateA earlierDate:dateB];
                  int flags = (NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit);
                  NSDateComponents * dateComponents = 
                         [gregorian components:flags fromDate:dateToRound];
                
                
                  NSDate * roundedDate = [gregorian dateFromComponents:dateComponents];
                
                  NSDate * otherDate = (dateToRound == dateA) ? dateB : dateA ;
                
                  NSInteger diff = abs([roundedDate timeIntervalSinceDate:otherDate]);
                
                  NSInteger daysDifference = floor(diff/(24 * 60 * 60));
                
                  return daysDifference;
                }
                

                在这里,我有效地将第一个日期从一天的开始四舍五入,然后按照上面乔纳森的建议计算差异......

                【讨论】:

                  【解决方案15】:
                  NSDate *lastDate = [NSDate date];
                  NSDate *todaysDate = [NSDate date];
                  NSTimeInterval lastDiff = [lastDate timeIntervalSinceNow];
                  NSTimeInterval todaysDiff = [todaysDate timeIntervalSinceNow];
                  NSTimeInterval dateDiff = lastDiff - todaysDiff;
                  

                  dateDiff 将是两个日期之间的秒数。只需除以一天中的秒数。

                  【讨论】:

                  • 为了清楚起见,您将除以 60*60*24,然后您需要确定部分天是否算作一天,5.9 是否等于 6 天?为您的应用找出合适的舍入方案。
                  • 你不应该在应用程序中做这种数学运算。这将随着时区转换(可能导致 23 或 25 小时的一天)、闰秒(在某些年份但不适用其他年份)以及任何数量的其他日历复杂情况下下降。
                  • NSCalendar 有执行日期数学的方法。 WWDC 2011 会议视频包括安全执行日期数学的会议。
                  • 错误答案。请参阅下面 Biosopher 的回答
                  • 这实在是大错特错,连Biosopher 的回答都是正确的。
                  【解决方案16】:

                  您是指日历日还是 24 小时周期?即周二晚上 9 点是周三早上 6 点的前一天,还是不到一天?

                  如果你指的是前者,它会有点复杂,你必须通过 NSCalendarNSDateComponent 进行操作,我不记得了。

                  如果您指的是后者,只需获取自参考日期以来的日期时间间隔,将其减去一个,然后除以 24 小时 (24 * 60 * 60) 即可得到大致间隔,不包括闰秒。

                  【讨论】:

                  • 排序。夏令时不会影响范围超过一个小时,因为它会随着时间来回翻转 - 它不会累积错误。
                  • 谁投了反对票——你愿意解释一下原因吗?
                  猜你喜欢
                  • 2012-10-25
                  • 2012-07-24
                  • 2016-06-06
                  • 1970-01-01
                  • 2012-07-31
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-12-31
                  • 1970-01-01
                  相关资源
                  最近更新 更多