NSTimeInterval para HH: mm: ss?

85

Se eu tiver um NSTimeInterval definido como 200.0, há uma maneira de convertê-lo em 00:03:20, pensei que poderia inicializar um NSDate com ele e usar NSDateFormatter usando HH: mm: ss. Minha pergunta é: existe uma maneira rápida de fazer isso ou eu mesmo preciso dividir o número e usar [NSString stringWithFormat: %02d:%02d:%02d, myHour, myMin, mySec]?

cabra peluda
fonte

Respostas:

198

Não há necessidade de usar NSDateFormatterou qualquer outra coisa senão divisão e módulo. NSTimeIntervalé apenas um duplo contendo 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)
}

Objective-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
fonte
Muito apreciado, era isso que eu estava pensando, mas só queria verificar se não estava faltando algo já incluído como parte do SDK do iOS.
fuzzygoat
1
Quero ressaltar que ":" funciona como um delimitador apenas em inglês e em alguns outros idiomas. O finlandês, por exemplo, usa "." - assim "15.02.59"
sgosha
@sgosha, exatamente, normalmente a Apple tende a fornecer formatadores para estes. Em vez disso, devo usar uma string localizada para formatação?
Gerry Shaw de
20
esta é a resposta errada, pois não localiza o delimitador (como outros comentários mencionam). usando NSDateCompomentsFormatter você pode formatar corretamente seu intervalo de tempo em uma única linha, por exemplo, NSDateComponentsFormatter().stringFromTimeInterval(NSTimeInterval(duration))em swift
Ilias Karim
4
Para localização e verificação futura, a solução correta é usar NSDateComponentsFormatter conforme descrito por @jrc
voidref
103

No iOS 8, use NSDateComponentsFormatter.

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

emite "3:20".

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

emite "0:03:20".

jrc
fonte
2
Boa atualização. Mas se for realmente um cálculo tão simples, o uso desses objetos não representaria uma sobrecarga enorme (contanto que não houvesse nenhum formato dinâmico envolvido)?
Julian F. Weinert
2
você precisa da formatação para garantir que a string apareça corretamente para diferentes locais, etc.
Max MacLeod
Comentário muito pequeno, mas é estranho dar um exemplo de função C para a questão ObjectiveC. Provavelmente vale a pena mudá-lo para o formato ObjectiveC?
Rilakkuma
Me salvou uma tonelada de código! Foi assim que o usei para o OrangeIRC .
ahyattdev
17

Versão Swift 3 da resposta do 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
fonte
A criação de DateComponentsFormatter é dispendiosa em termos de desempenho, como DateFormatter ou NumberFormatter?
Moshe
1
Em vez de verificar se há um valor de hora, definir zeroFormattingBehaviorcomo ser [.dropLeading, .pad]pode ser uma opção melhor (menos código).
MaddTheSane
Os comentários parecem estar incorretos. Este código resulta em: 00:12, 01:05, 29:10, 01:04:50, 12:42:00
Jordan H
10

Algumas linhas extras de código, mas acho que usar NSDateComponents fornecerá um valor mais 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
fonte
O método acima usa as 2 datas com as quais o TimeInterval é criado. Você também pode passar [data NSDate] para o parâmetro iDate como um valor padrão.
Roshit
2
De que forma isso é "mais preciso" do que o método usado por @matthias-bauch?
jb
1
com este método, você obtém o intervalo de tempo em segundos, horas e minutos, ou seja, em termos de 60, enquanto a resposta de @matthias-bauch fornece em termos decimais, por exemplo, se você esperar o tempo suficiente, verá 1 min 79 seg em vez de 2 min 19 seg
Ashish Pisey
Esta é uma solução muito melhor do que a solução aceita. O uso de ":" ainda é um problema para a internacionalização, mas pelo menos isso é robusto em dias bissextos e limites de horário de verão.
evanflash
@AshishPisey O operador de módulo impede qualquer coisa maior que 59.
Matthias Bauch
8

Dentro Swift 2 , iOS 8+ . Isso garante que mostremos a hora apenas quando necessário

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

Então você tem

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

Swift 4

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

ex:

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

Para "estender" a sugestão de Matthias Bauch, em Swift eu faria isso uma propriedade computada 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)
  }
}

A vantagem disso é que ele está ligado ao NSTimeIntervaltipo, não ao seu controlador de visualização ou a qualquer outro lugar que você coloque essa função. Para usar, você faria algo como:

let timeInterval = NSDate().timeIntervalSinceDate(start)        
self.elapsedTimeLabel.text = timeInterval.stringValue
Kenny Winker
fonte
Obrigado, Kenny! No meu caso, eu precisava deles separadamente, então adicionei variáveis ​​adicionais à sua extensão: "var seconds: Int {let interval = Int (self); let seconds = interval% 60; return seconds}" e assim por diante para minutos e horas. Assim, esse uso será: print ("minutes lost: (timeInterval.minutes)")
Vitalii
3

Com base na resposta de @ onmyway133, aqui está a versão do 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
fonte
0

direto dos documentos da apple: no arquivo h:

 @property(strong,nonatomic)NSDateComponentsFormatter *timerFormatter;

no arquivo 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;
}

sempre que você precisar converter seu NSTimeInterval timeInterval para a string hh: mm: ss, faça o seguinte:

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

Eu acho que a fração do cronômetro deve ser limitada. Como o código de Matthias estava criando esse problema em segundos, eu uso o seguinte, ligeiramente modificado do 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
fonte
0

Versão Objective C da resposta 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
fonte