Cómo comparar dos NSDates: ¿Cuál es más reciente?


244

Estoy tratando de lograr una sincronización dropBox y necesito comparar las fechas de dos archivos. Uno está en mi cuenta de dropBox y el otro está en mi iPhone.

Se me ocurrió lo siguiente, pero obtengo resultados inesperados. Supongo que estoy haciendo algo fundamentalmente incorrecto al comparar las dos fechas. Simplemente usé los operadores> <, pero supongo que esto no es bueno ya que estoy comparando dos cadenas NSDate. Aquí vamos:

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.");
}

Esto me dio el siguiente resultado (aleatorio e incorrecto):

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.

o este que resulta ser correcto:

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.

11
Duplicados: 1 2 3 4 5 6 y c.
jscs

1
@JoshCaswell si es un duplicado real, ¿por qué no fusionarlos? Lo has hecho antes ...
Dan Rosenstark

1
Solo los moderadores de diamantes pueden realizar una fusión, @Yar.
jscs

Respuestas:


658

Asumamos dos fechas:

NSDate *date1;
NSDate *date2;

Entonces la siguiente comparación dirá cuál es anterior / posterior / igual:

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 la documentación de la clase NSDate para obtener más detalles.


¡Encantador! Es mejor que jugar con [date1 beforeDate: date2] etc ... Gracias, por alguna razón nunca pensé usar compare: before.
SomaMan el

11
Me gusta confiar en el hecho de que NSOrderedAscending <0 y NSOrderedDescending> 0. Eso hace que la comparación sea más fácil de leer: [date1 compare: date2] <0 / * date1 <date2 * / y evita el error (fácil de cometer) @albertamg señaló. ;-)
jpap

Bueno, el método de comparación es tan propenso a errores como los errores fuera de uno. Por lo tanto, debe usar (NSDate *) laterDate: (NSDate *) anotherDate que devolverá la fecha posterior de ambos. ¡así que solo compara el resultado esperado y listo! No jugar con "Waaait descendiendo / ascendiendo?"
masi

@jpap Eso también me fastidió; parece que Apple quiere que pienses en el resultado como date1 -> date2ascendente / descendente (y, por lo tanto, date1 es posterior o anterior, respectivamente).
Ja͢ck

@Jack es una forma más abstracta sin números mágicos (-1,0,1) para ordenar los algoritmos para ordenar los elementos. También podría redefinir esas constantes usted mismo con un nombre más legible. Mi respuesta es hacer el trabajo, pero no es el ganador del premio de código legible. Desplácese hacia abajo, hay otros buenos.
Nick Weaver

50

Tarde a la fiesta, pero otra forma fácil de comparar objetos NSDate es convertirlos en tipos primitivos que permitan un uso fácil de '>' '<' '==' etc.

p.ej.

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

timeIntervalSinceReferenceDateconvierte la fecha en segundos desde la fecha de referencia (1 de enero de 2001, GMT). Como timeIntervalSinceReferenceDatedevuelve un NSTimeInterval (que es un typedef doble), podemos usar comparadores primitivos.


44
Ligeramente más intuitivo que (NSComparisonResult)compare:(NSDate *)pero aún bastante detallado para esta operación simple ... (como de costumbre)
Pierre de LESPINAY

1
también puede hacer[dateA timeIntervalSinceDate:dateB] > 0
Scott Fister

14

En Swift, puede sobrecargar los operadores existentes:

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

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

A continuación, puede comparar directamente con NSDates <, >y ==(ya es compatible).


Si trato de hacer una extensión de esto, obtengo "¿Los operadores solo están permitidos a nivel global", sugerencias?
JohnVanDijk

@JohnVanDijk no puedes ponerlo dentro de una extensión. Lo pondría en el mismo archivo que la extensión, pero fuera de{ ... }
Andrew

13

NSDate Tiene una función de comparación.

compare:Devuelve un NSComparisonResultvalor que indica el orden temporal del receptor y otra fecha dada.

(NSComparisonResult)compare:(NSDate *)anotherDate

Parámetros : anotherDate la fecha con la que se compara el receptor. Este valor no debe ser nulo. Si el valor es nulo, el comportamiento es indefinido y puede cambiar en futuras versiones de Mac OS X.

Valor de retorno:

  • Si el receptor y otro DateDate son exactamente iguales entre sí, NSOrderedSame
  • Si el receptor llega más tarde que otroDate, NSOrderedDescending
  • Si el receptor es anterior en el tiempo que anotherDate, NSOrderedAscending.

@Irene, ¿hay alguna manera de comparar dos objetos NSDate en los que solo el componente de tiempo es diferente? Por alguna razón, el método anterior no funciona.
EL USUARIO

12

Desea utilizar la comparación NSDate :, laterDate :, beforeDate :, o isEqualToDate: métodos. Usar los operadores <y> en esta situación es comparar los punteros, no las fechas


11
- (NSDate *)earlierDate:(NSDate *)anotherDate

Esto devuelve el anterior del receptor y otroDate. Si ambos son iguales, se devuelve el receptor.


1
Tenga en cuenta que el objeto de respaldo de NSDates puede optimizarse en versiones de 64 bits de su código compilado, de modo que las fechas que representan la misma hora tengan la misma dirección. Por lo tanto, si cDate = [aDate earlierDate:bDate]entonces cDate == aDatey cDate == bDateambos pueden ser verdad. Encontré esto haciendo un trabajo de fecha en iOS 8.
Ben Flynn

1
Por el contrario, en plataformas de 32 bits, si las fechas no son las mismas, -earlierDate:(y -laterDate:) no pueden devolver ni el receptor ni el argumento.
Ben Lings

7

Algunas utilidades de fechas, incluidas las comparaciones EN INGLÉS, lo cual es bueno:

#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

La implementación:

#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

1
O simplemente use: github.com/erica/NSDate-Extensions
Dan Rosenstark


6

Deberías usar :

- (NSComparisonResult)compare:(NSDate *)anotherDate

para comparar fechas. No hay sobrecarga del operador en el objetivo C.


6

¿Por qué no usan estos NSDatemétodos de comparación?

- (NSDate *)earlierDate:(NSDate *)anotherDate;
- (NSDate *)laterDate:(NSDate *)anotherDate;

4

Me he encontrado con casi la misma situación, pero en mi caso estoy verificando si el número de días es diferente

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 cuando necesita comparar NSDate en días / mes / año


NSDayCalendarUnit está en desuso, así que use NSCalendarUnitDay en su lugar
Mazen Kasser

2

Puedes comparar dos fechas por este método también

        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;

        }

0

Lo he intentado espero que funcione para ti

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

0

Use esta función simple para comparar fechas

-(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;}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.