【问题标题】:How to determine if an NSDate is today?如何确定 NSDate 是否是今天?
【发布时间】:2011-01-20 19:52:46
【问题描述】:

如何检查NSDate 是否属于今天?

我曾经使用来自[aDate description] 的前 10 个字符来检查它。 [[aDate description] substringToIndex:10] 返回类似"YYYY-MM-DD" 的字符串,所以我将该字符串与[[[NSDate date] description] substringToIndex:10] 返回的字符串进行了比较。

是否有更快速和/或更简洁的检查方式?

谢谢。

【问题讨论】:

  • 我相信我的方法(在我写这篇文章时)是唯一一个实际引用 Apple 文档并按照他们在其中的建议来确定两天是否相同的方法。虽然其他方法可能有效,但我相信我的方法是最适合未来的。 stackoverflow.com/a/17641925/901641
  • @ArtOfWarfare:我不同意。您的代码仍然需要处理时间间隔,但苹果提供的方法已经将其排除在外——请参阅:stackoverflow.com/a/17642033/106435
  • NSDate "description" 方法以 UTC 格式显示日期,即格林威治标准时间,不修正夏令时。如果您在美国深夜或印度清晨,它不会根据您的日历显示“今天”。您需要一个 NSCalendar 对象来将 NSDate 转换为一天,并将当前日期转换为一天,然后进行比较。

标签: ios objective-c cocoa-touch nsdate nscalendar


【解决方案1】:

在 macOS 10.9+ 和 iOS 8+ 中,NSCalendar/Calendar 上有一个方法可以做到这一点!

- (BOOL)isDateInToday:(NSDate *)date 

所以你只需这样做

目标-C:

BOOL today = [[NSCalendar currentCalendar] isDateInToday:date];

斯威夫特 3:

let today = Calendar.current.isDateInToday(date)

【讨论】:

  • 使用 iOS 8 SDK 检查
  • 我被上面的评论弄糊涂了,这绝对是 iOS 8: - (BOOL)isDateInToday:(NSDate *)date NS_AVAILABLE(10_9, 8_0);
  • 是的,我就是这么说的。可能是之前的评论者对 iOS 7 SDK 感到困惑。它有一个不同的声明。
  • 对于“今天”,这是完美的答案 :)
  • 优秀的答案!
【解决方案2】:

您可以比较日期组件:

NSDateComponents *otherDay = [[NSCalendar currentCalendar] components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:aDate];
NSDateComponents *today = [[NSCalendar currentCalendar] components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:[NSDate date]];
if([today day] == [otherDay day] &&
   [today month] == [otherDay month] &&
   [today year] == [otherDay year] &&
   [today era] == [otherDay era]) {
    //do stuff
}

编辑:

我更喜欢 stefan 的方法,我认为它使 if 语句更简洁易懂:

NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:(NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]];
NSDate *today = [cal dateFromComponents:components];
components = [cal components:(NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:aDate];
NSDate *otherDate = [cal dateFromComponents:components];

if([today isEqualToDate:otherDate]) {
    //do stuff
}

克里斯,我采纳了你的建议。我必须查一下是什么时代,所以对于其他不知道的人来说,它区分了公元前和公元。这对大多数人来说可能是不必要的,但它很容易检查并增加了一些确定性,所以我将它包括在内。如果你追求速度,这可能不是一个好方法。


注意 就像关于 SO 的许多答案一样,7 年后这完全过时了。在 Swift 中,现在只需使用 .isDateInToday

【讨论】:

  • 谢谢。我认为这是对我的问题的更简洁和未来安全的解决方案。
  • 我认为您也需要包含时代组件。我认为这是两种方法之间的抛硬币; isEqualToDate 似乎更好,但它似乎有点浪费,并且需要大约相同数量的行来从组件重建日期。
  • 由于“otherDay”和“today”都只填写了这四个字段(这就是您要求的全部内容),您可以只使用 [today equalTo:otherDay],而无需在第二个字段中跳槽.
  • @Glenn 你是建议我在第一个比较 NSDateComponents 吗?我并没有真正考虑过,但这似乎也是一个好主意。
  • @DavidKanarek 挖掘旧帖子...我不建议直接比较日期组件,如果您遇到需要抵消其中一个组件的情况,您可能会得到类似月份 = = 3, day == 34。转换为日期将正确解释为 4 月 3 日,比较日期组件不会看到这与 month == 4, day == 3 相同。
【解决方案3】:

这是您问题的一个分支,但如果您想打印带有“今天”或“昨天”的 NSDate,请使用该功能

- (void)setDoesRelativeDateFormatting:(BOOL)b

对于 NSDateFormatter

【讨论】:

  • 聪明的主意。但我认为如果您进行本地化,查找字符串“Today”会中断。
【解决方案4】:

我会尝试将今天的日期标准化为午夜和第二个日期,标准化为午夜,然后比较它是否是相同的 NSDate。

an Apple example 开始,以下是您将今天的日期标准化为午夜的方法,对第二个日期执行相同操作并进行比较:

NSCalendar * gregorian = [[NSCalendar alloc]
                               initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents * components =
    [gregorian components:
                 (NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit)
                 fromDate:[NSDate date]];
NSDate * today = [gregorian dateFromComponents:components];

【讨论】:

  • 我认为你也需要包括那个时代。
  • 不应该通过将时间设置为 12 来标准化日期,即中午,而不是午夜?由于日期是格林威治标准时间,因此将其设置为正午可确保双向时区变化(并且它们仅双向上升到 12 小时)不会“跳转”到前一天或后一天。
【解决方案5】:

Catfish_Man 建议的 Swift 3 和 4 扩展

extension Date {

    func isToday() -> Bool {
        return Calendar.current.isDateInToday(self)
    }

}

【讨论】:

    【解决方案6】:

    无需纠结于组件、时代和其他东西。

    NSCalendar 提供了一种方法来获取现有日期的某个时间单位的开始。

    此代码将获取今天的开始日期和另一个日期并进行比较。如果计算结果为 NSOrderedSame,则两个日期都在同一天 - 所以今天。

    NSDate *today = nil;
    NSDate *beginningOfOtherDate = nil;
    
    NSDate *now = [NSDate date];
    [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&today interval:NULL forDate:now];
    [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&beginningOfOtherDate interval:NULL forDate:beginningOfOtherDate];
    
    if([today compare:beginningOfOtherDate] == NSOrderedSame) {
        //otherDate is a date in the current day
    }
    

    【讨论】:

      【解决方案7】:
      extension NSDate {
        func isToday() -> Bool {
          let cal = NSCalendar.currentCalendar()
          var components = cal.components([.Era, .Year, .Month, .Day], fromDate:NSDate())
          let today = cal.dateFromComponents(components)!
      
          components = cal.components([.Era, .Year, .Month, .Day], fromDate:self)
          let otherDate = cal.dateFromComponents(components)!
      
          return today.isEqualToDate(otherDate)
      }
      

      在 Swift 2.0 上为我工作

      【讨论】:

        【解决方案8】:

        Swift 版本的最佳答案:

        let cal = NSCalendar.currentCalendar()
        var components = cal.components([.Era, .Year, .Month, .Day], fromDate:NSDate())
        let today = cal.dateFromComponents(components)!
        
        components = cal.components([.Era, .Year, .Month, .Day], fromDate:aDate);
        let otherDate = cal.dateFromComponents(components)!
        
        if(today.isEqualToDate(otherDate)) {
            //do stuff
        }
        

        【讨论】:

          【解决方案9】:

          请参阅 Apple 的文档条目“执行日历计算”[link]

          该页面上的清单 13 表明,要确定几天之间的午夜数,您可以使用:

          - (NSInteger)midnightsFromDate:(NSDate *)startDate toDate:(NSDate *)endDate
          {
              NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
              NSInteger startDay = [calendar ordinalityOfUnit:NSDayCalendarUnit
                                                       inUnit:NSEraCalendarUnit
                                                      forDate:startDate];
              NSInteger endDay = [calendar ordinalityOfUnit:NSDayCalendarUnit
                                                     inUnit:NSEraCalendarUnit
                                                    forDate:endDate];
              return endDay - startDay;
          }
          

          然后您可以通过使用该方法并查看它是否返回 0 来确定两天是否相同。

          【讨论】:

          • 请记住,一个时代确实从格林威治标准时间 00:00 开始!如果日期之一(或两者)不在 GMT 时区,此方法可能无法正常工作。
          【解决方案10】:

          您还可以检查您拥有的日期和当前日期之间的时间间隔:

          [myDate timeIntervalSinceNow]
          

          这将为您提供 myDate 和当前日期/时间之间的时间间隔(以秒为单位)。

          Link.

          编辑:大家注意:我很清楚 [myDate timeIntervalSinceNow] 并不能明确地确定 myDate 是否是今天。

          我将保留此答案,以便如果有人正在寻找类似的东西并且 [myDate timeIntervalSinceNow] 很有用,他们可能会在这里找到它。

          【讨论】:

          • 你说的是对的,但间隔并不能真正帮助检查日期是否是今天。
          • 请记住,它会告诉您从现在起是否在 24 小时内,但如果日期相同则不会。一秒的差异可以是不同的日期,86399 秒的差异可以是同一个日期。
          • @大卫:是的。没有考虑到这一点,因为我在回答问题时正在做其他 3 件事,并且在我加载页面时没有发布您的答案。
          • 这根本不能回答问题。你应该删除它。
          • 这个答案不过是噪音。它既不试图回答这个问题,也没有必要参考timeIntervalSinceNow,因为它在许多其他帖子中都有介绍。同时处理日期比较和检查秒数会导致容易出错的除法 86400。
          【解决方案11】:

          基于最佳答案的 Swift 扩展:

          extension NSDate {
              func isToday() -> Bool {
                  let cal = NSCalendar.currentCalendar()
                  if cal.respondsToSelector("isDateInToday:") {
                      return cal.isDateInToday(self)
                  }
                  var components = cal.components((.CalendarUnitEra | .CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay), fromDate:NSDate())
                  let today = cal.dateFromComponents(components)!
          
                  components = cal.components((.CalendarUnitEra | .CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay), fromDate:self);
                  let otherDate = cal.dateFromComponents(components)!
                  return today.isEqualToDate(otherDate)
              }
          }
          

          【讨论】:

            【解决方案12】:

            如果您有很多这样的日期比较,那么对calendar:components:fromDate 的调用就会开始占用大量时间。根据我所做的一些分析,它们似乎相当昂贵。

            假设您正在尝试从某个日期数组(例如 NSArray *datesToCompare)中确定哪些日期与某个给定日期(例如 NSDate *baseDate)是同一天,那么您可以使用类似以下的内容(部分改编自上面的答案):

            NSDate *baseDate = [NSDate date];
            
            NSArray *datesToCompare = [NSArray arrayWithObjects:[NSDate date], 
                                       [NSDate dateWithTimeIntervalSinceNow:100],
                                       [NSDate dateWithTimeIntervalSinceNow:1000],
                                       [NSDate dateWithTimeIntervalSinceNow:-10000],
                                       [NSDate dateWithTimeIntervalSinceNow:100000],
                                       [NSDate dateWithTimeIntervalSinceNow:1000000],
                                       [NSDate dateWithTimeIntervalSinceNow:50],
                                       nil];
            
            // determine the NSDate for midnight of the base date:
            NSCalendar* calendar = [NSCalendar currentCalendar];
            NSDateComponents* comps = [calendar components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) 
                                                   fromDate:baseDate];
            NSDate* theMidnightHour = [calendar dateFromComponents:comps];
            
            // set up a localized date formatter so we can see the answers are right!
            NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
            [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
            [dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
            
            // determine which dates in an array are on the same day as the base date:
            for (NSDate *date in datesToCompare) {
                NSTimeInterval interval = [date timeIntervalSinceDate:theMidnightHour];
                if (interval >= 0 && interval < 60*60*24) {
                    NSLog(@"%@ is on the same day as %@", [dateFormatter stringFromDate:date], [dateFormatter stringFromDate:baseDate]);
                }
                else {
                    NSLog(@"%@ is NOT on the same day as %@", [dateFormatter stringFromDate:date], [dateFormatter stringFromDate:baseDate]);
                }
            }
            

            输出:

            Nov 23, 2011 1:32:00 PM is on the same day as Nov 23, 2011 1:32:00 PM
            Nov 23, 2011 1:33:40 PM is on the same day as Nov 23, 2011 1:32:00 PM
            Nov 23, 2011 1:48:40 PM is on the same day as Nov 23, 2011 1:32:00 PM
            Nov 23, 2011 10:45:20 AM is on the same day as Nov 23, 2011 1:32:00 PM
            Nov 24, 2011 5:18:40 PM is NOT on the same day as Nov 23, 2011 1:32:00 PM
            Dec 5, 2011 3:18:40 AM is NOT on the same day as Nov 23, 2011 1:32:00 PM
            Nov 23, 2011 1:32:50 PM is on the same day as Nov 23, 2011 1:32:00 PM
            

            【讨论】:

            • 谢谢。公认的方式对我们的应用来说 CPU 密集型太高了,所以我们需要对此进行优化。
            • 此代码将在夏令时失败。每个使用臭名昭著的数字 86,400 的代码的人都应该为此感到头疼。
            【解决方案13】:

            有比上述许多答案更简单的方法!

            NSDate *date = ... // The date you wish to test
            NSCalendar *calendar = [NSCalendar currentCalendar];
            
            if([calendar isDateInToday:date]) {
                //do stuff
            }
            

            【讨论】:

            • 和 Catfish_Man 的回答一样!
            【解决方案14】:

            这可能会被重新设计为 NSDate 类别,但我使用了:

            // Seconds per day (24h * 60m * 60s)
            #define kSecondsPerDay 86400.0f
            
            + (BOOL) dateIsToday:(NSDate*)dateToCheck
            {
                // Split today into components
                NSCalendar* gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
                NSDateComponents* comps = [gregorian components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit) 
                                                    fromDate:[NSDate date]];
            
                // Set to this morning 00:00:00
                [comps setHour:0];
                [comps setMinute:0];
                [comps setSecond:0];
                NSDate* theMidnightHour = [gregorian dateFromComponents:comps];
                [gregorian release];
            
                // Get time difference (in seconds) between date and then
                NSTimeInterval diff = [dateToCheck timeIntervalSinceDate:theMidnightHour];
                return ( diff>=0.0f && diff<kSecondsPerDay );
            }
            

            (但是,在原始问题中比较两个日期字符串几乎感觉“更干净”..)

            【讨论】:

            • 为什么在创建comps 时将小时、分钟和秒数立即设置为零?另外,我认为你需要包括那个时代。
            • 请注意,虽然这几乎适用于所有情况,但也有闰秒之类的东西。我不知道 Apple 的 NSDate 类是否或如何处理它们,但有可能这不起作用。这更像是“你知道吗”而不是批评你的方法,因为它们非常罕见(历史上 24 次)。
            • 由于夏令时,不是 24 小时而是 23 或 25 的日期怎么办?
            【解决方案15】:

            对于 iOS7 及更早版本:

            //this is now => need that for the current date
            NSDate * now = [NSDate date];
            
            NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
            [calendar setTimeZone:[NSTimeZone systemTimeZone]];
            
            NSDateComponents * components = [calendar components:( NSYearCalendarUnit|    NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate: now];
            
            [components setMinute:0];
            [components setHour:0];
            [components setSecond:0];
            
            //this is Today's Midnight
            NSDate *todaysMidnight = [calendar dateFromComponents: components];
            
            
            
            //now timeIntervals since Midnight => in seconds
            NSTimeInterval todayTimeInterval = [now timeIntervalSinceDate: todaysMidnight];
            
            //now timeIntervals since OtherDate => in seconds
            NSTimeInterval otherDateTimeInterval = [now timeIntervalSinceDate: otherDate];
            
            if(otherDateTimeInterval > todayTimeInterval) //otherDate is not in today
            {
                if((otherDateTimeInterval - todayTimeInterval) <= 86400) //86400 == a day total seconds
                {
                    @"yesterday";
                }
                else
                {
                    @"earlier";
                }
            }
            else
            {
                @"today";
            }
            
            
            now = nil;
            calendar = nil;
            components = nil;
            todaysMidnight = nil;
            
            NSLog("Thank you :-)");
            

            【讨论】:

              【解决方案16】:

              查看我们的 Erica Sadun 的 NSDate extension。使用非常简单。在这里就好了:

              http://github.com/erica/NSDate-Extensions

              这篇文章中已经有:https://stackoverflow.com/a/4052798/362310

              【讨论】:

                【解决方案17】:

                正确且安全的解决方案,无需强制解包,适用于 Swift 2.2 和 iOS 8 之前:

                func isToday() -> Bool {
                    let calendar = NSCalendar.currentCalendar()
                    if #available(iOS 8.0, *) {
                        return calendar.isDateInToday(self)
                    }
                
                    let todayComponents = calendar.components([.Era, .Year, .Month, .Day], fromDate:NSDate())
                    let dayComponents = calendar.components([.Era, .Year, .Month, .Day], fromDate:self)
                
                    guard let today = calendar.dateFromComponents(todayComponents),
                        day = calendar.dateFromComponents(dayComponents) else {
                        return false
                    }
                
                    return today.compare(day) == .OrderedSame
                }
                

                【讨论】:

                  【解决方案18】:

                  这是我基于已接受答案的 2 美分答案,但也支持较新的 API。注意:我使用公历,因为大多数时间戳都是 GMT,但可以根据需要更改您的时间戳

                  func isDateToday(date: NSDate) -> Bool {
                      let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
                      if calendar.respondsToSelector("isDateInToday:") {
                          return calendar.isDateInToday(date)
                      }
                      let dateComponents = NSCalendarUnit.CalendarUnitYear | NSCalendarUnit.CalendarUnitMonth | NSCalendarUnit.CalendarUnitDay
                      let today = calendar.dateFromComponents(calendar.components(dateComponents, fromDate: NSDate()))!
                      let dateToCompare = calendar.dateFromComponents(calendar.components(dateComponents, fromDate: date))!
                  
                      return dateToCompare == today
                  }
                  

                  【讨论】:

                    【解决方案19】:

                    我的解决方案是通过除法计算自 1970 年以来经过了多少天,并比较整数部分

                    #define kOneDay (60*60*24)
                    - (BOOL)isToday {
                      NSInteger offset = [[NSTimeZone defaultTimeZone] secondsFromGMT];
                    
                      NSInteger days =[self timeIntervalSince1970] + offset;
                      NSInteger currentDays = [[NSDate date] timeIntervalSince1970] + offset;
                      return (days / kOneDay == currentDays / kOneDay);
                    }
                    

                    【讨论】:

                    • 这实际上不是一个糟糕的解决方案。不知道为什么它如此被否决。在校正 GMT 之后,使用整数除法将截断余数并仅比较 unix 长时间的“天”值。坦率地说,我认为这是最干净的解决方案。
                    • @chaiguy:这是一个糟糕的解决方案,因为它假设每天都是 24 小时——事实并非如此。由于夏令时,一天可以是 23、24 或 25 小时。
                    • 嗯,没错。然而,每年的平均时间正好是 24 小时,所以在我看来,在最坏的情况下它只会减少一小时。也许可以明确检查。
                    • @chaiguy:没有。只是永远不要使用包含 60*60*24 的代码。或者忍受你的头痛。
                    • “secondsFromGMT”在世界上大多数地方每年都会发生两次突然变化。使用 NSCalendar 方法更安全。
                    【解决方案20】:
                    NSDate *dateOne = yourDate;
                    NSDate *dateTwo = [NSDate date];  
                    
                    switch ([dateOne compare:dateTwo])
                    {  
                        case NSOrderedAscending:  
                            NSLog(@”NSOrderedAscending”);  
                            break;  
                    
                        case NSOrderedSame: 
                            NSLog(@”NSOrderedSame”);  
                            break;
                    
                        case NSOrderedDescending:  
                            NSLog(@”NSOrderedDescending”);  
                            break;  
                    }  
                    

                    【讨论】:

                    • 这并不能告诉你两个 NSDate 是否相同,只是哪个更早,即使它们是同一天。
                    猜你喜欢
                    • 2015-06-12
                    • 2011-03-11
                    • 1970-01-01
                    • 2011-05-02
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-06-10
                    相关资源
                    最近更新 更多