2009-06-27 5 views
11

Me gustaría escribir un método de fecha difusa para calcular fechas en Objective-C para iPhone. Hay una explicación muy populares aquí:Algoritmo de fecha difusa en Objective-C

Calculate relative time in C#

Sin embargo, contiene argumentos que faltan. ¿Cómo podría usarse esto en Objective-C ?. Gracias.

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

Respuesta

23

Las fechas se representan en Cocoa utilizando la clase NSDate. Existe un método conveniente implementado en NSDate para obtener el delta en segundos entre dos instancias de fecha, timeIntervalSinceDate:. Se invoca una instancia NSDate, tomando otro objeto NSDate como argumento. Devuelve NSTimeInterval (que es un typedef para un doble), que es representativo del número de segundos entre las dos fechas.

Dado esto, sería bastante simple adaptar el código que ha dado anteriormente a un contexto Objective-C/Cocoa. Desde el delta calculado por NSDate se da en segundos, da dos fechas, fácilmente se podría adaptar el código anterior:

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

Esto luego sería llamado, pasando el inicio y final NSDate objetos como argumentos, y volvería una NSString con el intervalo de tiempo.

+0

Esto funciona muy bien. ¡Gracias! –

+3

El único problema con esta implementación es que no hace distinción entre las 24 horas del día y el día del calendario. es decir, si estoy comparando las 11:00 p.m. y las 2:00 p.m., la diferencia debe ser "ayer" no "hace 3 horas" Mire en NSCalendar y su clase complementaria NSDateComponents. – retainCount

1

Puede obtener el delta entre dos NSDate objetos utilizando el método timeIntervalSinceDate:. Eso te dará el delta en segundos.

A partir de eso, puede calcular los minutos/horas/días/polillas/años dividiendo por la cantidad apropiada.

1

Como alternativa, se puede evitar el error propensos aritmética del calendario, apoyándose en los componentes del calendario se puede tirar de la diferencia entre dos fechas :

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 clave es la configuración de las banderas de la unidad - esto le permite configurar las unidades de tiempo que desea que la fecha/hora para ser dividido en. Si solo quiere horas, debe establecer NSHourCalendarUnit, y ese valor seguirá aumentando a medida que las fechas se alejan, porque no hay una unidad más grande para comenzar a incrementar.

Una vez que tenga sus componentes, puede continuar con la lógica de su elección, quizás modificando el flujo condicional de @ alex.

Esto es lo que me tiró juntos:

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