Jak porównać dwie daty NSD: która jest nowsza?

244

Próbuję osiągnąć synchronizację DropBox i muszę porównać daty dwóch plików. Jeden jest na moim koncie dropBox, a drugi na moim iPhonie.

Wymyśliłem następujące, ale otrzymuję nieoczekiwane wyniki. Wydaje mi się, że robię coś zasadniczo nie tak, porównując dwie daty. Po prostu użyłem operatorów> <, ale myślę, że to nie jest dobre, ponieważ porównuję dwa ciągi NSDate. No to ruszamy:

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

To dało mi następujące (losowe i nieprawidłowe) dane wyjściowe:

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.

lub ten, który okazuje się być poprawny:

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.
nieważne
źródło
11
Duplikaty: 1 2 3 4 5 6 i c.
jscs
1
@JoshCaswell, jeśli to prawdziwy duplikat, dlaczego nie scalić ich? Zrobiłeś to wcześniej ...
Dan Rosenstark
1
Tylko moderatorzy diamentów mogą wykonać scalenie, @Yar.
jscs

Odpowiedzi:

658

Załóżmy dwie daty:

NSDate *date1;
NSDate *date2;

Następnie poniższe porównanie pokaże, które jest wcześniejsze / późniejsze / takie same:

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

Więcej informacji znajduje się w dokumentacji klasy NSDate .

Nick Weaver
źródło
Śliczny! Bicie się z [data1 wcześniejData: data2] itd ... Dzięki - z jakiegoś powodu nigdy nie pomyślałem, aby użyć porównania: wcześniej.
SomaMan,
11
Lubię polegać na tym, że NSOrdersAscending <0 i NSOrdersDescending> 0. To sprawia, że ​​porównanie jest łatwiejsze do odczytania: [data1 porównaj: data2] <0 / * data1 <data2 * / i pozwala uniknąć (łatwego do popełnienia) błędu @albertamg wskazany. ;-)
JPap
Cóż - metoda porównywania jest tak podatna na błędy, jak błędy indywidualne. Dlatego powinieneś użyć (NSDate *) laterDate: (NSDate *) anotherDate, która zwróci późniejszą datę obu. więc po prostu porównaj oczekiwany wynik i gotowe! Nie musisz się bawić z „Waaait descent / ascending?!”
masi
@jpap To też mnie zawiodło; Wygląda na to, że Apple chce, abyś pomyślał o wyniku w miarę date1 -> date2rosnącej / malejącej (i dlatego data1 jest późniejsza lub wcześniejsza).
Ja͢ck
@Jack to bardziej abstrakcyjny sposób bez magicznych liczb (-1,0,1) do sortowania algorytmów w celu uporządkowania elementów. Możesz także samodzielnie zdefiniować te stałe, używając bardziej czytelnej nazwy. Moja odpowiedź dotyczy pracy, ale nie jest laureatem czytelnego kodu. Przewiń w dół, są też inne dobre.
Nick Weaver
50

Późno na imprezę, ale innym łatwym sposobem porównywania obiektów NSDate jest konwersja ich na prymitywne typy, co pozwala na łatwe użycie „>” „<” „==” itp.

na przykład.

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

timeIntervalSinceReferenceDatekonwertuje datę na sekundy od daty odniesienia (1 stycznia 2001 r., GMT). Gdy timeIntervalSinceReferenceDatezwraca NSTimeInterval (który jest podwójnym typedef), możemy użyć prymitywnych komparatorów.

So Over It
źródło
4
Nieco bardziej intuicyjny niż, (NSComparisonResult)compare:(NSDate *)ale wciąż dość gadatliwy dla tej prostej operacji ... (jak zwykle)
Pierre de LESPINAY
1
można również zrobić[dateA timeIntervalSinceDate:dateB] > 0
Scott Fister
14

W Swift możesz przeciążać istniejących operatorów:

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

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

Następnie można porównać NSDates bezpośrednio <, >oraz ==(już obsługiwane).

Andrzej
źródło
Jeśli spróbuję zrobić z tego rozszerzenie, otrzymam „Operatorzy są dopuszczeni tylko w zakresie globalnym”, sugestie?
JohnVanDijk
@JohnVanDijk nie możesz umieścić go w rozszerzeniu. Umieściłbym go w tym samym pliku, co rozszerzenie, ale poza{ ... }
Andrew,
13

NSDate ma funkcję porównania.

compare:Zwraca NSComparisonResultwartość wskazującą czasowe uporządkowanie odbiornika i inną podaną datę.

(NSComparisonResult)compare:(NSDate *)anotherDate

Parametry : anotherDate data, z którą należy porównać odbiornik. Ta wartość nie może wynosić zero. Jeśli wartość wynosi zero, zachowanie jest niezdefiniowane i może ulec zmianie w przyszłych wersjach systemu Mac OS X.

Zwracana wartość:

  • Jeśli odbiornik i inna data są dokładnie sobie równe, NSOrderedSame
  • Jeśli odbiorca jest później niż w innym terminie, NSOrderedDescending
  • Jeśli odbiornik jest wcześniej w czasie niż anotherDate, NSOrderedAscending.
Gary
źródło
@ Irene jest sposób na porównanie dwóch obiektów NSDate, w których różni się tylko składnik czasu? Z jakiegoś powodu powyższa metoda nie działa.
uSeFuL
12

Chcesz użyć metod porównania NSDate :, laterDate :, wcześniejData :, lub isEqualToDate: metody. Używanie operatorów <i> w tej sytuacji polega na porównywaniu wskaźników, a nie dat

Dan F.
źródło
11
- (NSDate *)earlierDate:(NSDate *)anotherDate

Zwraca wcześniejszy odbiornik i inną datę. Jeśli oba są takie same, zwracany jest odbiornik.

Maulik
źródło
1
Należy pamiętać, że obiekt kopii zapasowej NSDates może być zoptymalizowany w 64-bitowych wersjach skompilowanego kodu, tak aby daty reprezentujące ten sam czas miały ten sam adres. Tak więc, jeśli cDate = [aDate earlierDate:bDate]wtedy cDate == aDatei cDate == bDateoba mogą być prawdą. Znalazłem to, pracując nad datą na iOS 8.
Ben Flynn
1
I odwrotnie, na platformach 32-bitowych, jeśli daty nie są takie same, -earlierDate:(i -laterDate:) nie mogą zwrócić ani odbiornika, ani argumentu.
Ben Lings
7

Niektóre narzędzia daty, w tym porównania W JĘZYKU ANGIELSKIM, co jest miłe:

#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

Implementacja:

#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
źródło
1
Lub po prostu użyj: github.com/erica/NSDate-Extensions
Dan Rosenstark
6

Powinieneś użyć :

- (NSComparisonResult)compare:(NSDate *)anotherDate

porównać daty. W celu C nie występuje przeciążenie operatora

Joris Mans
źródło
6

Dlaczego nie zastosujecie tych NSDatemetod porównywania:

- (NSDate *)earlierDate:(NSDate *)anotherDate;
- (NSDate *)laterDate:(NSDate *)anotherDate;
Justicepenny
źródło
4

Spotkałem prawie taką samą sytuację, ale w moim przypadku sprawdzam, czy liczba dni różni się

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.

Przydatne, gdy trzeba porównać NSDate w jednostce Dni / Miesiąc / Rok

Andy
źródło
NSDayCalendarUnit jest przestarzały, więc zamiast tego użyj NSCalendarUnitDay
Mazen Kasser
2

Możesz także porównać dwie daty tą metodą

        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
źródło
0

Próbowałem, mam nadzieję, że to zadziała

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];
Koncepcja Infoway
źródło
0

Użyj tej prostej funkcji do porównania dat

-(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
źródło