2009-08-06 10 views
54

Mam przedział czasu, który obejmuje lata i chcę cały czas komponenty od roku do sekund.Jak rozbić NSTimeInterval na rok, miesiące, dni, godziny, minuty i sekundy na iPhonie?

Moja pierwsza myśl to liczba całkowita dzieląca przedział czasu przez sekundy w roku, odejmuj to od sumy sekund, podziel ją przez sekundy w miesiącu, odejmij od sumy całkowitej i tak dalej.

To wydaje się zawiłe i przeczytałem, że za każdym razem, gdy robisz coś, co wygląda na zawiłe, prawdopodobnie istnieje wbudowana metoda.

Czy jest?

Zintegrowałem drugą metodę Alexa z moim kodem.

Jest to metoda wywoływana przez UIDatePicker w moim interfejsie.

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

Kiedy ustawić wyboru daty do daty 1 rok i 1 dzień w przeszłości, uzyskać:

1 rok 1 miesiąc 1 dni 16 godzin 0 minut 20 sekund

co oznacza 1 miesiąc i 16 godzin. Jeśli ustawię Selektor daty na 1 dzień w przeszłości, wyłączam o tę samą kwotę.

Aktualizacja: Mam aplikację, która obliczy wiek w latach, biorąc pod uwagę twoje urodziny (zestaw z UIDatePicker), ale to było często off. To dowodzi, że była nieścisłość, ale nie mogę ustalić, skąd ona pochodzi, prawda?

+0

Jeśli ustawisz datę próbnika do jednego dnia temu, ruszasz przez jeden miesiąc i szesnaście godzin ? –

+0

tak, jeden miesiąc i szesnaście godzin. – willc2

+0

Jeśli wynik jest zawsze jednomiesięczny i szesnaście godzin wyłączony, niezależnie od wybranej wartości, odejmujesz ją od zmiennych, przesuwając lub musisz ocenić, skąd ten błąd pochodzi. Domyślam się, że coś jest nie tak z twoją przerwą, jeśli zawsze jesteś wyłączony przez tę kwotę. –

Odpowiedz

99

Krótki opis

  1. Kolejna podejście do ukończenia odpowiedź JBRWilkinson ale dodawanie kodu . Może również zaoferować rozwiązanie komentarza Alexa Reynoldsa.

  2. Zastosowanie NSCalendar metoda:

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

    • "Powroty jako NSDateComponents obiektu przy użyciu określonych składników, różnica między dwiema datami dostarczonych". (Z dokumentacji API).

  3. Utwórz 2 NSDate, którego różnica jest NSTimeInterval, którą chcesz podzielić.(Jeśli twoja NSTimeInterval pochodzi z porównania 2 NSDate, nie musisz wykonywać tego kroku i nie potrzebujesz nawet NSTimeInterval, tylko zastosuj daty do metody NSCalendar).

  4. Zabieraj cytaty z NSDateComponents

Przykładowy kod

// 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

Rzeczywisty kod roboczy, w przeciwieństwie do tekstu, jest doceniany. – willc2

+0

Dziękuję uprzejmie proszę pana. – Martin

+5

Ta metoda ma problem z przechodzeniem przez zmiany czasu letniego, wystąpi problem z 1 godziną bezpośrednio. – hokkuk

4

Konwersja interwał do korzystania z NSDate+dateWithIntervalSince1970, dostać składniki date z tym używając NSCalendar „s -componentsFromDate metody.

SDK Reference

+4

+1. Aby być nieco bardziej gadatliwym dla OP, nie można precyzyjnie przekonwertować przedziału czasu na te komponenty, trzeba wiedzieć, od czego zaczynasz (stąd dataWithIntervalFrom1970), ponieważ na przykład 29 dni to miesiąc, jeśli jesteś w lutym 1. (z wyjątkiem oczywiście lat biseksu), ale nie, jeśli jesteś na 1 stycznia ... – 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

Oto kolejna możliwość, nieco czystsze:

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

Dla przedziału czasowego 1 sekundy (sekundę temu), zwraca "rok -1, miesiąc 12, dzień 31, godzina 16, minuta 0, sekunda 1 ". – willc2

+0

To brzmi jak błąd. –

3

Działa to dla mnie:

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

Główną różnicą jest to, że trzeba dostosować dla strefy czasowej.

+1

+1 za niezapominanie strefy czasowej –

+3

-1 za zapomnienie czegokolwiek poza 24 godziną nie uda się – Jonny

3

Lub istnieje moja metoda klasy. Nie radzi sobie wiele lat, ale można to łatwo dodać, choć jest to lepsze dla małych timelaps, takich jak dni, godziny i minuty. Upłynąć pod uwagę liczbę mnogą i tylko pokazuje, co jest potrzebne:

+(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

Ten kod jest świadomy dni lekkich czasach oszczędności i ewentualnych innych nieprzyjemnych rzeczy.

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

Od systemów iOS 8 i powyżej można użyć NSDateComponentsFormatter

Ma metody konwersji różnicę czasu w przyjaznej dla użytkownika sformatowany ciąg.

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

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

Daje wyjście - 2 tygodnie, 4 dni, 18 godzin, 57 minuty, 32 sekundy

Powiązane problemy