2010-06-19 9 views
5

Ich schreibe eine GTD-App für das iPhone. Für die fälligen Aufgaben möchte ich etwas wie "Fällig morgen" oder "Fällig gestern" oder "Fällig am 18. Juli" anzeigen. Offensichtlich muss ich "Morgen" anzeigen, selbst wenn die Aufgabe weniger als 24 Stunden entfernt ist (z. B. überprüft der Benutzer am Samstag um 23 Uhr und sieht, dass am Sonntag um 8 Uhr eine Aufgabe stattfindet). Also habe ich eine Methode geschrieben, um die Anzahl der Tage zwischen zwei Daten zu ermitteln. Hier ist der Code ...iPhone - Anzahl der Tage zwischen zwei Daten erhalten

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; 
[dateFormatter setDateFormat:@"yyyy-MM-dd-HH-mm"]; 

NSDate *nowDate = [dateFormatter dateFromString:@"2010-01-01-15-00"]; 
NSDate *dueDate = [dateFormatter dateFromString:@"2010-01-02-14-00"]; 

NSLog(@"NSDate *nowDate = %@", nowDate); 
NSLog(@"NSDate *dueDate = %@", dueDate); 

NSCalendar *calendar = [NSCalendar currentCalendar]; 

NSDateComponents *differenceComponents = [calendar components:(NSDayCalendarUnit) 
                fromDate:nowDate 
                 toDate:dueDate 
                 options:0]; 

NSLog(@"Days between dates: %d", [differenceComponents day]); 

... und hier ist die Ausgabe:

NSDate *nowDate = 2010-01-01 15:00:00 -0700 
NSDate *dueDate = 2010-01-02 14:00:00 -0700 
Days between dates: 0 

Wie Sie sehen können, gibt die Methode falsche Ergebnisse. Es sollte 1 als die Anzahl der Tage zwischen den zwei Tagen zurückgegeben werden. Was mache ich hier falsch?

EDIT: Ich schrieb eine andere Methode. Ich habe nicht umfangreiche Unit-Tests gemacht, aber bisher scheint es zu funktionieren:

+ (NSInteger)daysFromDate:(NSDate *)fromDate inTimeZone:(NSTimeZone *)fromTimeZone untilDate:(NSDate *)toDate inTimeZone:(NSTimeZone *)toTimeZone { 

    NSCalendar *calendar = [NSCalendar currentCalendar]; 
    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit; 

    [calendar setTimeZone:fromTimeZone]; 
    NSDateComponents *fromDateComponents = [calendar components:unitFlags fromDate:fromDate]; 

    [calendar setTimeZone:toTimeZone]; 
    NSDateComponents *toDateComponents = [calendar components:unitFlags fromDate:toDate]; 

    [calendar setTimeZone:[NSTimeZone defaultTimeZone]]; 
    NSDate *adjustedFromDate = [calendar dateFromComponents:fromDateComponents]; 
    NSDate *adjustedToDate = [calendar dateFromComponents:toDateComponents]; 

    NSTimeInterval timeIntervalBetweenDates = [adjustedToDate timeIntervalSinceDate:adjustedFromDate]; 
    NSInteger daysBetweenDates = (NSInteger)(timeIntervalBetweenDates/(60.0 * 60.0 * 24.0)); 

    NSDateComponents *midnightBeforeFromDateComponents = [[NSDateComponents alloc] init]; 
    [midnightBeforeFromDateComponents setYear:[fromDateComponents year]]; 
    [midnightBeforeFromDateComponents setMonth:[fromDateComponents month]]; 
    [midnightBeforeFromDateComponents setDay:[fromDateComponents day]]; 

    NSDate *midnightBeforeFromDate = [calendar dateFromComponents:midnightBeforeFromDateComponents]; 
    [midnightBeforeFromDateComponents release]; 

    NSDate *midnightAfterFromDate = [[NSDate alloc] initWithTimeInterval:(60.0 * 60.0 * 24.0) 
                   sinceDate:midnightBeforeFromDate]; 

    NSTimeInterval timeIntervalBetweenToDateAndMidnightBeforeFromDate = [adjustedToDate timeIntervalSinceDate:midnightBeforeFromDate]; 
    NSTimeInterval timeIntervalBetweenToDateAndMidnightAfterFromDate = [adjustedToDate timeIntervalSinceDate:midnightAfterFromDate]; 

    if (timeIntervalBetweenToDateAndMidnightBeforeFromDate < 0.0) { 

     // toDate is before the midnight before fromDate 

     timeIntervalBetweenToDateAndMidnightBeforeFromDate -= daysBetweenDates * 60.0 * 60.0 * 24.0; 

     if (timeIntervalBetweenToDateAndMidnightBeforeFromDate < 0.0) 
      daysBetweenDates -= 1; 
    } 
    else if (timeIntervalBetweenToDateAndMidnightAfterFromDate >= 0.0) { 

     // toDate is after the midnight after fromDate 

     timeIntervalBetweenToDateAndMidnightAfterFromDate -= daysBetweenDates * 60.0 * 60.0 * 24.0; 

     if (timeIntervalBetweenToDateAndMidnightAfterFromDate >= 0.0) 
      daysBetweenDates += 1; 
    } 

    [midnightAfterFromDate release]; 

    return daysBetweenDates; 
} 

Antwort

3

Aus der Dokumentation für components:fromDate:toDate:options::

Das Ergebnis verlustbehafteten ist, wenn es nicht eine kleine genug Einheit ist aufgefordert, halte die volle Genauigkeit des Unterschieds.

Da der Unterschied weniger als ein ganzer Tag ist, gibt es korrekt ein Ergebnis von 0 Tagen zurück.

+0

Nun, meine Frage anders zu Wort, wie kann ich überprüfen, wie viele midnights es zwischen meinen beiden Terminen war? – Tim

2

Wenn alles, was Sie interessiert, ist morgen oder gestern gegen ein bestimmtes Datum, dann können Sie sich eine Menge Arbeit sparen und nur testen, ob die Daten nur einen Kalendertag auseinander liegen.

Um dies zu tun, vergleichen Sie die Daten zu finden, die früher und welche später ist (und wenn sie gleich vergleichen, mit diesem Ergebnis bail out), dann testen, ob 1 Tag nach dem früheren Datum ein Datum mit dem gleichen Jahr produziert , Monat und Tag des Monats als das spätere Datum.


Wenn Sie wirklich genau wollen wissen, wie viele Kalendertage dort von einem Datum zum anderen sind:

  1. den Kalender eine components:fromDate: Nachricht senden Jahr, Monat und Tag-bekommen des Monats des ersten Datums.
  2. Wie # 1, aber für das zweite Datum.
  3. Wenn die beiden Daten im selben Jahr und Monat sind, subtrahieren Sie einen Tag des Monats von dem anderen und gehen Sie zu abs (siehe abs(3)), um den absoluten Wert zu nehmen.
  4. Wenn sie nicht im selben Jahr und Monat sind, testen Sie, ob sie sich in benachbarten Monaten befinden (z. B. Dezember 2010 bis Januar 2011 oder Juni 2010 bis Juli 2010). Wenn dies der Fall ist, fügen Sie die Anzahl der Tage im früheren Datumsmonat hinzu (die Sie erhalten, indem Sie dem Kalender eine rangeOfUnit:inUnit:forDate: Nachricht senden, die NSDayCalendarUnit bzw. NSMonthCalendarUnit) an den Tag des Monats des späteren Datums übergeben und dann dieses Ergebnis vergleichen zum Tagesdatum des früheren Tages.

    Wenn Sie zum Beispiel 2010-12-31 bis 2011-01-01 vergleichen, würden Sie zuerst feststellen, dass diese in benachbarten Monaten liegen, dann 31 (Anzahl der Tage in 2010-12) zu 1 (Tag-von (2011-01-01), dann subtrahieren Sie 31 (Tag des Monats 2010-12-31) von dieser Summe. Da die Differenz 1 ist, ist das frühere Datum ein Tag vor dem späteren Datum.

    Wenn 2010-12- Vergleich zu 2011-01- , würden Sie feststellen, dass sie in benachbarten Monate sind, dann fügen Sie 31 (Tage in 2010-12) bis 2 (Tag-of-Monat vom 2011-01-02), subtrahieren Sie dann 30 (Tag des Monats 2010-12-30) von dieser Summe. 33 minus 30 ist 3, also sind diese Daten drei Kalendertage auseinander.


So oder so, empfehle ich für diesen Code Unit-Tests zumindest zu schreiben. Ich habe festgestellt, dass der Code zur Verarbeitung von Datums zu den am häufigsten auftretenden Fehlern zählt, die sich nur zweimal pro Jahr manifestieren.

+0

Was Sie vorschlagen, klingt ziemlich kompliziert. Ich habe eine andere Methode geschrieben, schaue auf meine Bearbeitung des ursprünglichen Posts. Ich bin mir nicht sicher, ob es in allen Fällen richtig funktioniert, aber es hat so weit für mich. – Tim

+0

Tim: Meine beiden Vorschläge sind weniger kompliziert als das, was Sie in Ihrer Frage geschrieben haben - besonders der erste Vorschlag, der nicht mehr als ein Dutzend Aussagen enthalten sollte. –

2

Eine Sache, die Sie versuchen könnten, ist rangeOfUnit: zu verwenden, um Stunden, Minuten und Sekunden vom Anfangs- und Enddatum auf Null zu setzen.

NSCalendar *calendar = [NSCalendar currentCalendar]; 
NSCalendarUnit range = NSDayCalendarUnit; 
NSDateComponents *comps = [[NSDateComponents alloc] init]; 
NSDate *start = [NSDate date]; 
NSDate *end; 

[comps setDay:1]; 
[calendar rangeOfUnit:range startDate:&start interval:nil forDate:start]; 

end = [calendar dateByAddingComponents:comps toDate:start options:0]; 

In diesem Beispiel Start wird 2010-06-19 00:00:00 -0400 sein, wird Ende 2010-06-20 00:00:00 -0400 sein. Ich könnte mir vorstellen, dass dies mit den Vergleichsmethoden von NSCalendar besser funktioniert, obwohl ich es nicht selbst getestet habe.

0

Dies ist die Funktion, die ich in der Vergangenheit verwendet haben seine in einer Kategorie auf NSDate definiert

- (int) daysToDate:(NSDate*) endDate 
{ 
    //dates needed to be reset to represent only yyyy-mm-dd to get correct number of days between two days. 
    NSDateFormatter *temp = [[NSDateFormatter alloc] init]; 
    [temp setDateFormat:@"yyyy-MM-dd"]; 
    NSDate *stDt = [temp dateFromString:[temp stringFromDate:self]]; 
    NSDate *endDt = [temp dateFromString:[temp stringFromDate:endDate]]; 
    [temp release]; 
    unsigned int unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit; 
    NSCalendar *gregorian = [[NSCalendar alloc] 
          initWithCalendarIdentifier:NSGregorianCalendar]; 
    NSDateComponents *comps = [gregorian components:unitFlags fromDate:stDt toDate:endDt options:0]; 
    int days = [comps day]; 
    [gregorian release]; 
    return days; 
} 
2

Sie können es leicht arbeiten, um die sehr schöne Antwort mit aus diesem related question

0
-(NSInteger)daysBetweenTwoDates:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime 

{

NSDate *fromDate; 
NSDate *toDate; 

NSCalendar *calendar = [NSCalendar currentCalendar]; 

[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate 
      interval:NULL forDate:fromDateTime]; 
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate 
      interval:NULL forDate:toDateTime]; 

NSDateComponents *difference = [calendar components:NSDayCalendarUnit 
              fromDate:fromDate toDate:toDate options:0]; 

return [difference day]; 

}

1

ich dieses Stück Code verwenden, funktioniert es sehr gut:

- (NSInteger)daysToDate:(NSDate*)date 
{ 
    if(date == nil) { 
     return NSNotFound; 
    } 
    NSUInteger otherDay = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:date]; 
    NSUInteger currentDay = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:self]; 
    return (otherDay-currentDay); 
} 
Verwandte Themen