【问题标题】:Comparing two NSDates and ignoring the time component比较两个 NSDate 并忽略时间分量
【发布时间】:2010-12-23 17:12:27
【问题描述】:

比较两个 NSDate 的最有效/推荐的方法是什么?我希望能够查看两个日期是否在同一天,无论时间如何,并且已经开始编写一些代码,这些代码使用 NSDate 类中的 timeIntervalSinceDate: 方法并获取该值的整数除以秒数一天内。这似乎很啰嗦,我觉得我错过了一些明显的东西。

我要修复的代码是:

if (!([key compare:todaysDate] == NSOrderedDescending))
{
    todaysDateSection = [eventSectionsArray count] - 1;
}

其中 key 和 todaysDate 是 NSDate 对象,而 todaysDate 正在使用:

NSDate *todaysDate = [[NSDate alloc] init];

问候

戴夫

【问题讨论】:

  • 美丽问题+1

标签: iphone objective-c nsdate


【解决方案1】:

令我感到惊讶的是,没有其他答案具有此选项来获取对象的“一天的开始”日期:

[[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay startDate:&date1 interval:NULL forDate:date1];
[[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay startDate:&date2 interval:NULL forDate:date2];

date1date2 设置为各自日子的开始。如果它们相等,则它们在同一天。

或者这个选项:

NSUInteger day1 = [[NSCalendar currentCalendar] ordinalityOfUnit:NSDayCalendarUnit inUnit: forDate:date1];
NSUInteger day2 = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:date2];

day1day2 设置为可以比较的任意值。如果它们相等,则它们在同一天。

【讨论】:

  • 小仅供参考,NSDayCalendarUnit 已被弃用并替换为 NSCalendarUnitDay
【解决方案2】:

使用 Swift 3,根据您的需要,您可以选择以下两种模式之一来解决您的问题。


#1。使用compare(_:to:toGranularity:)方法

Calendar 有一个名为compare(_:​to:​to​Granularity:​) 的方法。 compare(_:​to:​to​Granularity:​) 有以下声明:

func compare(_ date1: Date, to date2: Date, toGranularity component: Calendar.Component) -> ComparisonResult

将给定日期与给定组件进行比较,如果它们在给定组件和所有更大的组件中相同,则报告它们ordered​Same,否则为ordered​Ascendingordered​Descending

下面的 Playground 代码显示使用它很热门:

import Foundation

let calendar = Calendar.current
let date1 = Date() // "Mar 31, 2017, 2:01 PM"
let date2 = calendar.date(byAdding: .day, value: -1, to: date1)! // "Mar 30, 2017, 2:01 PM"
let date3 = calendar.date(byAdding: .hour, value: 1, to: date1)! // "Mar 31, 2017, 3:01 PM"

/* Compare date1 and date2 */
do {
    let comparisonResult = calendar.compare(date1, to: date2, toGranularity: .day)
    switch comparisonResult {
    case ComparisonResult.orderedSame:
        print("Same day")
    default:
        print("Not the same day")
    }
    // Prints: "Not the same day"
}

/* Compare date1 and date3 */
do {
    let comparisonResult = calendar.compare(date1, to: date3, toGranularity: .day)
    if case ComparisonResult.orderedSame = comparisonResult {
        print("Same day")
    } else {
        print("Not the same day")
    }
    // Prints: "Same day"
}

#2。使用dateComponents(_:from:to:)

Calendar 有一个名为dateComponents(_:from:to:) 的方法。 dateComponents(_:from:to:) 有以下声明:

func dateComponents(_ components: Set<Calendar.Component>, from start: Date, to end: Date) -> DateComponents

返回两个日期之间的差。

下面的 Playground 代码显示使用它很热门:

import Foundation

let calendar = Calendar.current
let date1 = Date() // "Mar 31, 2017, 2:01 PM"
let date2 = calendar.date(byAdding: .day, value: -1, to: date1)! // "Mar 30, 2017, 2:01 PM"
let date3 = calendar.date(byAdding: .hour, value: 1, to: date1)! // "Mar 31, 2017, 3:01 PM"

/* Compare date1 and date2 */
do {
    let dateComponents = calendar.dateComponents([.day], from: date1, to: date2)
    switch dateComponents.day {
    case let value? where value < 0:
        print("date2 is before date1")
    case let value? where value > 0:
        print("date2 is after date1")
    case let value? where value == 0:
        print("date2 equals date1")
    default:
        print("Could not compare dates")
    }
    // Prints: date2 is before date1
}

/* Compare date1 and date3 */
do {
    let dateComponents = calendar.dateComponents([.day], from: date1, to: date3)
    switch dateComponents.day {
    case let value? where value < 0:
        print("date2 is before date1")
    case let value? where value > 0:
        print("date2 is after date1")
    case let value? where value == 0:
        print("date2 equals date1")
    default:
        print("Could not compare dates")
    }
    // Prints: date2 equals date1
}

【讨论】:

  • 谢谢!!我正在寻找的东西非常有帮助
【解决方案3】:

适用于使用 Swift 3 编码的开发人员

if(NSCalendar.current.isDate(selectedDate, inSameDayAs: NSDate() as Date)){
     // Do something
}

【讨论】:

    【解决方案4】:

    在进行比较之前,您将日期中的时间设置为 00:00:00:

    unsigned int flags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;
    NSCalendar* calendar = [NSCalendar currentCalendar];
    
    NSDateComponents* components = [calendar components:flags fromDate:date];
    
    NSDate* dateOnly = [calendar dateFromComponents:components];
    
    // ... necessary cleanup
    

    然后您可以比较日期值。请参阅overview in reference documentation

    【讨论】:

    • 感谢 Gregory 的快速回复,这真的很有帮助。我想我的下一个问题是,由于这是一个紧密循环的一部分,你认为使用 timeIntervalSinceDate: 方法和一些整数算术是否更有效,而不是创建更多对象,进行必要的计算然后清理?再次感谢您的帮助,戴夫
    • 我的建议是:正确的实施是金;然后简介。如果这个特定的循环真的是一个瓶颈,那么优化
    • 由于您只提取年、月和日单位,因此时、分和秒自动设置为 0。因此,您不必自己显式执行。
    • 我试过用这个方法没用。我的时间总是不等于 00:00?
    • 我必须使用 [calendar setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]]; 配置日历对象;否则无法正常进行比较。
    【解决方案5】:

    我的解决方案是使用 NSDateFormatter 进行两次转换:

        NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
        [dateFormat setDateFormat:@"yyyyMMdd"];
        [dateFormat setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
    
        NSDate *today = [NSDate dateWithTimeIntervalSinceNow:0];
        NSString *todayString=[dateFormat stringFromDate:today];
        NSDate *todayWithoutHour=[dateFormat dateFromString:todayString];
    
        if ([today compare:exprDate] == NSOrderedDescending)
        {
           //do  
        }
    

    【讨论】:

      【解决方案6】:

      对于 iOS8 及更高版本,检查两个日期是否在同一天很简单:

      [[NSCalendar currentCalendar] isDate:date1 inSameDayAsDate:date2]
      

      documentation

      【讨论】:

      • 这为我节省了很多时间。谢谢!
      【解决方案7】:

      从 iOS 8.0 开始,您可以使用:

      NSCalendar *calendar = [NSCalendar currentCalendar];
      NSComparisonResult dateComparison = [calendar compareDate:[NSDate date] toDate:otherNSDate toUnitGranularity:NSCalendarUnitDay];
      

      如果结果是例如NSOrderedDescending,otherDate 在 [NSDate date] 之前的天数。

      我在 NSCalendar 文档中没有看到这种方法,但它在 iOS 7.1 to iOS 8.0 API Differences

      【讨论】:

        【解决方案8】:
        NSUInteger unit = NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit;
        NSDateComponents *comp = [cal components:unit
                                        fromDate:nowDate
                                          toDate:setDate
                                         options:0];
        
        NSString *dMonth;
        dMonth = [NSString stringWithFormat:@"%02ld",comp.month];
        NSString *dDay;
        dDay = [NSString stringWithFormat:@"%02ld",comp.day + (comp.hour > 0 ? 1 : 0)];
        

        比较小时以及修复 1 天的差异

        【讨论】:

          【解决方案9】:

          在 iOS 8 中,NSCalendar 引入了一种新方法,使这变得更加容易。

          - (NSComparisonResult)compareDate:(NSDate *)date1 toDate:(NSDate *)date2 toUnitGranularity:(NSCalendarUnit)unit NS_AVAILABLE(10_9, 8_0);
          

          您将粒度设置为重要的单位。这会忽略所有其他单位并限制与所选单位的比较。

          【讨论】:

          • 这是一个非常酷的方法。 (已投票)可惜它只在 iOS 8 中可用。我想如果你仍然需要支持 iOS =8 上使用compareDate:toDate:toUnitGranularity:。然后,一旦您放弃对操作系统版本 compareDate:toDate:toUnitGranularity: 的调用。
          • 当然,最终的方法是differenceBetweenDate:andDate:usingUnit:,它会返回负、零或正整数值,表示请求的 uint 中日期之间的差异量。
          • 这个方法可以用但是对unit参数的解释有误!它只期望应该使用的最小单位。 Apple 文档:最小单位必须与所有较大单位一样,才能将给定日期视为相同
          • 使用示例:BOOL CompetitionStarted = [[NSCalendar currentCalendar] compareDate:self.competition.startDate toDate:[NSDate date] toUnitGranularity:NSCalendarUnitDay] != NSOrderedDescending;
          【解决方案10】:
          int interval = (int)[firstTime timeIntervalSinceDate:secondTime]/(60*60*24);
          if (interval!=0){
             //not the same day;
          }
          

          【讨论】:

            【解决方案11】:

            我用这个小工具方法:

            -(NSDate*)normalizedDateWithDate:(NSDate*)date
            {
               NSDateComponents* components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
                                                          fromDate: date];
               return [calendar_ dateFromComponents:components]; // NB calendar_ must be initialized
            }
            

            (您显然需要一个名为 calendar_ 的 ivar,其中包含一个 NSCalendar。)

            使用这个,很容易检查日期是否是今天这样:

            [[self normalizeDate:aDate] isEqualToDate:[self normalizeDate:[NSDate date]]];
            

            [NSDate date] 返回当前日期和时间。)

            这当然与 Gregory 的建议非常相似。这种方法的缺点是它往往会创建大量的临时NSDate 对象。如果您要处理大量日期,我建议您使用其他方法,例如直接比较组件,或者使用NSDateComponents 对象而不是NSDates

            【讨论】:

              【解决方案12】:

              这是一只特别难看的猫,但这是另一种方法。我并不是说它很优雅,但它可能与 iOS 中的日期/时间支持一样接近。

              bool isToday = [[NSDateFormatter localizedStringFromDate:date dateStyle:NSDateFormatterFullStyle timeStyle:NSDateFormatterNoStyle] isEqualToString:[NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterFullStyle timeStyle:NSDateFormatterNoStyle]];
              

              【讨论】:

                【解决方案13】:

                这是所有答案的简写:

                NSInteger interval = [[[NSCalendar currentCalendar] components: NSDayCalendarUnit
                                                                                  fromDate: date1
                                                                                    toDate: date2
                                                                                   options: 0] day];
                    if(interval<0){
                       //date1<date2
                    }else if (interval>0){
                       //date2<date1
                    }else{
                       //date1=date2
                    }
                

                【讨论】:

                • 此代码不起作用。如果您将 23 小时添加到 [NSDate date] 它将显示两个日期在同一天,这在大多数情况下显然是错误的。
                • @ Gottfired 这将忽略 OP 请求的时间部分。不知道添加 23 小时是什么意思。您的意思是更改日期?
                • @Bms270 他的意思是改变一天。如果您将今天 @ 3PM 与明天 @ 9AM 进行比较,您的代码会返回 0 天的增量,即使第二个日期显然是另一天。
                • @Jenox 你试过这个代码吗?你的例子应该返回:今天>明天,不管它是什么时间。它只是忽略了时间,就好像您将 12am 与 12am 进行比较一样。 OP 说“与时间无关”。
                • 日历方法的全部要点在于它们很聪明。该调用将两个日期映射到日历的时区,忽略所有较小的单位,如果将今天凌晨 2 点与明天凌晨 1 点进行比较,它们不在同一天,因此结果为 1。
                【解决方案14】:

                关于NSDate 的文档表明compare:isEqual: 方法都将执行基本比较并对结果进行排序,尽管它们仍然会考虑时间。

                管理任务的最简单方法可能是创建一个新的isToday 方法,效果如下:

                - (bool)isToday:(NSDate *)otherDate
                {
                    currentTime = [however current time is retrieved]; // Pardon the bit of pseudo-code
                
                    if (currentTime < [otherDate timeIntervalSinceNow])
                    {
                        return YES;
                    }
                    else
                    {
                        return NO;
                    }
                }
                

                【讨论】:

                  【解决方案15】:

                  答案比大家想象的要简单。 NSCalendar 有一个方法

                  components:fromDate:toDate:options
                  

                  该方法让您可以使用所需的任何单位计算两个日期之间的差异。

                  所以写一个这样的方法:

                  -(NSInteger) daysBetweenDate: (NSDate *firstDate) andDate: (NSDate *secondDate)
                  {
                    NSCalendar *currentCalendar = [NSCalendar currentCalendar];
                    NSDateComponents components* = [currentCalendar components: NSDayCalendarUnit
                      fromDate: firstDate 
                      toDate: secondDate
                      options: 0];
                  
                    NSInteger days = [components days];
                    return days;
                  }
                  

                  如果上述方法返回零,则两个日期在同一天。

                  【讨论】:

                    【解决方案16】:

                    我使用了 Duncan C 方法,我修正了他犯的一些错误

                    -(NSInteger) daysBetweenDate:(NSDate *)firstDate andDate:(NSDate *)secondDate { 
                    
                        NSCalendar *currentCalendar = [NSCalendar currentCalendar];
                        NSDateComponents *components = [currentCalendar components: NSDayCalendarUnit fromDate: firstDate toDate: secondDate options: 0];
                    
                        NSInteger days = [components day];
                    
                        return days;
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 2012-02-18
                      • 1970-01-01
                      • 2012-05-08
                      • 2011-10-25
                      • 2018-07-28
                      • 2011-09-09
                      • 2011-12-02
                      • 1970-01-01
                      • 2017-04-23
                      相关资源
                      最近更新 更多