【问题标题】:Cocoa-Touch: How do I see if two NSDates are in the same day?Cocoa-Touch:如何查看两个 NSDate 是否在同一天?
【发布时间】:2010-12-06 09:38:53
【问题描述】:

我需要知道两个 NSDate 实例是否都来自同一天。

有没有比获取 NSDateComponents 并比较日/月/年更简单/更好的方法?

【问题讨论】:

  • 您可能需要重新评估已接受的答案。情况发生了变化。

标签: ios objective-c macos cocoa-touch


【解决方案1】:

对我来说,NSDateComponents 听起来是最好的选择。另一种尝试的策略是将其免费桥接到 CFDate,然后使用 CFDateGetAbsoluteTime 并进行减法以获得两个日期之间的时间量。但是,您必须做一些额外的数学运算才能确定时差是否使日期在同一天。

【讨论】:

  • 是的,但是在处理时间方面你必须考虑很多事情,以至于你最终编写的代码不仅仅是使用 NSDateComponents。
  • 对;我建议使用 NSDateComponents 的替代方案,但仍然相信这是最好的选择。如果这成为代码的性能关键部分,则替代方案可能会稍微快一些,但在证明有必要之前我会避免这种优化。
  • NSDate 有这个很好的方法:timeIntervalSinceDate: 因此你甚至不需要核心基础。
  • NSDateComponents 是最好的选择,因为这样会自动处理时区和夏令时等问题。
  • 仅时间间隔不足以确定两个日期是否为同一天。 11:59 PM 和 12:01 AM 只有 2 分钟的间隔,但不同的日子。您可以使用时间间隔快速排除 > 24 小时的间隔,然后使用 NSDateComponents 进行完整检查。
【解决方案2】:

(注意:查看 Joe 的答案,了解在 iOS 8+ 上执行此操作的好方法)

我只是使用日期格式化程序:

NSDateFormatter *dateComparisonFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateComparisonFormatter setDateFormat:@"yyyy-MM-dd"];

if( [[dateComparisonFormatter stringFromDate:firstDate] isEqualToString:[dateComparisonFormatter stringFromDate:secondDate]] ) {
    …
}

HTH。

【讨论】:

  • 这比使用 NSDateComponents 更糟糕,因为格式化程序必须首先将日期分解为组件,然后创建两个字符串,然后比较字符串。
  • 您对“更好”的衡量标准是什么?我认为这比类似的 NSDateComponents 代码“更容易”,因为它更简洁且易于维护。
  • 我一直在使用 dateComponents 方法,但这更聪明。代码越短越好,如果性能有问题,我可以缓存 NSDateFormatter。
  • 为我工作,又好又聪明。将我从所有 NSCalendar 开销中解救出来。
【解决方案3】:

我使用 NSDateComponents 去除时间方面,然后进行比较。比如:

if ([[self beginningOfDay:date1] isEqualToDate:[self beginningOfDay:date2]]) 
{
...
}

- (NSDate *)beginningOfDay:(NSDate *)date {
    NSCalendar *calendar = [NSCalendar currentCalendar];

    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit;
    NSDateComponents *comp = [calendar components:unitFlags fromDate:date];

    return [calendar dateFromComponents:comp];
}

【讨论】:

  • 每个日期使用两次转换(组件:fromDate 和 dateFromComponents)。我只使用组件:fromDate,从每个组件中提取月/日/年并比较这 3 个整数以查看它们是否匹配。
  • 您不应该使用 beginOfDay,而不是所有的日子都从上午 12 点开始。您应该使用中午(中午 12 点)进行不关心的时间日期比较
【解决方案4】:

如果您的目标是 iOS 8(和 OS X 10.9)或更高版本,那么 Joe's answer 是一个更好的解决方案,为此使用 NSCalendar 中的新方法:

-[NSCalendar isDate:inSameDayAsDate:]

对于 iOS 7 或更早版本:NSDateComponents 是我的首选。像这样的东西怎么样:

- (BOOL)isSameDayWithDate1:(NSDate*)date1 date2:(NSDate*)date2 {
    NSCalendar* calendar = [NSCalendar currentCalendar];

    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit;
    NSDateComponents* comp1 = [calendar components:unitFlags fromDate:date1];
    NSDateComponents* comp2 = [calendar components:unitFlags fromDate:date2];

    return [comp1 day]   == [comp2 day] &&
           [comp1 month] == [comp2 month] &&
           [comp1 year]  == [comp2 year];
}

【讨论】:

  • 签名应该是:-(BOOL)isSameDayWithDate1:(NSDate *)date1 date2:(NSDate *)date2 {
  • - (BOOL)date:(NSDate *)date1 isTheSameDayAsDate:(NSDate *)date2 读起来更好:)
  • 或另一种方式:-(BOOL)isDate:(NSDate*)date1 sameDayAsDate:(NSDate*)date2
  • 返回 [comp1 isEqual:comp2];似乎给出了相同的结果。有区别吗?为什么选择手动比较日、月和年?
  • @Alex:isEqual 函数没有记录在 NSDateComponents 类参考中,因此它不确定它在所有情况下都会做正确的事情。它会忽略无效的组件吗?可能,但没有记录。这段代码只是稍微长了一点并且给出了正确的结果,即使在调用 components:fromDate: 时单位已经包含其他字段(如小时或分钟)
【解决方案5】:

我喜欢 progrmr 的解决方案,但我会更进一步,创建一个提供此方法的 NSDate 类别。它将使您的代码更具可读性,并且您无需将该方法复制并粘贴到每个可能需要它的新类中——只需导入头文件即可。

NSDate+SameDay.h

@interface NSDate (SameDay)
- (BOOL)isSameDayAsDate:(NSDate*)otherDate;
@end

NSDate+SameDay.m

#import "NSDate+SameDay.h"

@implementation NSDate (SameDay)

- (BOOL)isSameDayAsDate:(NSDate*)otherDate {

    // From progrmr's answer...
    NSCalendar* calendar = [NSCalendar currentCalendar];

    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit;
    NSDateComponents* comp1 = [calendar components:unitFlags fromDate:self];
    NSDateComponents* comp2 = [calendar components:unitFlags fromDate:otherDate];

    return [comp1 day]   == [comp2 day] &&
           [comp1 month] == [comp2 month] &&
           [comp1 year]  == [comp2 year];
}

@end

然后,在你的类中导入NSDate+SameDay.h 后,你可以像这样使用它:

if ([myFirstDate isSameDayAsDate:mySecondDate]) {
    // The two dates are on the same day...
}

【讨论】:

  • 我也认为这是更好的方法。
【解决方案6】:

我创建自己的实用程序类。

ZYUtility.h

@interface ZYUtility : NSObject
+ (NSDate *)yesterday;
+ (NSDate *)tomorrow;
+ (NSDate *)endOfDay:(NSDate *)date;
+ (NSDate *)beginningOfDay:(NSDate *)date;
@end

ZYUtility.m

+ (NSDate *)yesterday;
{
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *componets = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
                                              fromDate:[NSDate date]];
    componets.day -= 1;
    componets.hour = 24;
    componets.minute = 0;
    componets.second = 0;

    return [calendar dateFromComponents:componets];
}

+ (NSDate *)tomorrow;
{
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *componets = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
                                              fromDate:[NSDate date]];
    componets.day += 1;
    componets.hour = 0;
    componets.minute = 0;
    componets.second = 0;

    return [calendar dateFromComponents:componets];
}

+ (NSDate *)beginningOfDay:(NSDate *)date {
    NSCalendar *calendar = [NSCalendar currentCalendar];

    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit;
    NSDateComponents *comp = [calendar components:unitFlags fromDate:date];

    return [calendar dateFromComponents:comp];
}

+ (NSDate *)endOfDay:(NSDate *)date {
    NSCalendar *calendar = [NSCalendar currentCalendar];

    NSDateComponents *components = [NSDateComponents new];
    components.day = 1;

    NSDate *theDate = [calendar dateByAddingComponents:components
                                                toDate:[ZYUtility beginningOfDay:date]
                                               options:0];

    theDate = [theDate dateByAddingTimeInterval:-1];

    return theDate;
}

【讨论】:

    【解决方案7】:

    以下将测试两个日期是否代表给定时代中的同一天(这在许多情况下就足够了):

    - (BOOL)isDate:(NSDate *)date1 sameDayAsDate:(NSDate *)date2 {
        NSCalendar *calendar = [NSCalendar currentCalendar];
        int diff = [calendar ordinalityOfUnit:NSDayCalendarUnit 
                                       inUnit:NSEraCalendarUnit 
                                      forDate:date1] -
                   [calendar ordinalityOfUnit:NSDayCalendarUnit 
                                       inUnit:NSEraCalendarUnit 
                                      forDate:date2];
        return 0 == diff;
    }
    

    【讨论】:

      【解决方案8】:

      如果您使用的是 iOS 8,则可以使用 -isDate:inSameDayAsDate:

      来自NSCalendar.h

      /*
          This API compares the Days of the given dates, reporting them equal if they are in the same Day.
      */
      - (BOOL)isDate:(NSDate *)date1 inSameDayAsDate:(NSDate *)date2 NS_AVAILABLE(10_9, 8_0);
      

      请注意,由于某种原因,这并未出现在 Apple 开发者网站上的官方文档中。但它绝对是公共 API 的一部分。

      【讨论】:

        【解决方案9】:

        有没有比获取 NSDateComponents 并比较日/月/年更简单/更好的方法?

        是的,有更简单的方法

        -[NSCalendar rangeForUnit:startDate:interval:forDate:] 这个方法可以很容易地将日期设置为单位(天、小时、月、年……)的开头,并获得该单位的长度(间隔)。

        作为一个类别,它可以像这样使用

        @interface NSDate (Comparison)
        -(BOOL) isSameDay:(NSDate *)rhs;
        @end
        
        @implementation NSDate (Comparison)
        
        -(BOOL)isSameDay:(NSDate *)rhs
        {
            NSDate *lhs = self;
            NSDate *lhsStart;
            NSDate *rhsStart;
        
            NSCalendar *cal = [NSCalendar currentCalendar];
            [cal rangeOfUnit:NSCalendarUnitDay startDate:&lhsStart interval:NULL forDate:lhs];
            [cal rangeOfUnit:NSCalendarUnitDay startDate:&rhsStart interval:NULL forDate:rhs];
            return [lhsStart compare:rhsStart] == NSOrderedSame;
        }
        
        @end
        

        此类别应适用于所有 iOS 版本和 Mac OS X 10.5+。

        对于 iOS 8+ 和 Mac OS X 10.9+,您可以使用 NSCalendars

        - (BOOL)isDate:(NSDate *)date1 equalToDate:(NSDate *)date2 toUnitGranularity:(NSCalendarUnit)unit;
        

        【讨论】:

          【解决方案10】:

          在斯威夫特中:

          NSCalendar.currentCalendar().isDate(yourDate, equalToDate: yourOtherDate, toUnitGranularity: .Day)
          

          如果他们在同一天,它将返回true。

          【讨论】:

          • 适用于 iOS 8.0
          【解决方案11】:
          NSCalendar.currentCalendar().isDateInToday(yourNSDate)
          

          适用于 iOS 8.0 及更高版本以及 OS X v10.9 及更高版本。

          【讨论】:

            【解决方案12】:

            在 Swift 3.0、iOS8+ 中

            if NSCalendar.current.isDate(Date(), equalTo: date, toGranularity: .day) {
            
            }
            

            if NSCalendar.current.isDateInToday(date) {
            
            }
            

            【讨论】:

            • 还有Calendar.current.isDate(_ date1: Date, inSameDayAs date2: Date)
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-01-31
            • 1970-01-01
            • 1970-01-01
            • 2010-11-07
            • 1970-01-01
            相关资源
            最近更新 更多