NSTimeInterval à HH: mm: ss?

85

Si j'ai un NSTimeInterval défini sur 200.0, y a-t-il un moyen de le convertir en 00:03:20, je pensais que je pourrais initialiser un NSDate avec lui, puis utiliser NSDateFormatter en utilisant HH: mm: ss. Ma question est la suivante: y a-t-il un moyen rapide de le faire ou dois-je diviser le nombre moi-même et l'utiliser [NSString stringWithFormat: %02d:%02d:%02d, myHour, myMin, mySec]?

fuzzygoat
la source

Réponses:

198

Pas besoin d'utiliser NSDateFormatterou autre chose que la division et le modulo. NSTimeIntervalest juste un double contenant des secondes.

Rapide

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

Objectif 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
la source
Très apprécié, c'est ce à quoi je pensais mais je voulais juste vérifier que je ne manquais pas quelque chose de déjà inclus dans le SDK iOS.
fuzzygoat
1
Je tiens à souligner que ":" fonctionne comme séparateur uniquement en anglais et dans certaines autres langues. Le finnois, par exemple, utilise "." - aime ça "15.02.59"
sgosha
@sgosha, exactement, normalement, Apple a tendance à fournir des formateurs pour ceux-ci. Dois-je plutôt utiliser une chaîne localisée pour le formatage?
Gerry Shaw
20
c'est la mauvaise réponse car elle ne localise pas le délimiteur (comme d'autres commentaires le mentionnent). en utilisant NSDateCompomentsFormatter, vous pouvez formater correctement votre intervalle de temps sur une seule ligne, par exemple NSDateComponentsFormatter().stringFromTimeInterval(NSTimeInterval(duration))en swift
Ilias Karim
4
Pour la localisation et la vérification future, la solution correcte est d'utiliser NSDateComponentsFormatter comme décrit par @jrc
voidref
102

Sur iOS 8, utilisez NSDateComponentsFormatter.

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

sorties "3:20".

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

renvoie "0:03:20".

jrc
la source
2
Bonne mise à jour. Mais s'il s'agit vraiment d'un calcul aussi simple, l'utilisation de ces objets ne serait-elle pas une surcharge (tant qu'il n'y a pas de format dynamique impliqué)?
Julian F. Weinert
2
vous avez besoin du formatage pour vous assurer que la chaîne apparaît correctement pour différents paramètres régionaux, etc.
Max MacLeod
Vraiment petit commentaire, mais il est bizarre de donner un exemple de fonction C pour une question ObjectiveC. Cela vaut probablement la peine de le changer au format ObjectiveC?
Rilakkuma
M'a sauvé une tonne de code! C'est ainsi que je l'ai utilisé pour OrangeIRC .
ahyattdev
17

Version Swift 3 de la réponse 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
la source
La création d'un DateComponentsFormatter est-il coûteux en termes de performances, comme un DateFormatter ou NumberFormatter?
Moshe
1
Au lieu de vérifier s'il y a une valeur d'heure, le réglage zeroFormattingBehaviorsur être [.dropLeading, .pad]pourrait être une meilleure option (moins de code).
MaddTheSane
Les commentaires semblent incorrects. Ce code donne les résultats suivants: 00:12, 01:05, 29:10, 01:04:50, 12:42:00
Jordan H
10

Quelques lignes de code supplémentaires, mais je pense que l'utilisation de NSDateComponents donnera une valeur plus précise.

- (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
la source
La méthode ci-dessus utilise les 2 dates à l'aide desquelles TimeInterval est créé. Vous pouvez également transmettre [NSDate date] au paramètre iDate comme valeur par défaut.
Roshit
2
En quoi est-ce "plus précis" que la méthode utilisée par @ matthias-bauch?
jb
1
avec cette méthode, vous obtenez un intervalle de temps en secondes, heures, min ie en termes de 60 tandis que la réponse de @ matthias-bauch vous donne en termes décimaux par exemple si vous attendez suffisamment de temps, vous verrez 1 min 79 sec au lieu de 2 min 19 sec
Ashish Pisey
C'est une bien meilleure solution que la solution acceptée. L'utilisation de «:» est toujours un problème pour l'internationalisation, mais au moins cela est robuste à travers les jours bissextiles et les limites de l'heure d'été.
evanflash
@AshishPisey L'opérateur modulo empêche tout ce qui est supérieur à 59.
Matthias Bauch
8

Dans Swift 2 , iOS 8+ . Cela garantit que nous ne montrons l'heure que lorsque cela est nécessaire

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

Alors tu as

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
la source
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
la source
4

Swift 4

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

ex:

DateComponentsFormatter().string(from: 59.0)
Jakub
la source
3

Pour "étendre" la suggestion de Matthias Bauch, dans Swift je ferais de ceci une propriété calculée 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)
  }
}

L'avantage de ceci est qu'il est attaché au NSTimeIntervaltype, pas à votre contrôleur de vue ou à tout autre endroit où vous placez cette fonction. Pour utiliser, vous iriez quelque chose comme:

let timeInterval = NSDate().timeIntervalSinceDate(start)        
self.elapsedTimeLabel.text = timeInterval.stringValue
Kenny Winker
la source
Merci, Kenny! Dans mon cas, j'en avais besoin séparément, j'ai donc ajouté des variables supplémentaires à votre extension: "var seconds: Int {let interval = Int (self); let seconds = interval% 60; return seconds}" et ainsi de suite pendant des minutes et des heures. Pour que l'utilisation soit: print ("minutes passées: (timeInterval.minutes)")
Vitalii
3

Basé sur la réponse de @ onmyway133, voici la version 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
la source
0

directement à partir de la documentation Apple: dans le fichier h:

 @property(strong,nonatomic)NSDateComponentsFormatter *timerFormatter;

dans le fichier 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;
}

chaque fois que vous avez besoin de convertir votre NSTimeInterval timeInterval en chaîne hh: mm: ss, procédez comme suit:

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

Je suppose que la fraction de minuterie devrait être plafonnée. Comme le code de Matthias créait ce problème en quelques secondes, j'utilise le code suivant légèrement modifié par rapport à celui 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
la source
0

Version objectif C de la réponse 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
la source