2009-06-27 1 views
11

Ich möchte eine Fuzzy-Datum-Methode für die Berechnung von Daten in Objective-C für iPhone schreiben. Es ist eine beliebte Erklärung hier:Fuzzy-Date-Algorithmus in Objective-C

Calculate relative time in C#

jedoch es fehlende Argumente enthält. Wie könnte dies in Objective-C verwendet werden? Vielen Dank.

const int SECOND = 1; 
const int MINUTE = 60 * SECOND; 
const int HOUR = 60 * MINUTE; 
const int DAY = 24 * HOUR; 
const int MONTH = 30 * DAY; 

if (delta < 1 * MINUTE) 
{ 
    return ts.Seconds == 1 ? "one second ago" : ts.Seconds + " seconds ago"; 
} 
if (delta < 2 * MINUTE) 
{ 
    return "a minute ago"; 
} 
if (delta < 45 * MINUTE) 
{ 
    return ts.Minutes + " minutes ago"; 
} 
if (delta < 90 * MINUTE) 
{ 
    return "an hour ago"; 
} 
if (delta < 24 * HOUR) 
{ 
    return ts.Hours + " hours ago"; 
} 
if (delta < 48 * HOUR) 
{ 
    return "yesterday"; 
} 
if (delta < 30 * DAY) 
{ 
    return ts.Days + " days ago"; 
} 
if (delta < 12 * MONTH) 
{ 
    int months = Convert.ToInt32(Math.Floor((double)ts.Days/30)); 
    return months <= 1 ? "one month ago" : months + " months ago"; 
} 
else 
{ 
    int years = Convert.ToInt32(Math.Floor((double)ts.Days/365)); 
    return years <= 1 ? "one year ago" : years + " years ago"; 
} 

Antwort

23

Datumsangaben werden in Cocoa mit der Klasse NSDate dargestellt. Es gibt eine bequeme Methode, die in NSDate implementiert wird, um das Delta in Sekunden zwischen zwei Datuminstanzen timeIntervalSinceDate: zu erhalten. Dies wird an einer NSDate Instanz aufgerufen, wobei ein weiteres NSDate Objekt als Argument genommen wird. Es gibt eine NSTimeInterval (das ist ein typedef für ein Double) zurück, das die Anzahl der Sekunden zwischen den zwei Daten darstellt.

Vor diesem Hintergrund wäre es ziemlich einfach, den oben angegebenen Code an einen Objective-C/Cocoa-Kontext anzupassen. Da das von NSDate berechneten Delta in Sekunden angegeben, zwei Daten gegeben, könnte man leicht den Code oben anpassen:

//Constants 
#define SECOND 1 
#define MINUTE (60 * SECOND) 
#define HOUR (60 * MINUTE) 
#define DAY (24 * HOUR) 
#define MONTH (30 * DAY) 

- (NSString*)timeIntervalWithStartDate:(NSDate*)d1 withEndDate:(NSDate*)d2 
{ 
    //Calculate the delta in seconds between the two dates 
    NSTimeInterval delta = [d2 timeIntervalSinceDate:d1]; 

    if (delta < 1 * MINUTE) 
    { 
     return delta == 1 ? @"one second ago" : [NSString stringWithFormat:@"%d seconds ago", (int)delta]; 
    } 
    if (delta < 2 * MINUTE) 
    { 
     return @"a minute ago"; 
    } 
    if (delta < 45 * MINUTE) 
    { 
     int minutes = floor((double)delta/MINUTE); 
     return [NSString stringWithFormat:@"%d minutes ago", minutes]; 
    } 
    if (delta < 90 * MINUTE) 
    { 
     return @"an hour ago"; 
    } 
    if (delta < 24 * HOUR) 
    { 
     int hours = floor((double)delta/HOUR); 
     return [NSString stringWithFormat:@"%d hours ago", hours]; 
    } 
    if (delta < 48 * HOUR) 
    { 
     return @"yesterday"; 
    } 
    if (delta < 30 * DAY) 
    { 
     int days = floor((double)delta/DAY); 
     return [NSString stringWithFormat:@"%d days ago", days]; 
    } 
    if (delta < 12 * MONTH) 
    { 
     int months = floor((double)delta/MONTH); 
     return months <= 1 ? @"one month ago" : [NSString stringWithFormat:@"%d months ago", months]; 
    } 
    else 
    { 
     int years = floor((double)delta/MONTH/12.0); 
     return years <= 1 ? @"one year ago" : [NSString stringWithFormat:@"%d years ago", years]; 
    } 
} 

Diese dann aufgerufen werden würde, den Start geht und NSDate Objekte als Argumente beenden, und wäre eine Rückkehr NSString mit dem Zeitintervall.

+0

Das funktioniert wunderbar. Vielen Dank! –

+3

Das einzige Problem bei dieser Implementierung ist, dass nicht zwischen 24 Stunden als Tag und einem Kalendertag unterschieden wird. Wenn ich zum Beispiel 11:00 PM und 2:00 AM vergleiche, sollte der Unterschied "gestern" und nicht "3 Stunden" sein. Sehen Sie sich NSCalendar und seine begleitende NSDateComponents-Klasse an. – retainCount

1

Sie können das Delta zwischen zwei NSDate Objekte erhalten, indem die timeIntervalSinceDate: Methode. Das gibt dir das Delta in Sekunden.

Daraus können Sie Minuten/Stunden/Tage/Motten/Jahre durch Dividieren durch die entsprechende Menge herausfinden.

1

Als Alternative können Sie den Fehler anfällig Kalender Arithmetik vermeiden auf den Kalenderkomponenten, indem sie sich aus einer Differenz zwischen zwei Daten ziehen können :

NSDate *nowDate = [[NSDate alloc] init]; 
NSDate *targetDate = nil; // some other date here of your choosing, obviously nil isn't going to get you very far 

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; 
NSUInteger unitFlags = NSMonthCalendarUnit | NSWeekCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit; 
NSDateComponents *components = [gregorian components:unitFlags 
              fromDate:dateTime 
               toDate:nowDate options:0]; 
NSInteger months = [components month]; 
NSInteger weeks = [components week]; 
NSInteger days = [components day]; 
NSInteger hours = [components hour]; 
NSInteger minutes = [components minute]; 

der Schlüssel ist die Einrichtung der Einheit Flaggen - das Sie, welche Zeiteinheiten festlegen können Sie das Datum/Zeit wollen in aufgeschlüsselt werden. Wenn Sie nur Stunden brauchen, würden Sie NSHourCalendarUnit einstellen, und dieser Wert wird immer weiter ansteigen, wenn sich Ihre Daten weiter auseinander bewegen, weil es keine größere Einheit gibt, mit der inkrementiert werden könnte.

Sobald Sie Ihre Komponenten haben, können Sie mit der Logik Ihrer Wahl fortfahren, vielleicht indem Sie den bedingten Ablauf von @ alex modifizieren. Diese

ist, was ich warf zusammen:

if (months > 1) { 
    // Simple date/time 
    if (weeks >3) { 
     // Almost another month - fuzzy 
     months++; 
    } 
    return [NSString stringWithFormat:@"%ld months ago", (long)months]; 
} 
else if (months == 1) { 
    if (weeks > 3) { 
     months++; 
     // Almost 2 months 
     return [NSString stringWithFormat:@"%ld months ago", (long)months]; 
    } 
    // approx 1 month 
    return [NSString stringWithFormat:@"1 month ago"]; 
} 
// Weeks 
else if (weeks > 1) { 
    if (days > 6) { 
     // Almost another month - fuzzy 
     weeks++; 
    } 
    return [NSString stringWithFormat:@"%ld weeks ago", (long)weeks]; 
} 
else if (weeks == 1 || 
     days > 6) { 
    if (days > 6) { 
     weeks++; 
     // Almost 2 weeks 
     return [NSString stringWithFormat:@"%ld weeks ago", (long)weeks]; 
    } 
    return [NSString stringWithFormat:@"1 week ago"]; 
} 
// Days 
else if (days > 1) { 
    if (hours > 20) { 
     days++; 
    } 
    return [NSString stringWithFormat:@"%ld days ago", (long)days]; 
} 
else if (days == 1) { 
    if (hours > 20) { 
     days++; 
     return [NSString stringWithFormat:@"%ld days ago", (long)days]; 
    } 
    return [NSString stringWithFormat:@"1 day ago"]; 
} 
// Hours 
else if (hours > 1) { 
    if (minutes > 50) { 
     hours++; 
    } 
    return [NSString stringWithFormat:@"%ld hours ago", (long)hours]; 
} 
else if (hours == 1) { 
    if (minutes > 50) { 
     hours++; 
     return [NSString stringWithFormat:@"%ld hours ago", (long)hours]; 
    } 
    return [NSString stringWithFormat:@"1 hour ago"]; 
} 
// Minutes 
else if (minutes > 1) { 
    return [NSString stringWithFormat:@"%ld minutes ago", (long)minutes]; 
} 
else if (minutes == 1) { 
    return [NSString stringWithFormat:@"1 minute ago"]; 
} 
else if (minutes < 1) { 
    return [NSString stringWithFormat:@"Just now"]; 
}