2009-06-27 3 views
11

Je voudrais écrire une méthode de date floue pour calculer les dates en Objective-C pour iPhone. Il y a une explication populaire ici:Algorithme Fuzzy Date en Objective-C

Calculate relative time in C#

Cependant, il contient des arguments manquants. Comment cela pourrait-il être utilisé dans Objective-C ?. Merci.

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

Répondre

23

Les dates sont représentées dans Cocoa en utilisant la classe NSDate. Il existe une méthode pratique implémentée dans NSDate pour obtenir le delta en secondes entre deux instances de date, timeIntervalSinceDate:. Ceci est appelé sur une instance NSDate, en prenant comme argument un autre objet NSDate. Il renvoie un NSTimeInterval (qui est un typedef pour un double), ce qui est représentatif du nombre de secondes entre les deux dates. Compte tenu de cela, il serait assez simple d'adapter le code que vous avez donné ci-dessus à un contexte Objective-C/Cocoa. Étant donné que le delta calculé par NSDate est donnée en quelques secondes, étant donné deux dates, vous pouvez facilement adapter le code ci-dessus:

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

Ce serait alors appelé, passer le début et la fin NSDate objets comme arguments, et reviendriez un NSString avec l'intervalle de temps.

+0

Cela fonctionne à merveille. Merci! –

+3

Le seul problème avec cette implémentation est qu'il ne fait aucune distinction entre 24 heures par jour et un jour de calendrier. c'est-à-dire si je compare 23h00 et 02h00, la différence devrait être "hier" pas "3 heures auparavant" Regardez dans NSCalendar et sa classe NSDateComponents. – retainCount

1

Vous pouvez obtenir le delta entre deux NSDate objets en utilisant la méthode timeIntervalSinceDate:. Cela vous donnera le delta en quelques secondes. À partir de là, vous pouvez calculer les minutes/heures/jours/mois/années en divisant par la quantité appropriée.

1

Comme alternative, vous pouvez éviter l'erreur sujette arithmétique calendrier en se basant sur les composants de calendrier que vous pouvez tirer de la différence entre deux dates :

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

la clé est la mise en place de l'unité drapeaux - ce qui vous permet de définir les unités de temps vous voulez la date/heure à se décomposer en. Si vous voulez juste des heures, vous devez définir NSHourCalendarUnit, et cette valeur continuera à augmenter au fur et à mesure que vos dates s'éloigneront, car il n'y a pas de plus grande unité pour commencer à incrémenter. Une fois que vous avez vos composants, vous pouvez continuer avec la logique de votre choix, peut-être en modifiant le flux conditionnel de @ alex.

C'est ce que je jetais ensemble:

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