Comment convertir un NSTimeInterval (secondes) en minutes

104

J'en ai passé une partie à secondspartir d'un certain événement. Il est stocké dans un NSTimeIntervaltype de données.

Je veux le convertir en minuteset seconds.

Par exemple, j'ai: "326,4" secondes et je veux le convertir dans la chaîne suivante: "5:26".

Quelle est la meilleure façon d'atteindre cet objectif?

Merci.

Ilya Suzdalnitski
la source

Réponses:

147

pseudo-code:

minutes = floor(326.4/60)
seconds = round(326.4 - minutes * 60)
Brian Ramsay
la source
90
Je pense qu'il vaut également la peine de mentionner qu'un NSTimerInterval n'est qu'un typedef pour double.
Armentage
4
Voir la réponse d'Albaregar - c'est une meilleure façon de faire cela (plus de code mais plus flexible, maintenable)
benvolioT
1
Ce code est cependant très coûteux. Pour une conversion rapide d'une durée en secondes, cette réponse est meilleure.
SpacyRicochet
2
Veuillez noter que cette réponse vous donnera des trucs comme 1:60. Regardez la réponse de StratFan qui est plus proche de la vérité. Mais enlevez le sol et le rond et vous devriez être à la maison libre.
InvulgoSoft
La réponse de Mick MacCallum ci-dessous au moment de la rédaction de ce commentaire semble être la plus mise à jour et faite par la manière d'utiliser l'API native.
Tomasz Nazarenko
183

Brève description

  1. La réponse de Brian Ramsay est plus pratique si vous ne souhaitez convertir qu'en minutes.
  2. Si vous voulez que l'API Cocoa le fasse pour vous et convertisse votre NSTimeInterval non seulement en minutes mais aussi en jours, mois, semaine, etc., je pense que c'est une approche plus générique
  3. Utilisez la méthode NSCalendar:

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

    • "Renvoie, en tant qu'objet NSDateComponents utilisant des composants spécifiés, la différence entre deux dates fournies". De la documentation de l'API.

  4. Créez 2 NSDate dont la différence est le NSTimeInterval que vous souhaitez convertir. (Si votre NSTimeInterval provient de la comparaison de 2 NSDate, vous n'avez pas besoin de faire cette étape, et vous n'avez même pas besoin de NSTimeInterval).

  5. Obtenez vos devis auprès de NSDateComponents

Exemple de code

// 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];

Problèmes connus

  • Trop pour une simple conversion, vous avez raison, mais c'est ainsi que fonctionne l'API.
  • Ma suggestion: si vous vous habituez à gérer vos données de temps à l'aide de NSDate et NSCalendar, l'API fera le travail à votre place.
Albaregar
la source
4
Il y a certainement beaucoup de solution de contournement juste pour faire un peu de conversion, mais cela semble confortable, modulaire et vraiment flexible, votez pour cet exemple de code @Albaregar.
Rigo Vides
2
+1 Pour une bonne réponse approfondie, cependant ce code est assez cher.
Nick Weaver
1
+1 ... une mise en garde. Soyez prudent en utilisant ce style de code si vous devez arrondir les fractions de seconde. Le comportement par défaut de - [NSCalendar components: fromDate: toDate: options:] peut gâcher dans le scénario où le composant toDate est 31,5 secondes (par exemple) avant le fromDate - les composants renvoyés auront 31 dans le champ secondes. Il existe un paramètre facultatif de NSWrapCalendarComponents, mais à partir de l'expérimentation (limitée) que j'ai faite avec cela, il s'enroule à la seconde la plus proche plutôt que d'arrondir vers le haut ou vers le bas. Votre kilométrage peut varier.
David Doyle
%ldsi vous êtes sur une machine 64 bits.
Anoop Vaidya
5
Les gens qui trouvent cela (comme Nick Weaver) vont penser que le code semble lent. Ça ne l'est pas. Je fais une application de minuterie et je viens de tester la version d'Albaregar contre quelque chose dans le style de Brian Ramsey (et d'autres) ... et étonnamment, même en interrogeant 100 fois par seconde (ce qui est extrêmement exagéré) sur un appareil relativement lent (4S ) il y avait moins de 1% de différence dans l'utilisation du processeur.
mmc
42

Tous ces éléments ont l'air plus compliqués qu'ils ne devraient l'être! Voici une façon courte et douce de convertir un intervalle de temps en heures, minutes et secondes:

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;

Notez que lorsque vous mettez un float dans un int, vous obtenez automatiquement floor (), mais vous pouvez l'ajouter aux deux premiers si si vous vous sentez mieux :-)

Prasad
la source
Impressionnant! J'ai essayé le code de Ramsay bien qu'il semble être sorti d'une seconde.
uplearnedu.com
Il vous manque un point-virgule dans votre code mais cela ne me permet pas de le modifier pour le corriger.
Jeef
Je pense que votre approche est la meilleure - simple, fait le travail et «légère» sur les tiques. Parfait.
BonanzaDriver
29

Pardonnez-moi d'être vierge de Stack ... Je ne sais pas comment répondre à la réponse de Brian Ramsay ...

L'utilisation de round ne fonctionnera pas pour les secondes valeurs comprises entre 59,5 et 59,99999. La deuxième valeur sera de 60 pendant cette période. Utilisez plutôt trunc ...

 double progress;

 int minutes = floor(progress/60);
 int seconds = trunc(progress - minutes * 60);
StratFan
la source
1
En fait, retirez simplement le plancher et le coffre / rond. :)
InvulgoSoft
@StratFan Pouvez-vous s'il vous plaît ajouter des heures à votre réponse?
Shmidt
27

Si vous ciblez iOS 8 ou OS X 10.10 ou plus, cela devient beaucoup plus facile. La nouvelle NSDateComponentsFormatterclasse vous permet de convertir une donnée NSTimeIntervalde sa valeur en secondes en une chaîne localisée à afficher à l'utilisateur. Par exemple:

Objectif 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

Rapide

let interval = 326.4

let componentFormatter = NSDateComponentsFormatter()

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

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

NSDateCompnentsFormatterpermet également que cette sortie soit sous des formes plus longues. Plus d'informations peuvent être trouvées dans l' article NSFormatter de NSHipster . Et en fonction des classes avec lesquelles vous travaillez déjà (sinon NSTimeInterval), il peut être plus pratique de transmettre au formateur une instance de NSDateComponentsou deux NSDateobjets, ce qui peut également être fait via les méthodes suivantes.

Objectif c

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

Rapide

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

if let formattedString = componentFormatter.stringFromDateComponents(<#T##components: NSDateComponents##NSDateComponents#>) {
    // ...
}
Mick MacCallum
la source
C'est la bonne façon de créer la chaîne, correctement localisée
SwiftArchitect
1
Cette réponse est bien plus "Objective-C" et utile que la réponse acceptée. Vous n'avez pas besoin switchou if elsedéclarations avec cela. NSDateComponentsFormatters'occupe de tout. Heures, minutes, secondes imprimées sans aucun souci.
rustyMagnet le
17

Code de Brian Ramsay, dé-pseudofié:

- (NSString*)formattedStringForDuration:(NSTimeInterval)duration
{
    NSInteger minutes = floor(duration/60);
    NSInteger seconds = round(duration - minutes * 60);
    return [NSString stringWithFormat:@"%d:%02d", minutes, seconds];
}
Yang Meyer
la source
Cela ne localise pas
SwiftArchitect
7

Voici une version 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)
}

Peut être utilisé comme ceci:

let (d,h,m,s) = durationsBySecond(seconds: duration)
println("time left: \(d) days \(h) hours \(m) minutes \(s) seconds")
Jochen Bedersdorfer
la source
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);

Les sorties:

timeLater: 22:27
timeLeft: 1 hours 29 minutes  59 seconds
polarware
la source
son exemple vraiment parfait pour
cacher
5

Puisque c'est essentiellement un double ...

Divisez par 60,0 et extrayez la partie intégrale et la partie fractionnaire.

La partie intégrale sera le nombre entier de minutes.

Multipliez à nouveau la partie fractionnaire par 60,0.

Le résultat sera les secondes restantes.


la source
3

N'oubliez pas que la question d'origine concerne une sortie de chaîne, pas un pseudo-code ou des composants de chaîne individuels.

Je souhaite le convertir dans la chaîne suivante: "5:26"

De nombreuses réponses manquent les problèmes d'internationalisation et la plupart font les calculs mathématiques à la main. Tout juste si 20e siècle ...

Ne faites pas le calcul vous-même (Swift 4)

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

5:26


Tirer parti des bibliothèques

Si vous voulez vraiment des composants individuels et un code agréablement lisible, consultez SwiftDate :

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

5


Crédits à @mickmaccallum et @polarwar pour une utilisation adéquate deDateComponentsFormatter

SwiftArchitect
la source
0

Comment j'ai fait cela dans Swift (y compris la mise en forme de la chaîne pour l'afficher comme "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
la source
0

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