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.