比较两个NSDate并忽略时间分量


72

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

我要修复的代码是:

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

其中key和todaysDate是NSDate对象,而todaysDate使用以下方法创建:

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

问候

戴夫


美丽的问题+1
真挚的2014年

Answers:


73

在进行比较之前,您将日期中的时间设置为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

然后,您可以比较日期值。请参阅参考文档中概述


感谢Gregory的快速回复,这真的很有帮助。我想我的下一个问题是,因为这是一个紧密循环的一部分,您认为使用timeIntervalSinceDate:方法和一些整数算术比创建更多对象,进行必要的计算然后清除工作更有效吗?再次感谢您的帮助,Dave
Magic Bullet Dave

13
我的建议是:正确的执行是金;然后进行配置。如果此特定循环确实是瓶颈,请进行优化
Gregory Pakosz,2009年

3
由于只提取年,月和日单位,因此小时,分钟和秒会自动设置为0。因此,您不必自己明确地进行操作。
戴夫·德隆

1
我尝试使用此方法无效。我总是有不等于00:00的时间吗?
iosMentalist 2012年

1
我必须使用[calendar setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@“ GMT”]]配置日历对象;否则,比较将无法正常进行。
弗拉基米尔·斯拉维克

83

令我惊讶的是,没有其他答案可以使用此选项来获取对象的“开始日期”日期:

[[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];

其中规定day1,并day2以可以比较有点武断值。如果它们相等,则它们在同一天。


小FYI,NSDayCalendarUnit已弃用,取而代之的是NSCalendarUnitDay
Dwayne Forde 2015年

20

iOS 8向NSCalendar引入了一种新方法,使此操作变得更加容易。

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

您可以将粒度设置为重要的单位。这将忽略所有其他单位,并将比较范围限制为所选单位。


那是一个很酷的方法。(投票)可惜它仅在iOS 8中可用。我想如果您仍然需要支持iOS <8,则可以使用我为iOS <8和compareDate:toDate:toUnitGranularity:iOS> = 8列出的代码编写一种方法。然后,一旦放弃对<8版的OS版本的支持,就可以将实现压缩到对的调用中compareDate:toDate:toUnitGranularity:
Duncan C

1
当然,最终方法将是differenceBetweenDate:andDate:usingUnit:,该方法将返回负,零或正整数值,这些整数值表示请求的uint中的日期之间的差额。
Duncan C

可以使用此方法,但是unit参数的解释错误!它只期望应该使用的最小单位。Apple文档:对于给定的日期,必须与最小的所有单位以及所有较大的单位均相等
Matoz

用法示例:BOOL CompetitionStarted = [[NSCalendar currentCalendar] compareDate:self.competition.startDate toDate:[NSDate date] toUnitGranularity:NSCalendarUnitDay]!= NSOrderedDescending;
莱昂,

16

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

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

查看文件


这节省了我很多时间。谢谢!
维纳亚克

10

这是所有答案的简写:

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
    }

1
此代码无效。如果您在[NSDate date]中添加23小时,它将显示两个日期都在同一天,这在大多数情况下显然是错误的。
Gottfried 2014年

@ Gottfired这将忽略OP要求的时间部分。不确定增加23小时意味着什么。您是说要更改日期?
Bms270 2014年

@ Bms270他的意思是改变一天。如果您将今天@ 3PM与明天@ 9AM进行比较,即使第二个日期显然是另一天,您的代码也会返回0天的变化量。
Christian Schnorr 2014年

@Jenox您是否尝试过此代码?您的示例应该返回:今天>明天,无论现在是什么时候。它只是忽略了小时数,就好像您在比较凌晨12点和凌晨12点。OP表示“与时间无关”。
Bms270 2014年

日历方法的全部要点是它们很聪明。呼叫都映射到日期的日历的时区,忽略所有较小的单位,如果你比较今天凌晨2点与凌晨1点,明天,他们不是在同一天,所以结果是1
gnasher729

6

我使用了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;
}

6

我使用这个小方法:

-(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]返回当前日期和时间。)

当然,这与格雷戈里的建议非常相似。这种方法的缺点是它倾向于创建许多临时NSDate对象。如果您要处理大量日期,建议您使用其他方法,例如直接比较组件,或使用NSDateComponents对象代替NSDates


3

答案比每个人都说得简单。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;
}

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


3

从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到iOS 8.0 API的区别中


3

使用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
}

谢谢!!我正在寻找的东西非常有帮助
Vishal Shelake

2

对于使用Swift 3进行编码的开发人员

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

1
int interval = (int)[firstTime timeIntervalSinceDate:secondTime]/(60*60*24);
if (interval!=0){
   //not the same day;
}

1

我的解决方案是使用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  
    }

0

关于文档的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;
    }
}

0

这是只丑陋的猫,但是这是另一种方法。我并不是说它很优雅,但是它与iOS中的日期/时间支持所能达到的效果差不多。

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

0
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天的差异

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.