Vergleichen von zwei NSDates und Ignorieren der Zeitkomponente


72

Was ist der effizienteste / empfohlene Weg, um zwei NSDates zu vergleichen? Ich möchte in der Lage sein zu sehen, ob beide Daten unabhängig von der Uhrzeit am selben Tag liegen, und habe begonnen, Code zu schreiben, der die timeIntervalSinceDate: -Methode innerhalb der NSDate-Klasse verwendet und die Ganzzahl dieses Werts geteilt durch die Anzahl der Sekunden erhält in einem Tag. Das scheint langwierig und ich habe das Gefühl, dass mir etwas Offensichtliches fehlt.

Der Code, den ich zu beheben versuche, lautet:

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

Dabei sind key und todaysDate NSDate-Objekte und todaysDate wird erstellt mit:

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

Grüße

Dave


schöne Frage +1
Abbood

Antworten:


73

Sie stellen die Uhrzeit im Datum auf 00:00:00 ein, bevor Sie den Vergleich durchführen:

unsigned int flags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;
NSCalendar* calendar = [NSCalendar currentCalendar];

NSDateComponents* components = [calendar components:flags fromDate:date];

NSDate* dateOnly = [calendar dateFromComponents:components];

// ... necessary cleanup

Dann können Sie die Datumswerte vergleichen. Siehe die Übersicht in der Referenzdokumentation .


Vielen Dank für die schnelle Antwort Gregory, das ist wirklich hilfreich. Ich denke, meine nächste Frage ist, da dies Teil einer engen Schleife ist, denken Sie, dass es effizienter ist, die timeIntervalSinceDate: -Methode und eine ganzzahlige Arithmetik zu verwenden, anstatt mehr Objekte zu erstellen, die erforderliche Berechnung durchzuführen und dann aufzuräumen? Nochmals vielen Dank für Ihre Hilfe, Dave
Magic Bullet Dave

13
Mein Rat ist: Richtige Implementierung ist Gold; dann Profil. Wenn diese bestimmte Schleife wirklich ein Engpass ist, dann optimieren Sie
Gregory Pakosz

3
Da Sie nur die Einheiten Jahr, Monat und Tag herausziehen, werden Stunde, Minuten und Sekunden automatisch auf 0 gesetzt. Daher müssen Sie dies nicht explizit selbst tun.
Dave DeLong

1
Ich habe versucht, diese Methode zu verwenden, hat nicht funktioniert. Ich habe immer die Zeit ungleich 00:00?
iosMentalist

1
Ich musste das Kalenderobjekt mit [calendar setTimeZone: [NSTimeZone timeZoneWithAbbreviation: @ "GMT"]] konfigurieren. Andernfalls funktioniert der Vergleich nicht ordnungsgemäß.
Vladimír Slavík

83

Ich bin überrascht, dass keine anderen Antworten diese Option haben, um das "Beginn des Tages" -Datum für die Objekte zu erhalten:

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

Welche setzt date1und date2an den Anfang ihrer jeweiligen Tage. Wenn sie gleich sind, sind sie am selben Tag.

Oder diese Option:

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

Welche setzt day1und day2auf etwas willkürliche Werte, die verglichen werden können. Wenn sie gleich sind, sind sie am selben Tag.


Kleine FYI, NSDayCalendarUnitwurde veraltet und wird ersetzt durchNSCalendarUnitDay
Dwayne Forde

20

Es gibt eine neue Methode, die mit iOS 8 in NSCalendar eingeführt wurde und dies erheblich vereinfacht.

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

Sie legen die Granularität auf die Einheit (en) fest, die wichtig sind. Dies ignoriert alle anderen Einheiten und schränkt den Vergleich mit den ausgewählten ein.


Das ist eine sehr coole Methode. (abgestimmt) Schade, dass es nur in iOS 8 verfügbar ist. Ich denke, wenn Sie iOS <8 noch unterstützen müssen, können Sie eine Methode mit dem Code schreiben, den ich für iOS <8 und compareDate:toDate:toUnitGranularity:unter iOS> = 8 aufgelistet habe . Sobald Sie die Unterstützung für Betriebssystemversionen <8 einstellen, können Sie die Implementierung auf den Aufruf von reduzieren compareDate:toDate:toUnitGranularity:.
Duncan C

1
Die ultimative Methode wäre differenceBetweenDate:andDate:usingUnit:natürlich, negative, null oder positive ganzzahlige Werte zurückzugeben, die den Betrag der Differenz zwischen den Daten in der angeforderten Uint angeben.
Duncan C

Diese Methode kann verwendet werden, aber die Erklärung für den Einheitenparameter ist falsch! Es wird nur die kleinste Einheit erwartet, die verwendet werden sollte. Apple-Dokumentation: Die kleinste Einheit, die zusammen mit allen größeren Einheiten gleich sein muss, damit die angegebenen Daten als gleich gelten
Matoz

Anwendungsbeispiel: BOOL CompetitionStarted = [[NSCalendar currentCalendar] compareDate: self.competition.startDate toDate: [NSDate date] toUnitGranularity: NSCalendarUnitDay]! = NSOrderedDescending;
Leon

16

Für iOS8 und höher ist die Überprüfung, ob zwei Daten am selben Tag auftreten, so einfach wie:

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

Siehe Dokumentation


Das hat mir viel Zeit gespart. Vielen Dank!
Vinayak

10

Dies ist eine Abkürzung aller Antworten:

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
    }

1
Dieser Code funktioniert nicht. Wenn Sie dem [NSDate-Datum] 23 Stunden hinzufügen, werden beide Daten am selben Tag angezeigt, was in den meisten Fällen offensichtlich falsch ist.
Gottfried

@ Gottfired Dies ignoriert den Zeitteil, wie vom OP angefordert. Ich bin mir nicht sicher, was du mit 23 Stunden meinst. Du meinst die Daten ändern?
Bms270

@ Bms270 Er meint den Tag zu ändern. Wenn Sie heute um 15:00 Uhr mit morgen um 9:00 Uhr vergleichen, gibt Ihr Code ein Delta von 0 Tagen zurück, obwohl das zweite Datum eindeutig ein anderer Tag ist.
Christian Schnorr

@ Jenox Hast du diesen Code ausprobiert? Ihr Beispiel sollte zurückkehren: heute> morgen, egal wie spät es ist. Die Stunden werden einfach ignoriert, als würden Sie 12 Uhr mit 12 Uhr vergleichen. OP sagt "unabhängig von der Zeit".
Bms270

Der springende Punkt bei den Kalendermethoden ist, dass sie clever sind. Der Anruf ordnet beide Daten der Zeitzone des Kalenders zu, ignoriert alle kleineren Einheiten und wenn Sie heute 2 Uhr morgens mit 1 Uhr morgens vergleichen, sind sie nicht am selben Tag, daher ist das Ergebnis 1.
gnasher729

6

Ich habe den Duncan C-Ansatz verwendet und einige Fehler behoben, die er gemacht hat

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

Ich benutze diese kleine util-Methode:

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

(Sie benötigen offensichtlich einen Ivar calendar_mit einem NSCalendar.)

Auf diese Weise können Sie leicht überprüfen, ob ein Datum heute so ist:

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

( [NSDate date]Gibt das aktuelle Datum und die aktuelle Uhrzeit zurück.)

Dies ist natürlich sehr ähnlich zu dem, was Gregory vorschlägt. Der Nachteil dieses Ansatzes besteht darin, dass er dazu neigt, viele temporäre NSDateObjekte zu erstellen . Wenn Sie viele Daten verarbeiten möchten, würde ich empfehlen, eine andere Methode zu verwenden, z. B. die Komponenten direkt zu vergleichen oder NSDateComponentsstattdessen mit Objekten zu arbeiten NSDates.


3

Die Antwort ist einfacher als jeder denkt. NSCalendar hat eine Methode

components:fromDate:toDate:options

Mit dieser Methode können Sie die Differenz zwischen zwei Daten mit den gewünschten Einheiten berechnen.

Schreiben Sie also eine Methode wie diese:

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

Wenn die obige Methode Null zurückgibt, befinden sich die beiden Daten am selben Tag.


3

Ab iOS 8.0 können Sie Folgendes verwenden:

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

Wenn das Ergebnis zB NSOrderedDescending ist, liegt otherDate in Tagen vor [NSDate date].

Ich sehe diese Methode nicht in der NSCalendar-Dokumentation, aber in den API-Unterschieden zwischen iOS 7.1 und iOS 8.0


3

Mit Swift 3 können Sie je nach Bedarf eines der beiden folgenden Muster auswählen, um Ihr Problem zu lösen.


# 1. Mit compare(_:to:toGranularity:)Methode

Calendarhat eine Methode namens compare(_:​to:​to​Granularity:​). compare(_:​to:​to​Granularity:​)hat die folgende Erklärung:

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

Vergleicht die angegebenen Daten mit der angegebenen Komponente und gibt sie an, ordered​Samewenn sie in der angegebenen Komponente und allen größeren Komponenten identisch sind, andernfalls entweder ordered​Ascendingoder ordered​Descending.

Der folgende Spielplatzcode zeigt, wie heiß es ist, ihn zu verwenden:

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. Verwenden vondateComponents(_:from:to:)

Calendarhat eine Methode namens dateComponents(_:from:to:). dateComponents(_:from:to:)hat die folgende Erklärung:

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

Gibt die Differenz zwischen zwei Daten zurück.

Der folgende Spielplatzcode zeigt, wie heiß es ist, ihn zu verwenden:

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
}

Vielen Dank!! das, wonach ich suche Sehr hilfreich
Vishal Shelake

2

Für Entwickler, die in Swift 3 codieren

if(NSCalendar.current.isDate(selectedDate, inSameDayAs: NSDate() as Date)){
     // Do something
}

1
int interval = (int)[firstTime timeIntervalSinceDate:secondTime]/(60*60*24);
if (interval!=0){
   //not the same day;
}

1

Meine Lösung waren zwei Konvertierungen mit 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  
    }

0

Die Dokumentation in Bezug auf NSDate zeigt an, dass die compare:und isEqual:Methoden sowohl einen grundlegenden Vergleich durchführen und die Ergebnisse zu bestellen, wenn auch sie noch Faktor in der Zeit.

Der wahrscheinlich einfachste Weg, die Aufgabe zu verwalten, besteht darin, eine neue isTodayMethode zu erstellen, die Folgendes bewirkt:

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

0

Dies ist eine besonders hässliche Katze für die Haut, aber hier ist eine andere Möglichkeit, dies zu tun. Ich sage nicht, dass es elegant ist, aber es ist wahrscheinlich so nah wie möglich an der Datums- / Zeitunterstützung in iOS.

bool isToday = [[NSDateFormatter localizedStringFromDate:date dateStyle:NSDateFormatterFullStyle timeStyle:NSDateFormatterNoStyle] isEqualToString:[NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterFullStyle timeStyle:NSDateFormatterNoStyle]];

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

Vergleichen Sie auch die Stunde, um den Unterschied von 1 Tag zu beheben

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.