Da NSTimeInterval a HH: mm: ss?


85

Se ho un NSTimeInterval impostato su 200.0, c'è un modo per convertirlo in 00:03:20, stavo pensando di poter inizializzare un NSDate con esso e quindi utilizzare NSDateFormatter usando HH: mm: ss. La mia domanda è: c'è un modo rapido per farlo o devo suddividere il numero da solo e usarlo [NSString stringWithFormat: %02d:%02d:%02d, myHour, myMin, mySec]?

Risposte:


198

Non c'è bisogno di usare NSDateFormattero altro che divisione e modulo. NSTimeIntervalè solo un doppio che contiene secondi.

Swift

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

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

Molto apprezzato, questo è quello che stavo pensando, ma volevo solo controllare che non mi mancasse qualcosa già incluso come parte dell'SDK iOS.
fuzzygoat

1
Vorrei sottolineare che ":" funziona come delimitatore solo in inglese e in alcune altre lingue. Il finlandese, ad esempio, usa "." - come questo "15.02.59"
sgosha

@sgosha, esattamente, normalmente Apple tende a fornire formattatori per questi. Dovrei invece usare una stringa localizzata per la formattazione?
Gerry Shaw

20
questa è la risposta sbagliata poiché non localizza il delimitatore (come menzionano altri commenti). utilizzando NSDateCompomentsFormatter puoi formattare correttamente il tuo intervallo di tempo in una singola riga, ad esempio NSDateComponentsFormatter().stringFromTimeInterval(NSTimeInterval(duration))in swift
Ilias Karim

4
Per la localizzazione e la verifica futura, la soluzione corretta è utilizzare NSDateComponentsFormatter come descritto da @jrc
voidref

102

Su iOS 8, usa NSDateComponentsFormatter.

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

uscite "3:20".

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

emette "0:03:20".


2
Bel aggiornamento. Ma se fosse davvero solo un calcolo così semplice, l'utilizzo di questi oggetti non sarebbe un enorme sovraccarico (purché non sia coinvolto un formato dinamico)?
Julian F. Weinert

2
hai bisogno della formattazione per assicurarti che la stringa appaia correttamente per diverse impostazioni locali ecc.
Max MacLeod

Commento davvero minuscolo, ma è strano fornire un esempio di funzione C per la domanda ObjectiveC. Probabilmente vale la pena cambiarlo nel formato ObjectiveC?
Rilakkuma

Mi ha salvato un sacco di codice! Ecco come l'ho usato per OrangeIRC .
ahyattdev

17

La versione Swift 3 della risposta di 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

La creazione di un DateComponentsFormatter è costosa in termini di prestazioni, come DateFormatter o NumberFormatter?
Moshe

1
Invece di controllare se è presente un valore orario, l'impostazione zeroFormattingBehaviorsu [.dropLeading, .pad]potrebbe essere un'opzione migliore (meno codice).
MaddTheSane

I commenti sembrano essere errati. Questo codice risulta in: 00:12, 01:05, 29:10, 01:04:50, 12:42:00
Jordan H

10

Alcune righe di codice extra, ma credo che l'utilizzo di NSDateComponents darà un valore più preciso.

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

Il metodo precedente utilizza le 2 date utilizzando le quali viene creato TimeInterval. Puoi anche passare [Data NSDate] al parametro iDate come valore predefinito.
Roshit

2
In che modo questo metodo è "più preciso" del metodo usato da @ matthias-bauch?
jb

1
con questo metodo ottieni l'intervallo di tempo in secondi, ore, min cioè in termini di 60 mentre la risposta di @ matthias-bauch ti dà in termini decimali per esempio se aspetti un tempo sufficiente vedrai 1 min 79 sec invece di 2 min 19 sec
Ashish Pisey

Questa è una soluzione molto migliore rispetto alla soluzione accettata. L'uso di ":" è ancora un problema per l'internazionalizzazione, ma almeno questo è robusto nei giorni bisestili e nei confini dell'ora legale.
evanflash

@AshishPisey L'operatore modulo impedisce qualsiasi cosa più grande di 59.
Matthias Bauch

8

In Swift 2 , iOS 8+ . Questo ci assicura di mostrare l'ora solo quando necessario

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) ?? ""
}

Quindi l'hai fatto

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

Swift 4

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

ex:

DateComponentsFormatter().string(from: 59.0)

3

Per "estendere" il suggerimento di Matthias Bauch, in Swift vorrei rendere questa una proprietà calcolata di 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)
  }
}

Il vantaggio di questo è che è collegato al NSTimeIntervaltipo, non al controller della vista o ovunque tu metta quella funzione. Per usarlo dovresti andare qualcosa come:

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

Grazie, Kenny! Nel mio caso ne avevo bisogno separatamente, quindi ho aggiunto ulteriori variabili alla tua estensione: "var seconds: Int {let interval = Int (self); let seconds = interval% 60; return seconds}" e così via per minuti e ore. Quindi tale utilizzo sarà: print ("minuti spesi: (timeInterval.minutes)")
Vitalii

3

Sulla base della risposta di @ onmyway133 ecco la versione di 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

direttamente da documenti Apple: nel file h:

 @property(strong,nonatomic)NSDateComponentsFormatter *timerFormatter;

nel file 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;
}

ovunque tu abbia bisogno di convertire il tuo NSTimeInterval timeInterval in hh: mm: ss stringa, fai questo:

NSString *txt=[timerFormatter stringFromTimeInterval:timeInterval];

0

Immagino che la frazione del timer dovrebbe essere chiusa. Poiché il codice di Matthias stava creando quel problema in pochi secondi, utilizzo il seguente leggermente modificato da quello di Matthias

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

Versione dell'obiettivo C della risposta di onmyway133

- (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];
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.