Como comparar dois NSDates: Qual é o mais recente?

244

Estou tentando alcançar uma sincronização do dropBox e preciso comparar as datas de dois arquivos. Um está na minha conta dropBox e outro no meu iPhone.

Eu vim com o seguinte, mas obtenho resultados inesperados. Acho que estou fazendo algo fundamentalmente errado ao comparar as duas datas. Simplesmente usei os operadores>, mas acho que isso não é bom, pois estou comparando duas seqüências de caracteres NSDate. Aqui vamos nós:

NSLog(@"dB...lastModified: %@", dbObject.lastModifiedDate); 
NSLog(@"iP...lastModified: %@", [self getDateOfLocalFile:@"NoteBook.txt"]);

if ([dbObject lastModifiedDate] < [self getDateOfLocalFile:@"NoteBook.txt"]) {
    NSLog(@"...db is more up-to-date. Download in progress...");
    [self DBdownload:@"NoteBook.txt"];
    NSLog(@"Download complete.");
} else {
    NSLog(@"...iP is more up-to-date. Upload in progress...");
    [self DBupload:@"NoteBook.txt"];
    NSLog(@"Upload complete.");
}

Isso me deu a seguinte saída (aleatória e errada):

2011-05-11 14:20:54.413 NotePage[6918:207] dB...lastModified: 2011-05-11 13:18:25 +0000
2011-05-11 14:20:54.414 NotePage[6918:207] iP...lastModified: 2011-05-11 13:20:48 +0000
2011-05-11 14:20:54.415 NotePage[6918:207] ...db is more up-to-date.

ou este que está correto:

2011-05-11 14:20:25.097 NotePage[6903:207] dB...lastModified: 2011-05-11 13:18:25 +0000
2011-05-11 14:20:25.098 NotePage[6903:207] iP...lastModified: 2011-05-11 13:19:45 +0000
2011-05-11 14:20:25.099 NotePage[6903:207] ...iP is more up-to-date.
deixa pra lá
fonte
11
Duplicatas: 1 2 3 4 5 6 & c.
JSCs
1
@ JoshCaswell se é uma duplicata real, por que não mesclá-los? Você já fez isso antes ...
Dan Rosenstark
1
Somente moderadores de diamante podem executar uma mesclagem, @Yar.
JSCs

Respostas:

658

Vamos assumir duas datas:

NSDate *date1;
NSDate *date2;

A seguinte comparação dirá qual é mais cedo / mais tarde / mesmo:

if ([date1 compare:date2] == NSOrderedDescending) {
    NSLog(@"date1 is later than date2");
} else if ([date1 compare:date2] == NSOrderedAscending) {
    NSLog(@"date1 is earlier than date2");
} else {
    NSLog(@"dates are the same");
}

Consulte a documentação da classe NSDate para obter mais detalhes.

Nick Weaver
fonte
Encantador! É melhor do que mexer com [date1 previousDate: date2] etc ... Obrigado - por algum motivo, nunca pensei em usar o compare: antes.
SomaMan
11
Eu gosto de confiar no fato de que NSOrderedAscending <0 e NSOrderedDescending> 0. Isso facilita a comparação da leitura: [date1 compare: date2] <0 / * date1 <date2 * / e evita o erro (fácil de cometer) @albertamg apontou. ;-)
jpap 04/04
Bem - o método de comparação é tão suscetível a erros quanto erros de um por um. Portanto, você deve usar (NSDate *) laterDate: (NSDate *) anotherDate que retornará a data posterior de ambos. então basta comparar o resultado esperado e pronto! Não há brincadeira com "Waaait descendente / ascendente?!"
Masi 11/05
@jpap Isso também me atrapalhou; parece que a Apple quer que você pense no resultado como date1 -> date2ascendente / descendente (e, portanto, date1 é posterior ou anterior, respectivamente).
Ja͢ck
@Jack é uma maneira mais abstrata sem números mágicos (-1,0,1) para algoritmos de classificação para colocar os elementos em ordem. Você também pode redefinir essas constantes com um nome mais legível. Minha resposta é fazer o trabalho, mas não é o vencedor do código legível. Role para baixo, existem outros bons.
Nick Weaver
50

Tarde para a festa, mas outra maneira fácil de comparar objetos NSDate é convertê-los em tipos primitivos, o que permite o uso fácil de '>' '<' '==' etc

por exemplo.

if ([dateA timeIntervalSinceReferenceDate] > [dateB timeIntervalSinceReferenceDate]) {
    //do stuff
}

timeIntervalSinceReferenceDateconverte a data em segundos desde a data de referência (1 de janeiro de 2001, GMT). Como timeIntervalSinceReferenceDateretorna um NSTimeInterval (que é um double typedef), podemos usar comparadores primitivos.

So Over It
fonte
4
Ligeiramente mais intuitivo do que (NSComparisonResult)compare:(NSDate *)mas ainda bastante detalhado para esta operação simples ... (como de costume)
Pierre de LESPINAY
1
também pode fazer[dateA timeIntervalSinceDate:dateB] > 0
Scott Fister
14

No Swift, você pode sobrecarregar os operadores existentes:

func > (lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.timeIntervalSinceReferenceDate > rhs.timeIntervalSinceReferenceDate
}

func < (lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.timeIntervalSinceReferenceDate < rhs.timeIntervalSinceReferenceDate
}

Em seguida, você pode comparar NSDates diretamente com <, >e ==(já suportado).

Andrew
fonte
Se eu tentar fazer uma extensão disso, recebo "Os operadores são permitidos apenas no escopo global", sugestões?
precisa saber é o seguinte
@JohnVanDijk você não pode colocá-lo dentro de uma extensão. Eu o colocaria no mesmo arquivo que a extensão, mas fora do{ ... }
Andrew
13

NSDate tem uma função de comparação.

compare:Retorna um NSComparisonResultvalor que indica a ordem temporal do receptor e outra data especificada.

(NSComparisonResult)compare:(NSDate *)anotherDate

Parâmetros : anotherDate a data com a qual comparar o receptor. Este valor não deve ser nulo. Se o valor for nulo, o comportamento é indefinido e pode ser alterado nas versões futuras do Mac OS X.

Valor de retorno:

  • Se o receptor e outroData forem exatamente iguais um ao outro, NSOrderedSame
  • Se o receptor chegar mais tarde do que outroData, NSOrderedDescending
  • Se o receptor estiver mais adiantado que outroData NSOrderedAscending,.
Gary
fonte
@Irene existe uma maneira de comparar dois objetos NSDate nos quais apenas o componente de tempo é diferente? Por alguma razão, o método acima não funciona.
The UseFuL
12

Você deseja usar os métodos NSDate compare :, laterDate :, previousDate: ou isEqualToDate:. O uso dos operadores <e> nessa situação está comparando os ponteiros, não as datas

Dan F
fonte
11
- (NSDate *)earlierDate:(NSDate *)anotherDate

Isso retorna o anterior do receptor e outroData. Se ambos são iguais, o receptor é retornado.

Maulik
fonte
1
Observe que o objeto de backup do NSDates pode ser otimizado nas versões de 64 bits do seu código compilado, de modo que as datas que representam a mesma hora tenham o mesmo endereço. Assim, se cDate = [aDate earlierDate:bDate]então cDate == aDatee cDate == bDateambos podem ser verdadeiros. Encontrado esta fazendo algum trabalho data em iOS 8.
Ben Flynn
1
Por outro lado, em plataformas de 32 bits, se as datas não forem as mesmas, -earlierDate:(e -laterDate:) não poderão retornar nem o receptor nem o argumento.
Ben Lings
7

Alguns utilitários de data, incluindo comparações em inglês, o que é bom:

#import <Foundation/Foundation.h>


@interface NSDate (Util)

-(BOOL) isLaterThanOrEqualTo:(NSDate*)date;
-(BOOL) isEarlierThanOrEqualTo:(NSDate*)date;
-(BOOL) isLaterThan:(NSDate*)date;
-(BOOL) isEarlierThan:(NSDate*)date;
- (NSDate*) dateByAddingDays:(int)days;

@end

A implementação:

#import "NSDate+Util.h"


@implementation NSDate (Util)

-(BOOL) isLaterThanOrEqualTo:(NSDate*)date {
    return !([self compare:date] == NSOrderedAscending);
}

-(BOOL) isEarlierThanOrEqualTo:(NSDate*)date {
    return !([self compare:date] == NSOrderedDescending);
}
-(BOOL) isLaterThan:(NSDate*)date {
    return ([self compare:date] == NSOrderedDescending);

}
-(BOOL) isEarlierThan:(NSDate*)date {
    return ([self compare:date] == NSOrderedAscending);
}

- (NSDate *) dateByAddingDays:(int)days {
    NSDate *retVal;
    NSDateComponents *components = [[NSDateComponents alloc] init];
    [components setDay:days];

    NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    retVal = [gregorian dateByAddingComponents:components toDate:self options:0];
    return retVal;
}

@end
Dan Rosenstark
fonte
1
Ou simplesmente usar: github.com/erica/NSDate-Extensions
Dan Rosenstark
6

Você deveria usar :

- (NSComparisonResult)compare:(NSDate *)anotherDate

para comparar datas. Não há sobrecarga de operador no objetivo C.

Joris Mans
fonte
6

Por que vocês não usam esses NSDatemétodos de comparação:

- (NSDate *)earlierDate:(NSDate *)anotherDate;
- (NSDate *)laterDate:(NSDate *)anotherDate;
justicepenny
fonte
4

Eu encontrei quase a mesma situação, mas no meu caso eu estou verificando se o número de dias de diferença

NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *compDate = [cal components:NSDayCalendarUnit fromDate:fDate toDate:tDate options:0];
int numbersOfDaysDiff = [compDate day]+1; // do what ever comparison logic with this int.

Útil quando você precisa comparar o NSDate em dias / mês / ano

Andy
fonte
NSDayCalendarUnit está obsoleta então use NSCalendarUnitDay vez
Mazen Kasser
2

Você pode comparar duas datas por esse método também

        switch ([currenttimestr  compare:endtimestr])
        {
            case NSOrderedAscending:

                // dateOne is earlier in time than dateTwo
                break;

            case NSOrderedSame:

                // The dates are the same
                break;
            case NSOrderedDescending:

                // dateOne is later in time than dateTwo


                break;

        }
Kapil
fonte
0

Eu tentei, espero que funcione para você

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];      
int unitFlags =NSDayCalendarUnit;      
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];     
NSDate *myDate; //= [[NSDate alloc] init];     
[dateFormatter setDateFormat:@"dd-MM-yyyy"];   
myDate = [dateFormatter dateFromString:self.strPrevioisDate];     
NSDateComponents *comps = [gregorian components:unitFlags fromDate:myDate toDate:[NSDate date] options:0];   
NSInteger day=[comps day];
Conceito Infoway
fonte
0

Use esta função simples para comparação de datas

-(BOOL)dateComparision:(NSDate*)date1 andDate2:(NSDate*)date2{

BOOL isTokonValid;

if ([date1 compare:date2] == NSOrderedDescending) {
    NSLog(@"date1 is later than date2");
    isTokonValid = YES;
} else if ([date1 compare:date2] == NSOrderedAscending) {
    NSLog(@"date1 is earlier than date2");
    isTokonValid = NO;
} else {
    isTokonValid = NO;
    NSLog(@"dates are the same");
}

return isTokonValid;}
Akhtar
fonte