NSTimeInterval a HH: mm: ss?

85

Si tengo un NSTimeInterval que está configurado para decir 200.0, ¿hay alguna manera de convertirlo en 00:03:20? Estaba pensando que podría inicializar un NSDate con él y luego usar NSDateFormatter usando HH: mm: ss. Mi pregunta es, ¿hay una manera rápida de hacer esto o tengo que dividir el número yo mismo y usarlo [NSString stringWithFormat: %02d:%02d:%02d, myHour, myMin, mySec]?

cabra peluda
fuente

Respuestas:

198

No es necesario utilizar NSDateFormatternada más que división y módulo. NSTimeIntervales solo un doble que contiene segundos.

Rápido

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

C objetivo

- (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
fuente
Muy apreciado, eso es lo que estaba pensando, pero solo quería comprobar que no me faltaba algo ya incluido como parte del SDK de iOS.
fuzzygoat
1
Quiero señalar que ":" funciona como delimitador solo en inglés y en algunos otros idiomas. El finlandés, por ejemplo, usa "." - como este "15.02.59"
sgosha
@sgosha, exactamente, normalmente Apple tiende a proporcionar formateadores para estos. ¿Debería utilizar en su lugar una cadena localizada para formatear?
Gerry Shaw
20
esta es la respuesta incorrecta ya que no localiza el delimitador (como mencionan otros comentarios). usando NSDateCompomentsFormatter puede formatear correctamente su intervalo de tiempo en una sola línea, por ejemplo, NSDateComponentsFormatter().stringFromTimeInterval(NSTimeInterval(duration))en rápido
Ilias Karim
4
Para la localización y pruebas futuras, la solución correcta es usar NSDateComponentsFormatter como lo describe @jrc
voidref
102

En iOS 8, use NSDateComponentsFormatter.

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

salidas "3:20".

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

salidas "0:03:20".

jrc
fuente
2
Buena actualización. Pero si realmente es un cálculo tan simple, ¿no sería una gran sobrecarga usar estos objetos (siempre que no haya un formato dinámico involucrado)?
Julian F. Weinert
2
necesita el formato para asegurarse de que la cadena aparezca correctamente para diferentes configuraciones regionales, etc.
Max MacLeod
Comentario realmente pequeño, pero es extraño dar un ejemplo de función C para la pregunta ObjectiveC. ¿Probablemente valga la pena cambiarlo al formato ObjectiveC?
Rilakkuma
¡Me ahorró un montón de código! Así es como lo usé para OrangeIRC .
ahyattdev
17

Versión Swift 3 de la respuesta de 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
fuente
¿Crear un DateComponentsFormatter es costoso en términos de rendimiento, como DateFormatter o NumberFormatter?
Moshe
1
En lugar de comprobar si hay un valor de hora, el establecimiento de zeroFormattingBehaviorser [.dropLeading, .pad]podría ser una opción mejor (menos código).
MaddTheSane
Los comentarios parecen ser incorrectos. Este código da como resultado: 00:12, 01:05, 29:10, 01:04:50, 12:42:00
Jordan H
10

Algunas líneas adicionales de código, pero creo que usar NSDateComponents dará un valor más 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;
}
Roshit
fuente
El método anterior utiliza las 2 fechas con las que se crea TimeInterval. También puede pasar [NSDate date] al parámetro iDate como valor predeterminado.
Roshit
2
¿De qué manera es este "más preciso" que el método utilizado por @ matthias-bauch?
jb
1
con este método, obtienes un intervalo de tiempo en segundos, horas, minutos, es decir, en términos de 60, mientras que la respuesta de @ matthias-bauch te da en términos decimales, por ejemplo, si esperas el tiempo suficiente, verás 1 min 79 seg en lugar de 2 min 19 seg
Ashish Pisey
Esta es una solución mucho mejor que la solución aceptada. El uso de ":" sigue siendo un problema para la internacionalización, pero al menos esto es sólido en los días bisiestos y los límites del horario de verano.
evanflash
@AshishPisey El operador de módulo evita cualquier cosa mayor que 59.
Matthias Bauch
8

En Swift 2 , iOS 8+ . Esto asegura que solo mostramos la hora cuando sea necesario

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

Así que tienes

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
fuente
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
fuente
4

Rápido 4

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

ex:

DateComponentsFormatter().string(from: 59.0)
Jakub
fuente
3

Para "extender" la sugerencia de Matthias Bauch, en Swift haría de esto una propiedad calculada de 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)
  }
}

La ventaja de esto es que está adjunto al NSTimeIntervaltipo, no a su controlador de vista o donde sea que coloque esa función. Para usarlo, irías algo como:

let timeInterval = NSDate().timeIntervalSinceDate(start)        
self.elapsedTimeLabel.text = timeInterval.stringValue
Kenny Winker
fuente
¡Gracias, Kenny! En mi caso, los necesitaba por separado, así que agregué variables adicionales a su extensión: "var seconds: Int {let interval = Int (self); let seconds = interval% 60; return seconds}" y así sucesivamente durante minutos y horas. De modo que ese uso será: print ("minutos dedicados: (timeInterval.minutes)")
Vitalii
3

Según la respuesta de @ onmyway133, aquí está la versión 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
fuente
0

directamente de los documentos de Apple: en el archivo h:

 @property(strong,nonatomic)NSDateComponentsFormatter *timerFormatter;

en archivo 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;
}

donde sea que necesite convertir su NSTimeInterval timeInterval a hh: mm: ss string, haga esto:

NSString *txt=[timerFormatter stringFromTimeInterval:timeInterval];
Boris Gafurov
fuente
0

Supongo que la fracción del temporizador debería cerrarse. Como el código de Matthias estaba creando ese problema en segundos, utilizo lo siguiente ligeramente modificado del de 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];
    }
Mujtahid Akon
fuente
0

Versión Objective C de la respuesta de 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];
}
Fonix
fuente