Сравнение двух NSDates и игнорирование компонента времени - PullRequest
71 голосов
/ 06 декабря 2009

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

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

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

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

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

Привет

Дейв

Ответы [ 16 ]

83 голосов
/ 07 декабря 2009

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

[[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 в несколько произвольных значений, которые можно сравнивать. Если они равны, они в один и тот же день.

72 голосов
/ 06 декабря 2009

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

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

19 голосов
/ 14 февраля 2015

В NSCalendar появился новый метод с iOS 8, который делает это намного проще.

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

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

14 голосов
/ 30 ноября 2015

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

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

См. документацию

10 голосов
/ 13 октября 2012

Это сокращение всех ответов:

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
    }
6 голосов
/ 23 апреля 2012

Я использовал подход Дункана С, я исправил некоторые ошибки, которые он сделал

-(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 голосов
/ 06 декабря 2009

Я использую этот маленький утилитарный метод:

-(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 объектов. Если вы собираетесь обрабатывать много дат, я бы рекомендовал использовать какой-либо другой метод, например, сравнивать компоненты напрямую или работать с NSDateComponents объектами вместо NSDates.

3 голосов
/ 31 марта 2015

Начиная с iOS 8.0, вы можете использовать:

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

Если результат, например, NSOrderedDescending, otherDate до [NSDate date] в днях.

Я не вижу этот метод в документации NSCalendar, но он есть в Различия API от iOS 7.1 до iOS 8.0

3 голосов
/ 27 ноября 2011

Ответ проще, чем все думают. 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;
}

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

2 голосов
/ 31 марта 2017

В 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
}
...