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

fuzzygoat
źródło

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];
}
Matthias Bauch
źródło
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".

jrc
źródło
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
Scott Gardner
źródło
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;
}
Roshit
źródło
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
onmyway133
źródło
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));
Julian F. Weinert
źródło
4

Szybki 4

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

dawny:

DateComponentsFormatter().string(from: 59.0)
Kuba
źródło
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
Kenny Winker
źródło
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) ?? "";
}
Vahid Amiri
źródło
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];
Boris Gafurov
źródło
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];
    }
Mujtahid Akon
źródło
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];
}
Fonix
źródło