Como decompôo um NSTimeInterval em ano, meses, dias, horas, minutos e segundos no iPhone?

Eu tenho um intervalo de tempo que abrange anos e eu quero todos os componentes do tempo do ano para baixo para segundos.

Meu primeiro pensamento é dividir o intervalo de tempo inteiro por segundos em um ano, subtrair isso de um total de segundos em execução, dividir isso por segundos em um mês, subtrair esse do total em execução e assim por diante.

Isso parece complicado e eu li que sempre que você está fazendo algo que parece confuso, provavelmente existe um método embutido.

Existe?

Eu integrei o segundo método de Alex no meu código.

Está em um método chamado por um UIDatePicker na minha interface.

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

Quando defino o selecionador de data para uma data de 1 ano e 1 dia no passado, recebo:

1 anos 1 meses 1 dia 16 horas 0 minutos 20 segundos

que é de 1 mês e 16 horas de folga. Se eu definir o selecionador de data para 1 dia no passado, eu estou fora pelo mesmo valor.

Atualização : Eu tenho um aplicativo que calcula a sua idade em anos, dado o seu aniversário (definido a partir de um UIDatePicker), mas muitas vezes estava desligado. Isso prova que havia uma imprecisão, mas não consigo descobrir de onde vem, consegue?

Descrição breve

  1. Apenas outra abordagem para completar a resposta do JBRWilkinson, mas adicionando algum código. Também pode oferecer uma solução para o comentário de Alex Reynolds.

  2. Use o método NSCalendar:

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

    • “Retorna, como um object NSDateComponents usando componentes especificados, a diferença entre duas datas fornecidas”. (Da documentação da API).

  3. Crie 2 NSDate cuja diferença é o NSTimeInterval que você deseja dividir. (Se o seu NSTimeInterval vem da comparação de 2 NSDate, você não precisa fazer este passo, e você nem precisa do NSTimeInterval, apenas aplique as datas ao método NSCalendar).

  4. Obtenha suas citações do NSDateComponents

Código de amostra

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

Este código está ciente dos tempos de economia de luz do dia e outras possíveis coisas desagradáveis.

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

No iOS8 e acima, você pode usar o NSDateComponentsFormatter

Tem methods para converter diferença de tempo em seqüência formatada amigável.

 NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init]; formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull; NSLog(@"%@", [formatter stringFromTimeInterval:1623452]); 

Isto dá a saída – 2 semanas, 4 dias, 18 horas, 57 minutos, 32 segundos

Converta seu intervalo em um NSDate usando +dateWithIntervalSince1970 , obtenha os componentes de data fora disso usando o método -componentsFromDate NSCalendar .

Referência do SDK

Isso funciona para mim:

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

A principal diferença aqui é que você precisa ajustar para o fuso horário.

Ou existe o meu método de aula. Ele não aguenta anos, mas isso pode ser facilmente adicionado, embora seja melhor para pequenos timelaps como dias, horas e minutos. É preciso levar em conta os plurais e só mostra o que é necessário:

 +(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; } 
 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. ... 

Aqui está outra possibilidade, um pouco mais limpa:

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