Cómo convertir un NSTimeInterval (segundos) en minutos

104

Tengo una cantidad de secondseso que pasó de cierto evento. Está almacenado en un NSTimeIntervaltipo de datos.

Quiero convertirlo en minutesy seconds.

Por ejemplo, tengo: "326,4" segundos y quiero convertirlo en la siguiente cadena: "5:26".

¿Cuál es la mejor forma de lograr este objetivo?

Gracias.

Ilya Suzdalnitski
fuente

Respuestas:

147

pseudocódigo:

minutes = floor(326.4/60)
seconds = round(326.4 - minutes * 60)
Brian Ramsay
fuente
90
Creo que también vale la pena mencionar que un NSTimerInterval es solo un typedef para double.
Armentage
4
Vea la respuesta de Albaregar: es una mejor manera de hacer esto (más código pero más flexible, mantenible)
benvolioT
1
Sin embargo, ese código es muy caro. Para una conversión rápida de una duración en segundos, esta respuesta es mejor.
SpacyRicochet
2
Tenga en cuenta que esta respuesta le dará cosas como 1:60. Mire la respuesta de StratFan, que está más cerca de la verdad. Pero retire el piso y la ronda y debería estar en casa libre.
InvulgoSoft
La respuesta de Mick MacCallum a continuación en el momento de escribir este comentario parece estar más actualizada y realizada mediante el uso de API nativa.
Tomasz Nazarenko
183

Breve descripción

  1. La respuesta de Brian Ramsay es más conveniente si solo desea convertir a minutos.
  2. Si desea Cocoa API, hágalo por usted y convierta su NSTimeInterval no solo en minutos, sino también en días, meses, semanas, etc., creo que este es un enfoque más genérico.
  3. Utilice el método NSCalendar:

    • (NSDateComponents *)components:(NSUInteger)unitFlags fromDate:(NSDate *)startingDate toDate:(NSDate *)resultDate options:(NSUInteger)opts

    • "Devuelve, como un objeto NSDateComponents utilizando componentes especificados, la diferencia entre dos fechas proporcionadas". De la documentación de la API.

  4. Cree 2 NSDate cuya diferencia sea el NSTimeInterval que desea convertir. (Si su NSTimeInterval proviene de comparar 2 NSDate, no es necesario que realice este paso y ni siquiera necesita NSTimeInterval).

  5. Obtenga sus cotizaciones de NSDateComponents

Código de muestra

// The time interval 
NSTimeInterval theTimeInterval = 326.4;

// Get the system calendar
NSCalendar *sysCalendar = [NSCalendar currentCalendar];

// Create the NSDates
NSDate *date1 = [[NSDate alloc] init];
NSDate *date2 = [[NSDate alloc] initWithTimeInterval:theTimeInterval sinceDate:date1]; 

// Get conversion to months, days, hours, minutes
unsigned int unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit;

NSDateComponents *conversionInfo = [sysCalendar components:unitFlags fromDate:date1  toDate:date2  options:0];

NSLog(@"Conversion: %dmin %dhours %ddays %dmoths",[conversionInfo minute], [conversionInfo hour], [conversionInfo day], [conversionInfo month]);

[date1 release];
[date2 release];

Problemas conocidos

  • Demasiado para una conversión, tienes razón, pero así es como funciona la API.
  • Mi sugerencia: si se acostumbra a administrar sus datos de tiempo con NSDate y NSCalendar, la API hará el trabajo duro por usted.
Albaregar
fuente
4
Definitivamente es una gran solución solo para hacer una pequeña conversión, pero se siente cómodo, modular y realmente flexible, vote por este código de muestra @Albaregar.
Rigo Vides
2
+1 Para una buena respuesta completa, sin embargo, este código es bastante caro.
Nick Weaver
1
+1 ... una advertencia. Tenga cuidado al usar este estilo de código si necesita redondear fracciones de segundo. El comportamiento predeterminado de - [Componentes NSCalendar: fromDate: toDate: options:] puede estropearse en el escenario donde el componente toDate está 31.5 segundos (por ejemplo) antes que fromDate - los componentes devueltos tendrán 31 en el campo de segundos. Hay un parámetro opcional de NSWrapCalendarComponents, pero a partir de la experimentación (limitada) que he hecho con esto, se ajusta al segundo más cercano en lugar de redondear hacia arriba o hacia abajo. Su experiencia puede ser diferente.
David Doyle
%ldsi está en una máquina de 64 bits.
Anoop Vaidya
5
Las personas que encuentren esto (como Nick Weaver) van a pensar que el código parece lento. No lo es. Estoy haciendo una aplicación de temporizador y acabo de probar la versión de Albaregar contra algo al estilo de Brian Ramsey (y otros) ... y sorprendentemente, incluso encuestando 100 veces por segundo (que es una exageración tremenda) en un dispositivo relativamente lento (4S ) hubo menos de un 1% de diferencia en la utilización del procesador.
mmc
42

¡Todos estos parecen más complicados de lo que deberían ser! A continuación, se muestra una forma breve y sencilla de convertir un intervalo de tiempo en horas, minutos y segundos:

NSTimeInterval timeInterval = 326.4;
long seconds = lroundf(timeInterval); // Since modulo operator (%) below needs int or long

int hour = seconds / 3600;
int mins = (seconds % 3600) / 60;
int secs = seconds % 60;

Tenga en cuenta que cuando coloca un flotador en un int, obtiene floor () automáticamente, pero puede agregarlo a los dos primeros si lo hace sentir mejor :-)

Prasad
fuente
¡Increíble! Probé el código de Ramsay, aunque parece que se apaga por un segundo.
uplearnedu.com
Le falta un punto y coma en su código, pero no me deja editarlo para solucionarlo.
Jeef
Creo que su enfoque es el mejor: simple, hace el trabajo y "ligero" en los tics. Perfecto.
BonanzaDriver
29

Perdóname por ser virgen de Stack ... No estoy seguro de cómo responder a la respuesta de Brian Ramsay ...

Usar round no funcionará para los segundos valores entre 59.5 y 59.99999. El segundo valor será 60 durante este período. Utilice trunc en su lugar ...

 double progress;

 int minutes = floor(progress/60);
 int seconds = trunc(progress - minutes * 60);
StratFan
fuente
1
En realidad, solo quite el piso y el maletero / redondo por completo. :)
InvulgoSoft
@StratFan ¿Puede agregar horas a su respuesta?
Shmidt
27

Si su objetivo es iOS 8 o OS X 10.10 o superior, esto es mucho más fácil. La nueva NSDateComponentsFormatterclase le permite convertir un NSTimeIntervalvalor dado de su valor en segundos a una cadena localizada para mostrársela al usuario. Por ejemplo:

C objetivo

NSTimeInterval interval = 326.4;

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

componentFormatter.unitsStyle = NSDateComponentsFormatterUnitsStylePositional;
componentFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorDropAll;

NSString *formattedString = [componentFormatter stringFromTimeInterval:interval];
NSLog(@"%@",formattedString); // 5:26

Rápido

let interval = 326.4

let componentFormatter = NSDateComponentsFormatter()

componentFormatter.unitsStyle = .Positional
componentFormatter.zeroFormattingBehavior = .DropAll

if let formattedString = componentFormatter.stringFromTimeInterval(interval) {
    print(formattedString) // 5:26
}

NSDateCompnentsFormattertambién permite que esta salida esté en formas más largas. Puede encontrar más información en el artículo NSFormatter de NSHipster . Y dependiendo de las clases con las que ya esté trabajando (si no NSTimeInterval), puede ser más conveniente pasar al formateador una instancia de NSDateComponents, o dos NSDateobjetos, lo que también se puede hacer a través de los siguientes métodos.

C objetivo

NSString *formattedString = [componentFormatter stringFromDate:<#(NSDate *)#> toDate:<#(NSDate *)#>];
NSString *formattedString = [componentFormatter stringFromDateComponents:<#(NSDateComponents *)#>];

Rápido

if let formattedString = componentFormatter.stringFromDate(<#T##startDate: NSDate##NSDate#>, toDate: <#T##NSDate#>) {
    // ...
}

if let formattedString = componentFormatter.stringFromDateComponents(<#T##components: NSDateComponents##NSDateComponents#>) {
    // ...
}
Mick MacCallum
fuente
Esta es la forma correcta de crear la cadena, adecuadamente localizada
SwiftArchitect
1
Esta respuesta es mucho más "Objective-C" y útil que la respuesta aceptada. No es necesario switcho if elsedeclaraciones con esto. NSDateComponentsFormatterse encarga de todo. Horas, minutos, segundos impresos sin preocupaciones.
rustyMagnet
17

Código de Brian Ramsay, des-pseudofied:

- (NSString*)formattedStringForDuration:(NSTimeInterval)duration
{
    NSInteger minutes = floor(duration/60);
    NSInteger seconds = round(duration - minutes * 60);
    return [NSString stringWithFormat:@"%d:%02d", minutes, seconds];
}
Yang Meyer
fuente
Esto no se localiza
SwiftArchitect
7

Aquí hay una versión Swift:

func durationsBySecond(seconds s: Int) -> (days:Int,hours:Int,minutes:Int,seconds:Int) {
    return (s / (24 * 3600),(s % (24 * 3600)) / 3600, s % 3600 / 60, s % 60)
}

Puede usarse así:

let (d,h,m,s) = durationsBySecond(seconds: duration)
println("time left: \(d) days \(h) hours \(m) minutes \(s) seconds")
Jochen Bedersdorfer
fuente
6
    NSDate *timeLater = [NSDate dateWithTimeIntervalSinceNow:60*90];

    NSTimeInterval duration = [timeLater  timeIntervalSinceNow];

    NSInteger hours = floor(duration/(60*60));
    NSInteger minutes = floor((duration/60) - hours * 60);
    NSInteger seconds = floor(duration - (minutes * 60) - (hours * 60 * 60));

    NSLog(@"timeLater: %@", [dateFormatter stringFromDate:timeLater]);

    NSLog(@"time left: %d hours %d minutes  %d seconds", hours,minutes,seconds);

Salidas:

timeLater: 22:27
timeLeft: 1 hours 29 minutes  59 seconds
polarware
fuente
es un ejemplo realmente perfecto para encubrir una cita
ravinder521986
5

Dado que es esencialmente un doble ...

Dividir por 60.0 y extraer la parte integral y la parte fraccionaria.

La parte integral será el número entero de minutos.

Multiplica la parte fraccionaria por 60.0 nuevamente.

El resultado serán los segundos restantes.


fuente
3

Recuerde que la pregunta original se trata de una salida de cadena, no de pseudocódigo o componentes de cadena individuales.

Quiero convertirlo en la siguiente cadena: "5:26".

A muchas respuestas les faltan los problemas de internacionalización y la mayoría hace los cálculos matemáticos a mano. Todo tan del siglo XX ...

No hagas las matemáticas tú mismo (Swift 4)

let timeInterval: TimeInterval = 326.4
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.unitsStyle = .positional
if let formatted = dateComponentsFormatter.string(from: timeInterval) {
    print(formatted)
}

5:26


Aprovechar las bibliotecas

Si realmente desea componentes individuales y un código agradablemente legible, consulte SwiftDate :

import SwiftDate
...
if let minutes = Int(timeInterval).seconds.in(.minute) {
    print("\(minutes)")
}

5


Créditos a @mickmaccallum y @polarwar por el uso adecuado deDateComponentsFormatter

SwiftArquitecto
fuente
0

Cómo hice esto en Swift (incluido el formato de cadena para mostrarlo como "01:23"):

let totalSeconds: Double = someTimeInterval
let minutes = Int(floor(totalSeconds / 60))
let seconds = Int(round(totalSeconds % 60))        
let timeString = String(format: "%02d:%02d", minutes, seconds)
NSLog(timeString)
SilentDirge
fuente
0

Versión Swift 2

extension NSTimeInterval {
            func toMM_SS() -> String {
                let interval = self
                let componentFormatter = NSDateComponentsFormatter()

                componentFormatter.unitsStyle = .Positional
                componentFormatter.zeroFormattingBehavior = .Pad
                componentFormatter.allowedUnits = [.Minute, .Second]
                return componentFormatter.stringFromTimeInterval(interval) ?? ""
            }
        }
    let duration = 326.4.toMM_SS()
    print(duration)    //"5:26"
SPatel
fuente