NSTimeInterval do HH: mm: ss?


85

Jeśli mam NSTimeInterval, który jest ustawiony na 200.0, czy istnieje sposób na przekonwertowanie go na 00:03:20, myślałem, że mógłbym zainicjować NSDate z nim, a następnie użyć NSDateFormatter przy użyciu HH: mm: ss. Moje pytanie brzmi: czy istnieje szybki sposób, aby to zrobić, czy też muszę sam podzielić numer i użyć [NSString stringWithFormat: %02d:%02d:%02d, myHour, myMin, mySec]?

Odpowiedzi:


198

Nie ma potrzeby używania NSDateFormatterani niczego innego niż dzielenie i modulo. NSTimeIntervaljest po prostu podwójnym zawierającym sekundy.

Szybki

func stringFromTimeInterval(interval: NSTimeInterval) -> String {
    let interval = Int(interval)
    let seconds = interval % 60
    let minutes = (interval / 60) % 60
    let hours = (interval / 3600)
    return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
}

Cel C

- (NSString *)stringFromTimeInterval:(NSTimeInterval)interval {
    NSInteger ti = (NSInteger)interval;
    NSInteger seconds = ti % 60;
    NSInteger minutes = (ti / 60) % 60;
    NSInteger hours = (ti / 3600);
    return [NSString stringWithFormat:@"%02ld:%02ld:%02ld", (long)hours, (long)minutes, (long)seconds];
}

Bardzo doceniane, właśnie o tym myślałem, ale chciałem tylko sprawdzić, czy nie brakuje mi czegoś, co jest już zawarte w pakiecie SDK iOS.
— fuzzygoat

1
Chcę zaznaczyć, że „:” działa jako separator tylko w języku angielskim i niektórych innych językach. Na przykład fiński używa „.” - w ten sposób „15.02.59”
— sgosha,

@sgosha, dokładnie, zwykle Apple ma tendencję do dostarczania dla nich elementów formatujących. Czy zamiast tego powinienem użyć zlokalizowanego ciągu do formatowania?
— Gerry Shaw,

20
to jest zła odpowiedź, ponieważ nie lokalizuje separatora (jak wspominają inne komentarze). używając NSDateCompomentsFormatter możesz poprawnie sformatować swój przedział czasu w jednej linii, np. NSDateComponentsFormatter().stringFromTimeInterval(NSTimeInterval(duration))w swift
— Ilias Karim

4
W celu lokalizacji i przyszłego sprawdzania poprawnym rozwiązaniem jest użycie NSDateComponentsFormatter zgodnie z opisem @jrc
— voidref,

102

W systemie iOS 8 użyj NSDateComponentsFormatter.

NSDateComponentsFormatter *dateComponentsFormatter = [[NSDateComponentsFormatter alloc] init];
NSLog(@"%@", [dateComponentsFormatter stringFromTimeInterval:200.0]);

wyświetla „3:20”.

NSDateComponentsFormatter *dateComponentsFormatter = [[NSDateComponentsFormatter alloc] init];
dateComponentsFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad;
dateComponentsFormatter.allowedUnits = (NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond);
NSLog(@"%@", [dateComponentsFormatter stringFromTimeInterval:200.0]);

wyświetla "0:03:20".


2
Niezła aktualizacja. Ale jeśli to naprawdę takie proste obliczenie, czy użycie tych obiektów nie byłoby ogromnym narzutem (o ile nie jest to format dynamiczny)?
— Julian F. Weinert

2
potrzebujesz formatowania, aby zapewnić poprawne wyświetlanie ciągu dla różnych lokalizacji itp.
— Max MacLeod

Naprawdę mały komentarz, ale dziwne jest podawanie przykładu funkcji C dla pytania ObjectiveC. Chyba warto zmienić go na format ObjectiveC?
— Rilakkuma

Uratowałam mnóstwo kodu! Tak go użyłem w OrangeIRC .
— ahyattdev

17

Odpowiedź w Swift 3 wersji onmyway133 :

import Foundation

func format(_ duration: TimeInterval) -> String {
    let formatter = DateComponentsFormatter()
    formatter.zeroFormattingBehavior = .pad
    formatter.allowedUnits = [.minute, .second]

    if duration >= 3600 {
        formatter.allowedUnits.insert(.hour)
    }

    return formatter.string(from: duration)!
}


print(format(12)) // 0:12
print(format(65)) // 1:05
print(format(1750)) // 29:10
print(format(3890)) // 1:04:50
print(format(45720)) // 12:42:00

Czy tworzenie DateComponentsFormatter jest kosztowne z punktu widzenia wydajności, na przykład DateFormatter lub NumberFormatter?
— Moshe

1
Zamiast sprawdzać, czy jest wartość godziny, ustawienie zeroFormattingBehaviorna [.dropLeading, .pad]może być lepszą opcją (mniej kodu).
— MaddTheSane

Komentarze wydają się nieprawidłowe. Ten kod powoduje: 00:12, 01:05, 29:10, 01:04:50, 12:42:00
— Jordan H

10

Kilka dodatkowych wierszy kodu, ale wydaje mi się, że użycie NSDateComponents da bardziej precyzyjną wartość.

- (NSString *)getTimeRepresentationFromDate:(NSDate *)iDate withTimeInterval:(NSTimeInterval)iTimeInterval {
    NSString *aReturnValue = nil;
    NSDate *aNewDate = [iDate dateByAddingTimeInterval:iTimeInterval]; 

    unsigned int theUnits = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
    NSCalendar *aCalender = [NSCalendar currentCalendar];
    NSDateComponents *aDateComponents = [aCalender components:theUnits fromDate:iDate toDate:aNewDate options:0];

    aReturnValue = [NSString stringWithFormat:@"%d:%d:%d", [aDateComponents hour], [aDateComponents minute], [aDateComponents second]];

    return aReturnValue;
}

Powyższa metoda wykorzystuje 2 daty, przy użyciu których tworzony jest TimeInterval. Możesz również przekazać [NSDate date] do parametru iDate jako wartość domyślną.
— Roszit

2
W jakim sensie jest to „dokładniejsze” niż metoda stosowana przez @ matthias-bauch?
— jb

1
dzięki tej metodzie otrzymujesz odstęp czasu w sekundach, godzinach, min tj. 60, podczas gdy odpowiedź @ matthiasa-baucha podaje w ujęciu dziesiętnym, na przykład jeśli poczekasz wystarczająco długo, zobaczysz 1 min 79 s zamiast 2 min 19 s
— Ashish Pisey

To znacznie lepsze rozwiązanie niż przyjęte rozwiązanie. Użycie znaku „:” nadal stanowi problem w przypadku internacjonalizacji, ale przynajmniej jest to solidne w przypadku dni przestępnych i granic czasu letniego.
— evanflash

@AshishPisey Operator modulo zapobiega czegokolwiek większym niż 59.
— Matthias Bauch

8

W Swift 2 , iOS 8+ . Dzięki temu wyświetlamy godzinę tylko wtedy, gdy jest to konieczne

func format(duration: NSTimeInterval) -> String {
  let formatter = NSDateComponentsFormatter()
  formatter.zeroFormattingBehavior = .Pad

  if duration >= 3600 {
    formatter.allowedUnits = [.Hour, .Minute, .Second]
  } else {
    formatter.allowedUnits = [.Minute, .Second]
  }

  return formatter.stringFromTimeInterval(duration) ?? ""
}

Więc masz

print(format(12)) // 0:12
print(format(65)) // 1:05
print(format(1750)) // 29:10
print(format(3890)) // 1:04:50
print(format(45720)) // 12:42:00

4
NSTimeInterval ti = 3667;
double hours = floor(ti / 60 / 60);
double minutes = floor((ti - (hours * 60 * 60)) / 60);
double seconds = floor(ti - (hours * 60 * 60) - (minutes * 60));

4

Szybki 4

DateComponentsFormatter().string(from: <# TimeInterval #>)

dawny:

DateComponentsFormatter().string(from: 59.0)

3

Aby „rozszerzyć” sugestię Matthiasa Baucha, w Swift uczyniłbym z tego obliczoną właściwość NSTimeInterval:

extension NSTimeInterval {
  var stringValue: String {
    let interval = Int(self)
    let seconds = interval % 60
    let minutes = (interval / 60) % 60
    let hours = (interval / 3600)
    return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
  }
}

Zaletą tego jest to, że jest on powiązany z NSTimeIntervaltypem, a nie z kontrolerem widoku lub gdziekolwiek indziej umieścisz tę funkcję. Aby użyć, podałbyś coś takiego:

let timeInterval = NSDate().timeIntervalSinceDate(start)        
self.elapsedTimeLabel.text = timeInterval.stringValue

Dzięki, Kenny! W moim przypadku potrzebowałem ich osobno, więc dodałem dodatkowe zmienne do twojego rozszerzenia: "var sekund: Int {let interwał = Int (self); let seconds = interval% 60; return seconds}" i tak dalej dla minut i godzin. Tak więc użycie będzie następujące: print ("wykorzystane minuty: (timeInterval.minutes)")
— Vitalii

3

Na podstawie odpowiedzi udzielonej przez @ onmyway133 jest to wersja Swift 4:

func format(duration: TimeInterval) -> String {
    let formatter = DateComponentsFormatter()
    formatter.zeroFormattingBehavior = .pad

    if duration >= 3600 {
        formatter.allowedUnits = [.hour, .minute, .second];
    } else {
        formatter.allowedUnits = [.minute, .second];
    }

    return formatter.string(from: duration) ?? "";
}

0

prosto z apple docs: w pliku h:

 @property(strong,nonatomic)NSDateComponentsFormatter *timerFormatter;

w pliku m

@synthesize timerFormatter;

- (void)viewDidLoad {
[super viewDidLoad];
NSDateComponentsFormatter *timerFormatter = [[NSDateComponentsFormatter alloc] init];
timerFormatter.unitsStyle = NSDateComponentsFormatterUnitsStylePositional; 
//10:59 Positional THIS ONE DOES NOT SEEM TO WORK iOS<9 WHEN <1Minute JUST SHOWS 01, 10m 59s Abbreviated, 10min 59sec Short, 10minutes 59seconds Full ...
timerFormatter.allowedUnits = NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond;
}

gdziekolwiek chcesz przekonwertować swój NSTimeInterval timeInterval na ciąg hh: mm: ss, zrób to:

NSString *txt=[timerFormatter stringFromTimeInterval:timeInterval];

0

Wydaje mi się, że część timera powinna zostać ograniczona. Ponieważ kod Matthiasa tworzył ten problem w ciągu kilku sekund, używam następującego nieco zmodyfikowanego kodu Matthiasa

    - (NSString *)stringFromTimeInterval:(NSTimeInterval)interval {
        int ti = (int) ceil(interval);
        int seconds = ti % 60;
        int minutes = (ti / 60) % 60;
        int hours = (ti / 3600);
        return [NSString stringWithFormat:@"%02d:%02d:%02d",hours, minutes, seconds];
    }

0

Cel C wersja onmyway133 za odpowiedź

- (NSString*) formatTimeInterval:(NSTimeInterval) timeInterval {

    NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];

    formatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorPad;

    if (timeInterval > 3600) {
        formatter.allowedUnits = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
    } else {
        formatter.allowedUnits = NSCalendarUnitMinute | NSCalendarUnitSecond;
    }

    return [formatter stringFromTimeInterval:timeInterval];
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.