Сравнение двух NSDates и игнорирование компонента времени


что является наиболее эффективным/рекомендуемый способ сравнения двух NSDates? Я хотел бы видеть, находятся ли обе даты в один и тот же день, независимо от времени, и начали писать некоторый код, который использует метод timeIntervalSinceDate: в классе NSDate и получает целое число этого значения, разделенное на количество секунд в день. Это кажется долго запыхавшимся, и я чувствую, что упускаю что-то очевидное.

код, который я пытаюсь исправить это:

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

где key и todaysDate являются объектами NSDate и todaysdate создает с помощью:

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

в отношении

Дэйв

16 71

16 ответов:

вы устанавливаете время в дату 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

затем вы можете сравнить значения даты. Смотрите обзор в справочной документации.

Я удивлен, что никакие другие ответы не имеют этой опции для получения даты "начала дня" для объектов:

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

, который устанавливает date1 и date2 к началу своих дней. Если они равны, то они находятся в один и тот же день.

или такой вариант:

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

, который устанавливает day1 и day2 к несколько произвольным значениям, которые можно сравнить. Если они равны, то они находятся в один и тот же день.

есть новый метод, который был введен в NSCalendar с iOS 8, что делает это намного проще.

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

вы устанавливаете степень детализации на единицу(ы), которые имеют значение. Это игнорирует все другие единицы и ограничивает сравнение с выбранными.

для iOS8 и более поздних версий проверка наличия двух дат в один и тот же день так же проста, как:

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

посмотреть документация

это стенография всех ответов:

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
    }

я использовал подход 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;
}

я использую этот маленький метод util:

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

(вы, очевидно, должны иметь Ивар называется calendar_ содержащий NSCalendar.)

используя это, легко проверить, является ли дата сегодня такой:

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

([NSDate date] возвращает текущую дату и время.)

это, конечно, очень похоже на то, что Гриша говорит. Недостатком этого подхода является то, что он имеет тенденцию создавать много временных NSDate объекты. Если вы собираетесь обрабатывать много из дат я бы рекомендовал использовать какой-то другой метод, например, напрямую сравнивать компоненты или работать с объекты NSDates.

ответ проще, чем все делают это, чтобы быть. 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;
}

Если выше метод возвращает ноль, две даты в один день.

начиная с 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 различия

для разработчиков кодирования в Swift 3

if(NSCalendar.current.isDate(selectedDate, inSameDayAs: NSDate() as Date)){
     // Do something
}
int interval = (int)[firstTime timeIntervalSinceDate:secondTime]/(60*60*24);
if (interval!=0){
   //not the same day;
}

мое решение было два преобразования с 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  
    }

С 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​Ascending или ordered​Descending.

код игровой площадки ниже показывает горячий, чтобы использовать его:

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

возвращает разницу между двумя датами.

код игровой площадки ниже показывает горячий, чтобы использовать его:

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
}

документация в отношении 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;
    }
}

это особенно уродливая кошка для кожи, но вот еще один способ сделать это. Я не говорю, что это элегантно, но это, вероятно, так близко, как вы можете получить с поддержкой даты/времени в iOS.

bool isToday = [[NSDateFormatter localizedStringFromDate:date dateStyle:NSDateFormatterFullStyle timeStyle:NSDateFormatterNoStyle] isEqualToString:[NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterFullStyle timeStyle:NSDateFormatterNoStyle]];
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 день