Comparando dois NSDates e ignorando o componente de tempo

Qual é a maneira mais eficiente / recomendada de comparar duas NSDates? Eu gostaria de poder ver se ambas as datas estão no mesmo dia, independentemente do tempo e comecei a escrever algum código que usa o método timeIntervalSinceDate: dentro da class NSDate e obtém o inteiro deste valor dividido pelo número de segundos Em um dia. Isso parece longo e sinto como se estivesse faltando algo óbvio.

O código que estou tentando corrigir é:

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

onde key e todaysDate são objects NSDate e o todaysDate está criando usando:

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

Saudações

Dave

Você define a hora na data para 00:00:00 antes de fazer a comparação:

 unsigned int flags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay; NSCalendar* calendar = [NSCalendar currentCalendar]; NSDateComponents* components = [calendar components:flags fromDate:date]; NSDate* dateOnly = [calendar dateFromComponents:components]; // ... necessary cleanup 

Então você pode comparar os valores de data. Veja a visão geral na documentação de referência .

Estou surpreso que nenhuma outra resposta tenha essa opção para obter a data do “início do dia” para os objects:

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

Que define date1 e date2 para o início de seus respectivos dias. Se eles são iguais, eles estão no mesmo dia.

Ou esta opção:

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

Que define day1 e day2 para valores um pouco arbitrários que podem ser comparados. Se eles são iguais, eles estão no mesmo dia.

Há um novo método que foi introduzido no NSCalendar com o iOS 8, o que facilita muito isso.

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

Você define a granularidade para as unidades que importam. Isso desconsidera todas as outras unidades e limita a comparação com as selecionadas.

Para iOS8 e posterior, verificar se duas datas ocorrem no mesmo dia é tão simples quanto:

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

Veja a documentação

Esta é uma abreviação de todas as respostas:

 NSInteger interval = [[[NSCalendar currentCalendar] components: NSDayCalendarUnit fromDate: date1 toDate: date2 options: 0] day]; if(interval<0){ //date10){ //date2 

Eu usei a abordagem Duncan C, eu corrigi alguns erros que ele fez

 -(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; } 

Eu uso esse pequeno método util:

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

(Você obviamente precisa ter um ivar chamado calendar_ contendo um NSCalendar .)

Usando isso, é fácil verificar se uma data é hoje assim:

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

( [NSDate date] retorna a data e a hora atuais.)

Isto é claro, muito semelhante ao que Gregory sugere. A desvantagem dessa abordagem é que ela tende a criar muitos objects NSDate temporários. Se você for processar muitas datas, eu recomendaria usar outro método, como comparar os componentes diretamente ou trabalhar com objects NSDateComponents vez de NSDates .

A resposta é mais simples do que todos fazem. O NSCalendar tem um método

 components:fromDate:toDate:options 

Esse método permite calcular a diferença entre duas datas usando as unidades desejadas.

Então, escreva um método como este:

 -(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; } 

Se o método acima retornar zero, as duas datas serão no mesmo dia.

Do iOS 8.0 em diante, você pode usar:

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

Se o resultado for, por exemplo, NSOrderedDescending, otherDate é antes de [NSDate date] em termos de dias.

Eu não vejo este método na documentação do NSCalendar, mas é no iOS 7.1 para iOS 8.0 Diferenças API

Para desenvolvedores que codificam no 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; } 

minha solução foi duas conversões com o 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 } 

Com o Swift 3, de acordo com as suas necessidades, você pode escolher um dos dois padrões a seguir para resolver seu problema.


# 1. Usando o método compare(_:to:toGranularity:)

Calendar tem um método chamado compare(_:​to:​to​Granularity:​) . compare(_:​to:​to​Granularity:​) tem a seguinte declaração:

 func compare(_ date1: Date, to date2: Date, toGranularity component: Calendar.Component) -> ComparisonResult 

Compara as datas dadas até o componente dado, relatando-as ordered​Same se forem as mesmas no componente fornecido e em todos os componentes maiores, caso contrário, serão ordered​Ascending ou ordered​Descending .

O código Playground abaixo mostra quente para usá-lo:

 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. Usando dateComponents(_:from:to:)

Calendar tem um método chamado dateComponents(_:from:to:) . dateComponents(_:from:to:) tem a seguinte declaração:

 func dateComponents(_ components: Set, from start: Date, to end: Date) -> DateComponents 

Retorna a diferença entre duas datas.

O código Playground abaixo mostra quente para usá-lo:

 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 } 

A documentação referente ao NSDate indica que os methods compare: e isEqual: executam uma comparação básica e ordenam os resultados, embora ainda faturem no tempo.

Provavelmente, a maneira mais simples de gerenciar a tarefa seria criar um novo método isToday para o efeito do seguinte:

 - (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; } } 

Este é um gato particularmente feio para a pele, mas aqui está outra maneira de fazê-lo. Não digo que seja elegante, mas provavelmente é o mais próximo possível do suporte de data / hora 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)]; 

compare a hora também para corrigir a diferença de um dia