Como converter um NSTimeInterval (segundos) em minutos


104

Recebi uma parte secondsdisso de um determinado evento. É armazenado em um NSTimeIntervaltipo de dados.

Quero convertê-lo em minutese seconds.

Por exemplo, tenho: "326,4" segundos e desejo convertê-lo na seguinte string: "5:26".

Qual é a melhor forma de atingir esse objetivo?

Obrigado.

Respostas:


147

pseudo-código:

minutes = floor(326.4/60)
seconds = round(326.4 - minutes * 60)

90
Acho que também vale a pena mencionar que um NSTimerInterval é apenas um typedef para double.
Armentage

4
Veja a resposta de Albaregar - é a melhor maneira de fazer isso (mais código, mas mais flexível, de
fácil

1
Esse código é muito caro, no entanto. Para uma conversão rápida de duração em segundos, essa resposta é melhor.
SpacyRicochet

2
Observe que esta resposta fornecerá coisas como 1:60. Veja a resposta da StratFan, que está mais perto da verdade. Mas remova o chão e as redondas e você estará em casa.
InvulgoSoft

A resposta de Mick MacCallum abaixo no momento da redação deste comentário parece ser a mais atualizada e feita por meio do uso de API nativa.
Tomasz Nazarenko

183

Descrição breve

  1. A resposta de Brian Ramsay é mais conveniente se você quiser apenas converter para minutos.
  2. Se você quiser a API Cocoa, faça isso por você e converta seu NSTimeInterval não apenas em minutos, mas também em dias, meses, semanas, etc ... Acho que esta é uma abordagem mais genérica
  3. Use o método NSCalendar:

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

    • "Retorna, como um objeto NSDateComponents usando componentes especificados, a diferença entre duas datas fornecidas". Da documentação da API.

  4. Crie 2 NSDate cuja diferença é o NSTimeInterval que você deseja converter. (Se o seu NSTimeInterval vem da comparação de 2 NSDate, você não precisa fazer esta etapa e nem mesmo precisa do NSTimeInterval).

  5. Obtenha suas cotações de NSDateComponents

Código de amostra

// 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 conhecidos

  • Demais para apenas uma conversão, você tem razão, mas é assim que a API funciona.
  • Minha sugestão: se você se acostumar a gerenciar seus dados de tempo usando NSDate e NSCalendar, a API fará o trabalho pesado para você.

4
Definitivamente, é uma solução alternativa apenas para fazer uma pequena conversão, mas parece confortável, modular e realmente flexível, vote por este código de amostra @Albaregar.
Rigo Vides

2
+1 Para uma boa resposta completa, no entanto, este código é bastante caro.
Nick Weaver

1
+1 ... uma advertência. Tenha cuidado ao usar esse estilo de código se precisar arredondar segundos fracionários. O comportamento padrão de - [componentes NSCalendar: fromDate: toDate: options:] pode bagunçar no cenário onde o componente toDate está 31,5 segundos (por exemplo) à frente do fromDate - os componentes retornados terão 31 no campo de segundos. Há um parâmetro opcional de NSWrapCalendarComponents, mas pela experimentação (limitada) que fiz com isso, ele quebra para o segundo mais próximo, em vez de arredondar para cima ou para baixo. Sua milhagem pode variar.
David Doyle,

%ldse você estiver em uma máquina de 64 bits.
Anoop Vaidya

5
As pessoas que encontrarem isso (como Nick Weaver) vão pensar que o código parece lento. Não é. Estou fazendo um aplicativo de cronômetro e acabei de testar a versão de Albaregar contra algo no estilo de Brian Ramsey (e outros) ... e surpreendentemente, até mesmo pesquisando 100 vezes por segundo (o que é um tremendo exagero) em um dispositivo relativamente lento (4S ) havia menos de 1% de diferença na utilização do processador.
mmc de

42

Tudo isso parece mais complicado do que precisa ser! Esta é uma maneira curta e agradável de converter um intervalo de tempo em horas, minutos e 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;

Observe que quando você coloca um float em um int, você obtém floor () automaticamente, mas pode adicioná-lo aos dois primeiros se se sentir melhor :-)


Impressionante! Tentei o código de Ramsay, embora pareça estar fora do ar por um segundo.
uplearnedu.com

Está faltando um ponto-e-vírgula em seu código, mas ele não me deixou editá-lo para corrigi-lo.
Jeef

Eu acho que sua abordagem é a melhor - simples, faz o trabalho e "leve" os carrapatos. Perfeito.
BonanzaDriver

29

Perdoe-me por ser uma virgem Stack ... Não tenho certeza de como responder à resposta de Brian Ramsay ...

O uso de arredondamento não funcionará para os segundos valores entre 59,5 e 59,99999. O segundo valor será 60 durante este período. Em vez disso, use trunc ...

 double progress;

 int minutes = floor(progress/60);
 int seconds = trunc(progress - minutes * 60);

1
Na verdade, apenas remova o chão e o porta-malas / redondo completamente. :)
InvulgoSoft

@StratFan Você pode adicionar horas à sua resposta?
Shmidt

27

Se você tem como alvo o iOS 8 ou o OS X 10.10 ou superior, isso ficou muito mais fácil. A nova NSDateComponentsFormatterclasse permite que você converta um dado NSTimeIntervalde seu valor em segundos em uma string localizada para mostrar ao usuário. Por exemplo:

Objective-C

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
}

NSDateCompnentsFormattertambém permite que essa saída seja em formatos mais longos. Mais informações podem ser encontradas no artigo NSFormatter do NSHipster . E dependendo das classes com as quais você já está trabalhando (se não NSTimeInterval), pode ser mais conveniente passar ao formatador uma instância de NSDateComponents, ou dois NSDateobjetos, o que também pode ser feito por meio dos métodos a seguir.

Objective-C

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#>) {
    // ...
}

Esta é a maneira correta de criar a string, adequadamente localizada
SwiftArchitect de

1
Esta resposta é muito mais "Objective-C" e útil do que a resposta aceita. Você não precisa switchou if elsedeclarações com isso. NSDateComponentsFormattercuida de tudo. Horas, minutos, segundos impressos sem qualquer preocupação.
rustyMagnet

17

Código de Brian Ramsay, despseudificado:

- (NSString*)formattedStringForDuration:(NSTimeInterval)duration
{
    NSInteger minutes = floor(duration/60);
    NSInteger seconds = round(duration - minutes * 60);
    return [NSString stringWithFormat:@"%d:%02d", minutes, seconds];
}

Isso não localiza
SwiftArchitect de

7

Esta é uma versão do 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)
}

Pode ser usado assim:

let (d,h,m,s) = durationsBySecond(seconds: duration)
println("time left: \(d) days \(h) hours \(m) minutes \(s) seconds")

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

Saídas:

timeLater: 22:27
timeLeft: 1 hours 29 minutes  59 seconds

é um exemplo realmente perfeito para ocultar uma data
ravinder521986,

5

Uma vez que é essencialmente um duplo ...

Divida por 60,0 e extraia a parte integral e a parte fracionária.

A parte integrante será o número inteiro de minutos.

Multiplique a parte fracionária por 60,0 novamente.

O resultado serão os segundos restantes.


3

Lembre-se de que a questão original é sobre uma saída de string, não pseudocódigo ou componentes de string individuais.

Quero convertê-lo na seguinte string: "5:26"

Muitas respostas estão perdendo os problemas de internacionalização e a maioria fazendo os cálculos matemáticos à mão. Tudo tão século 20 ...

Não faça a matemática sozinho (Swift 4)

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

5:26


Aproveite as bibliotecas

Se você realmente deseja componentes individuais e um código de fácil leitura, verifique SwiftDate :

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

5


Créditos para @mickmaccallum e @polarwar pelo uso adequado deDateComponentsFormatter


0

Como fiz isso em Swift (incluindo a formatação da string para mostrar 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)

0

Versão 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"
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.