2009-08-06 22 views
54

Tengo un intervalo de tiempo que abarca años y quiero componentes todo el tiempo del año a segundos.¿Cómo se descompone un NSTimeInterval en año, meses, días, horas, minutos y segundos en el iPhone?

Mi primer pensamiento es dividir enteros el intervalo de tiempo por segundos en un año, restarlo de un total acumulado de segundos, dividirlo por segundos en un mes, restarlo del total acumulado, y así sucesivamente.

Eso parece enrevesado y he leído que cada vez que haces algo que parece enrevesado, probablemente haya un método incorporado.

¿Lo hay?

Integré el segundo método de Alex en mi código.

Está en un método llamado por un UIDatePicker en mi interfaz.

NSDate *now = [NSDate date]; 
NSDate *then = self.datePicker.date; 
NSTimeInterval howLong = [now timeIntervalSinceDate:then]; 

NSDate *date = [NSDate dateWithTimeIntervalSince1970:howLong]; 
NSString *dateStr = [date description]; 
const char *dateStrPtr = [dateStr UTF8String]; 
int year, month, day, hour, minute, sec; 

sscanf(dateStrPtr, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &sec); 
year -= 1970; 

NSLog(@"%d years\n%d months\n%d days\n%d hours\n%d minutes\n%d seconds", year, month, day, hour, minute, sec); 

Cuando me puse el selector de fecha a fecha de 1 año y 1 día en el pasado, me sale:

1 años 1 meses 1 días 16 horas 0 minutos 20 segundos

que tiene 1 mes y 16 horas de descanso. Si configuré el selector de fecha en 1 día en el pasado, estoy fuera de la misma cantidad.

actualización: Tengo una aplicación que calcula su edad en años, dada su cumpleaños (conjunto de una UIDatePicker), sin embargo, a menudo estaba fuera. Esto prueba que hubo una inexactitud, pero no puedo entender de dónde viene, ¿verdad?

+0

Si se establece el selector de fechas con hace un día, estás fuera por un mes y dieciséis horas ? –

+0

sí, un mes y dieciséis horas de descanso. – willc2

+0

Si el resultado es consistentemente un mes y dieciséis horas de descanso, independientemente del valor que elija, o bien resta eso de las variables, con desplazamiento, o necesita evaluar de dónde proviene ese error. Supongo que algo no está bien con tu intervalo, si siempre estás fuera de esa cantidad. –

Respuesta

99

Breve descripción

  1. Sólo otro enfoque para completar la respuesta de JBRWilkinson pero añadiendo algún código . También puede ofrecer una solución al comentario de Alex Reynolds.

  2. método Uso NSCalendar:

    • (NSDateComponents *)components:(NSUInteger)unitFlags fromDate:(NSDate *)startingDate toDate:(NSDate *)resultDate options:(NSUInteger)opts

    • "devuelve, como un NSDateComponents objeto utilizando componentes especificados, la diferencia entre dos fechas suministrados". (De la documentación API).

  3. crear 2 NSDate cuya diferencia es la NSTimeInterval desea romper.(Si su NSTimeInterval proviene de comparar 2 NSDate, no necesita hacer este paso, y ni siquiera necesita NSTimeInterval, solo aplique las fechas al método NSCalendar).

  4. Obtener sus citas de NSDateComponents

Código de ejemplo

// The time interval 
NSTimeInterval theTimeInterval = ...; 

// Get the system calendar 
NSCalendar *sysCalendar = [NSCalendar currentCalendar]; 

// Create the NSDates 
NSDate *date1 = [[NSDate alloc] init]; 
NSDate *date2 = [[NSDate alloc] initWithTimeInterval:theTimeInterval sinceDate:date1]; 

// Get conversion to months, days, hours, minutes 
NSCalendarUnit unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit; 

NSDateComponents *breakdownInfo = [sysCalendar components:unitFlags fromDate:date1 toDate:date2 options:0]; 
NSLog(@"Break down: %i min : %i hours : %i days : %i months", [breakdownInfo minute], [breakdownInfo hour], [breakdownInfo day], [breakdownInfo month]); 
+0

Se agradece el código de trabajo real, a diferencia del texto. – willc2

+0

Gracias amablemente señor. – Martin

+5

este método tiene un problema de pasar por los cambios de horario de verano, experimentará un problema de 1 hora directamente. – hokkuk

4

convertir su intervalo en un NSDate usando +dateWithIntervalSince1970, obtener los componentes de la fecha de esa -componentsFromDate método utilizando NSCalendar 's.

SDK Reference

+4

+1. Para ser un poco más detallado para el OP, no puede convertir con precisión un intervalo de tiempo a estos componentes, debe saber desde dónde comienza (por tanto, dateWithIntervalFrom1970), porque, por ejemplo, 29 días son un mes si está en febrero. Primero (excepto en años bisexibles por supuesto) pero no si está el 1 de enero ... – fraca7

2
NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval]; 

// format: YYYY-MM-DD HH:MM:SS ±HHMM 
NSString *dateStr = [date description]; 
NSRange range; 

// year 
range.location = 0; 
range.length = 4; 
NSString *yearStr = [dateStr substringWithRange:range]; 
int year = [yearStr intValue] - 1970; 

// month 
range.location = 5; 
range.length = 2; 
NSString *monthStr = [dateStr substringWithRange:range]; 
int month = [monthStr intValue]; 

// day, etc. 
... 
0

Aquí es otra posibilidad, un poco más limpio:

NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval]; 
NSString *dateStr = [date description]; 
const char *dateStrPtr = [dateStr UTF8String]; 

// format: YYYY-MM-DD HH:MM:SS ±HHMM 
int year, month, day, hour, minutes, seconds; 
sscanf(dateStrPtr, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minutes, &seconds); 
year -= 1970; 
+0

Para un intervalo de tiempo de 1 segundo (hace un segundo), devuelve "año -1, mes 12, día 31, hora 16, minuto 0, segundo 1 ". – willc2

+0

Eso suena como un error. –

3

Esto funciona para mí:

float *lenghInSeconds = 2345.234513; 
    NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:lenghInSeconds]; 
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 


    [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]]; 

    [formatter setDateFormat:@"HH:mm:ss"]; 
    NSLog(@"%@", [formatter stringFromDate:date]); 
    [formatter release]; 

La diferencia principal aquí es que es necesario ajustar para la zona horaria

+1

+1 para no olvidar la zona horaria –

+3

-1 por olvidar algo más allá de las 24 horas fallará – Jonny

3

O está mi método de clase. No maneja años, pero podría agregarse fácilmente, aunque es mejor para pequeños tiempos como días, horas y minutos. Se toma en cuenta los plurales y sólo muestra lo que se necesita:

+(NSString *)TimeRemainingUntilDate:(NSDate *)date { 

    NSTimeInterval interval = [date timeIntervalSinceNow]; 
    NSString * timeRemaining = nil; 

    if (interval > 0) { 

     div_t d = div(interval, 86400); 
     int day = d.quot; 
     div_t h = div(d.rem, 3600); 
     int hour = h.quot; 
     div_t m = div(h.rem, 60); 
     int min = m.quot; 

     NSString * nbday = nil; 
     if(day > 1) 
      nbday = @"days"; 
     else if(day == 1) 
      nbday = @"day"; 
     else 
      nbday = @""; 
     NSString * nbhour = nil; 
     if(hour > 1) 
      nbhour = @"hours"; 
     else if (hour == 1) 
      nbhour = @"hour"; 
     else 
      nbhour = @""; 
     NSString * nbmin = nil; 
     if(min > 1) 
      nbmin = @"mins"; 
     else 
      nbmin = @"min"; 

     timeRemaining = [NSString stringWithFormat:@"%@%@ %@%@ %@%@",day ? [NSNumber numberWithInt:day] : @"",nbday,hour ? [NSNumber numberWithInt:hour] : @"",nbhour,min ? [NSNumber numberWithInt:min] : @"00",nbmin]; 
    } 
    else 
     timeRemaining = @"Over"; 

    return timeRemaining; 
} 
0
- (NSString *)convertTimeFromSeconds:(NSString *)seconds { 

    // Return variable. 
    NSString *result = @""; 

    // Int variables for calculation. 
    int secs = [seconds intValue]; 
    int tempHour = 0; 
    int tempMinute = 0; 
    int tempSecond = 0; 

    NSString *hour  = @""; 
    NSString *minute = @""; 
    NSString *second = @""; 

    // Convert the seconds to hours, minutes and seconds. 
    tempHour = secs/3600; 
    tempMinute = secs/60 - tempHour * 60; 
    tempSecond = secs - (tempHour * 3600 + tempMinute * 60); 

    hour = [[NSNumber numberWithInt:tempHour] stringValue]; 
    minute = [[NSNumber numberWithInt:tempMinute] stringValue]; 
    second = [[NSNumber numberWithInt:tempSecond] stringValue]; 

    // Make time look like 00:00:00 and not 0:0:0 
    if (tempHour < 10) { 
     hour = [@"0" stringByAppendingString:hour]; 
    } 

    if (tempMinute < 10) { 
     minute = [@"0" stringByAppendingString:minute]; 
    } 

    if (tempSecond < 10) { 
     second = [@"0" stringByAppendingString:second]; 
    } 

    if (tempHour == 0) { 

     NSLog(@"Result of Time Conversion: %@:%@", minute, second); 
     result = [NSString stringWithFormat:@"%@:%@", minute, second]; 

    } else { 

     NSLog(@"Result of Time Conversion: %@:%@:%@", hour, minute, second); 
     result = [NSString stringWithFormat:@"%@:%@:%@",hour, minute, second]; 

    } 

    return result; 

} 
15

Este código es consciente de los tiempos de ahorro de luz diurna y otras cosas desagradables posibles.

NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; 
NSDateComponents *components = [gregorianCalendar components: (NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) 
                fromDate:startDate 
                 toDate:[NSDate date] 
                options:0]; 


NSLog(@"%ld", [components year]); 
NSLog(@"%ld", [components month]); 
NSLog(@"%ld", [components day]); 
NSLog(@"%ld", [components hour]); 
NSLog(@"%ld", [components minute]); 
NSLog(@"%ld", [components second]); 
4

De iOS8 y arriba se puede utilizar NSDateComponentsFormatter

Tiene métodos para convertir diferencia de tiempo en la cadena con formato de usuario amigable.

NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init]; 
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull; 

NSLog(@"%@", [formatter stringFromTimeInterval:1623452]); 

Esto le da a la salida - 2 semanas, 4 días, 18 horas, 57 minutos, 32 segundos

Cuestiones relacionadas