Swift - Conversion d'entiers en heures / minutes / secondes

132

J'ai une (un peu?) Question de base concernant les conversions de temps dans Swift .

J'ai un entier que je voudrais convertir en heures / minutes / secondes.

Exemple: Int = 27005 me donnerait:

7 Hours  30 Minutes 5 Seconds

Je sais comment faire cela en PHP, mais hélas, swift n'est pas PHP :-)

Tout conseil pour y parvenir rapidement serait fantastique! Merci d'avance!

Joe
la source

Réponses:

296

Définir

func secondsToHoursMinutesSeconds (seconds : Int) -> (Int, Int, Int) {
  return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)
}

Utilisation

> secondsToHoursMinutesSeconds(27005)
(7,30,5)

ou

let (h,m,s) = secondsToHoursMinutesSeconds(27005)

La fonction ci-dessus utilise des tuples Swift pour renvoyer trois valeurs à la fois. Vous déstructurez le tuple en utilisant lelet (var, ...) syntaxe ou pouvez accéder à des membres de tuple individuels, si nécessaire.

Si vous avez réellement besoin de l'imprimer avec les mots, Hoursetc., utilisez quelque chose comme ceci:

func printSecondsToHoursMinutesSeconds (seconds:Int) -> () {
  let (h, m, s) = secondsToHoursMinutesSeconds (seconds)
  print ("\(h) Hours, \(m) Minutes, \(s) Seconds")
}

Notez que l'implémentation ci-dessus secondsToHoursMinutesSeconds()fonctionne pour les Intarguments. Si vous voulez une Doubleversion, vous devrez décider quelles sont les valeurs de retour - pourraient être (Int, Int, Double)ou pourraient être (Double, Double, Double). Vous pouvez essayer quelque chose comme:

func secondsToHoursMinutesSeconds (seconds : Double) -> (Double, Double, Double) {
  let (hr,  minf) = modf (seconds / 3600)
  let (min, secf) = modf (60 * minf)
  return (hr, min, 60 * secf)
}
GoZoner
la source
14
La dernière valeur (seconds % 3600) % 60peut être optimisée à seconds % 60. Pas besoin d'extraire d'abord les heures.
zisoft
@GoZoner - Je n'arrive pas à obtenir la fonction printSecondsToHoursMinutesSeconds pour fonctionner correctement. C'est ce que j'ai dans un terrain de jeu, mais printSecondsToHoursMinutesSeconds ne renvoie rien: import UIKit func secondsToHoursMinutesSeconds (seconds: Int) -> (Int, Int, Int) {return (seconds / 3600, (seconds% 3600) / 60, (seconds % 3600)% 60)} let (h, m, s) = secondsToHoursMinutesSeconds (27005) func printSecondsToHoursMinutesSeconds (secondes: Int) -> () {let (h, m, s) = secondsToHoursMinutesSeconds (secondes) println ("(h ) Heures, (m) Minutes, (s) Secondes ")}
Joe
printSecondstoHoursMinutesSeconds()ne renvoie rien (voir le -> ()type de retour dans la déclaration de fonction). La fonction imprime quelque chose; qui n'apparaît pas dans un Playground. Si vous voulez qu'il renvoie quelque chose, dites a, Stringpuis éliminez l' println()appel et corrigez le type de retour de la fonction.
GoZoner
@GoZoner - juste une petite question dans votre syntaxe ci-dessus. Comment déclarer que 27005 dans une variable? Je travaille actuellement sur un outil pour me mouiller les pieds avec rapidité. J'ai une constante qui affiche le nombre de secondes (généré après mes calculs de base). let cocSeconds = cocMinutes * 60. (cocSeconds est ce que je veux utiliser à la place de 27005.) Je pense que le problème que j'ai est cocSeconds est un double, et dans votre syntaxe, vous utilisez Ints. Comment procéder pour ajuster ce code pour mettre une variable à la place de 27005? Merci d'avance!!!
Joe
La solution que j'ai donnée fonctionne pour les Inttypes en raison de la nature des fonctions /et %. Cela /laisse tomber la partie factionnelle. Le même code ne fonctionnera pas Double. Plus précisément 2 == 13/5 mais 2,6 == 13,0 / 5. Par conséquent, vous auriez besoin d'une implémentation différente pour Double. J'ai mis à jour ma réponse avec une note à ce sujet.
GoZoner
173

Dans macOS 10.10+ / iOS 8.0+ (NS)DateComponentsFormattera été introduit pour créer une chaîne lisible.

Il prend en compte les paramètres régionaux et la langue de l'utilisateur.

let interval = 27005

let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
formatter.unitsStyle = .full

let formattedString = formatter.string(from: TimeInterval(interval))!
print(formattedString)

Les styles d'unités disponibles sont positional, abbreviated, short, full, spellOutet brief.

Pour plus d'informations, veuillez lire la documentation .

vadian
la source
19
Utiliser formatter.unitsStyle = .positionaldonne exactement ce que je veux (ce qui est 7:30:05)! Meilleure réponse IMO
Sam
11
Et vous pouvez ajouter le zéro `` formatter.zeroFormattingBehavior = .pad ''
Eduardo Irias
Comment obtenir l'inverse de cela. Voilà comment convertir les heures, les minutes en secondes
Angel F Syrus
1
@AngelFSyrus Simplyhours * 3600 + minutes * 60
vadian
59

En me basant sur la réponse de Vadian , j'ai écrit une extension qui prend un Double(dont TimeIntervalest un alias de type) et crache une chaîne formatée en temps.

extension Double {
  func asString(style: DateComponentsFormatter.UnitsStyle) -> String {
    let formatter = DateComponentsFormatter()
    formatter.allowedUnits = [.hour, .minute, .second, .nanosecond]
    formatter.unitsStyle = style
    guard let formattedString = formatter.string(from: self) else { return "" }
    return formattedString
  }
}

Voici à quoi ressemblent les différentes DateComponentsFormatter.UnitsStyleoptions:

10000.asString(style: .positional)  // 2:46:40
10000.asString(style: .abbreviated) // 2h 46m 40s
10000.asString(style: .short)       // 2 hr, 46 min, 40 sec
10000.asString(style: .full)        // 2 hours, 46 minutes, 40 seconds
10000.asString(style: .spellOut)    // two hours, forty-six minutes, forty seconds
10000.asString(style: .brief)       // 2hr 46min 40sec
Adrian
la source
@MaksimKniazev Habituellement, les valeurs temporelles sont représentées par Doublesdans Swift.
Adrian
@Adrian merci pour l'extension. J'aime le .positionnel UnitStyle, cependant je voudrais afficher: 9 secondes comme "00:09" au lieu de "9", 1min 25 secondes comme "01:25" au lieu de "1:25". Je suis capable de réaliser ce calcul manuellement, basé sur la valeur Double et je me demandais s'il y avait un moyen de l'intégrer dans l'extension elle-même? Je vous remercie.
Vetuka le
FYI: si vous souhaitez conserver les 0 avant une valeur à un seul chiffre (ou simplement si la valeur est 0), vous devez l'ajouter formatter.zeroFormattingBehavior = .pad. Cela nous donnerait:3660.asString(style: .positional) // 01:01:00
Moucheg
27

J'ai construit un mashup de réponses existantes pour tout simplifier et réduire la quantité de code nécessaire pour Swift 3 .

func hmsFrom(seconds: Int, completion: @escaping (_ hours: Int, _ minutes: Int, _ seconds: Int)->()) {

        completion(seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)

}

func getStringFrom(seconds: Int) -> String {

    return seconds < 10 ? "0\(seconds)" : "\(seconds)"
}

Usage:

var seconds: Int = 100

hmsFrom(seconds: seconds) { hours, minutes, seconds in

    let hours = getStringFrom(seconds: hours)
    let minutes = getStringFrom(seconds: minutes)
    let seconds = getStringFrom(seconds: seconds)

    print("\(hours):\(minutes):\(seconds)")                
}

Impressions:

00:01:40

David Seek
la source
5
De mon point de vue, ajouter la fermeture ne simplifie vraiment rien. Y a-t-il une bonne raison de fermer?
derpoliuk
@derpoliuk J'ai besoin de la fermeture pour mes besoins spécifiques dans mon application
David Seek
24

Voici une approche plus structurée / flexible: (Swift 3)

struct StopWatch {

    var totalSeconds: Int

    var years: Int {
        return totalSeconds / 31536000
    }

    var days: Int {
        return (totalSeconds % 31536000) / 86400
    }

    var hours: Int {
        return (totalSeconds % 86400) / 3600
    }

    var minutes: Int {
        return (totalSeconds % 3600) / 60
    }

    var seconds: Int {
        return totalSeconds % 60
    }

    //simplified to what OP wanted
    var hoursMinutesAndSeconds: (hours: Int, minutes: Int, seconds: Int) {
        return (hours, minutes, seconds)
    }
}

let watch = StopWatch(totalSeconds: 27005 + 31536000 + 86400)
print(watch.years) // Prints 1
print(watch.days) // Prints 1
print(watch.hours) // Prints 7
print(watch.minutes) // Prints 30
print(watch.seconds) // Prints 5
print(watch.hoursMinutesAndSeconds) // Prints (7, 30, 5)

Avoir une approche comme celle-ci permet d'ajouter une analyse pratique comme celle-ci:

extension StopWatch {

    var simpleTimeString: String {
        let hoursText = timeText(from: hours)
        let minutesText = timeText(from: minutes)
        let secondsText = timeText(from: seconds)
        return "\(hoursText):\(minutesText):\(secondsText)"
    }

    private func timeText(from number: Int) -> String {
        return number < 10 ? "0\(number)" : "\(number)"
    }
}
print(watch.simpleTimeString) // Prints 07:30:05

Il convient de noter que les approches purement basées sur des nombres entiers ne prennent pas en compte les jours / secondes bissextiles. Si le cas d'utilisation traite avec les dates / heures réel La date et le calendrier devrait être utilisé.

NoLongerContributingToSE
la source
Pourriez-vous s'il vous plaît ajouter monthà votre mise en œuvre? Merci d'avance!
ixany
3
Vous devriez utiliser NSCalendar (Calendar) pour quelque chose comme ça.
NoLongerContributingToSE
Vous pouvez remplacer return number < 10 ? "0\(number)" : "\(number)"en utilisant un formateur de chaîne return String(format: "%02d", number)qui ajoute automatiquement un zéro lorsque le nombre est inférieur à 10
Continuum
19

Dans Swift 5:

    var i = 9897

    func timeString(time: TimeInterval) -> String {
        let hour = Int(time) / 3600
        let minute = Int(time) / 60 % 60
        let second = Int(time) % 60

        // return formated string
        return String(format: "%02i:%02i:%02i", hour, minute, second)
    }

Pour appeler la fonction

    timeString(time: TimeInterval(i))

Reviendra 02:44:57

DialDT
la source
Superbe! Juste ce dont j'avais besoin. J'ai changé le 9897 pour une variable Int et j'ai obtenu les résultats souhaités. Je vous remercie!
David_2877
13

Swift 4

func formatSecondsToString(_ seconds: TimeInterval) -> String {
    if seconds.isNaN {
        return "00:00"
    }
    let Min = Int(seconds / 60)
    let Sec = Int(seconds.truncatingRemainder(dividingBy: 60))
    return String(format: "%02d:%02d", Min, Sec)
}
r3dm4n
la source
Qu'est-ce que truncatingRemainder? Xcode ne le reconnaît pas pour moi.
Alfi
10

Voici une autre implémentation simple dans Swift3.

func seconds2Timestamp(intSeconds:Int)->String {
   let mins:Int = intSeconds/60
   let hours:Int = mins/60
   let secs:Int = intSeconds%60

   let strTimestamp:String = ((hours<10) ? "0" : "") + String(hours) + ":" + ((mins<10) ? "0" : "") + String(mins) + ":" + ((secs<10) ? "0" : "") + String(secs)
   return strTimestamp
}
neeks
la source
9

Solution SWIFT 3.0 basée à peu près sur celle ci-dessus en utilisant des extensions.

extension CMTime {
  var durationText:String {
    let totalSeconds = CMTimeGetSeconds(self)
    let hours:Int = Int(totalSeconds.truncatingRemainder(dividingBy: 86400) / 3600)
    let minutes:Int = Int(totalSeconds.truncatingRemainder(dividingBy: 3600) / 60)
    let seconds:Int = Int(totalSeconds.truncatingRemainder(dividingBy: 60))

    if hours > 0 {
        return String(format: "%i:%02i:%02i", hours, minutes, seconds)
    } else {
        return String(format: "%02i:%02i", minutes, seconds)
    }

  }
}

Utilisez-le avec AVPlayer en l'appelant comme ça?

 let dTotalSeconds = self.player.currentTime()
 playingCurrentTime = dTotalSeconds.durationText
user3069232
la source
8

J'avais répondu à la question similaire , mais vous n'avez pas besoin d'afficher les millisecondes dans le résultat. Par conséquent, ma solution nécessite iOS 10.0, tvOS 10.0, watchOS 3.0 ou macOS 10.12.

Vous devriez appeler à func convertDurationUnitValueToOtherUnits(durationValue:durationUnit:smallestUnitDuration:)partir de la réponse que j'ai déjà mentionnée ici:

let secondsToConvert = 27005
let result: [Int] = convertDurationUnitValueToOtherUnits(
    durationValue: Double(secondsToConvert),
    durationUnit: .seconds,
    smallestUnitDuration: .seconds
)
print("\(result[0]) hours, \(result[1]) minutes, \(result[2]) seconds") // 7 hours, 30 minutes, 5 seconds
Roman Podymov
la source
5

Swift 5:

extension Int {

    func secondsToTime() -> String {

        let (h,m,s) = (self / 3600, (self % 3600) / 60, (self % 3600) % 60)

        let h_string = h < 10 ? "0\(h)" : "\(h)"
        let m_string =  m < 10 ? "0\(m)" : "\(m)"
        let s_string =  s < 10 ? "0\(s)" : "\(s)"

        return "\(h_string):\(m_string):\(s_string)"
    }
}

Usage:

let seconds : Int = 119
print(seconds.secondsToTime()) // Result = "00:01:59"
ZAFAR007
la source
3

Selon la réponse de GoZoner, j'ai écrit une extension pour que l' heure soit formatée en fonction des heures, des minutes et des secondes:

extension Double {

    func secondsToHoursMinutesSeconds () -> (Int?, Int?, Int?) {
        let hrs = self / 3600
        let mins = (self.truncatingRemainder(dividingBy: 3600)) / 60
        let seconds = (self.truncatingRemainder(dividingBy:3600)).truncatingRemainder(dividingBy:60)
        return (Int(hrs) > 0 ? Int(hrs) : nil , Int(mins) > 0 ? Int(mins) : nil, Int(seconds) > 0 ? Int(seconds) : nil)
    }

    func printSecondsToHoursMinutesSeconds () -> String {

        let time = self.secondsToHoursMinutesSeconds()

        switch time {
        case (nil, let x? , let y?):
            return "\(x) min \(y) sec"
        case (nil, let x?, nil):
            return "\(x) min"
        case (let x?, nil, nil):
            return "\(x) hr"
        case (nil, nil, let x?):
            return "\(x) sec"
        case (let x?, nil, let z?):
            return "\(x) hr \(z) sec"
        case (let x?, let y?, nil):
            return "\(x) hr \(y) min"
        case (let x?, let y?, let z?):
            return "\(x) hr \(y) min \(z) sec"
        default:
            return "n/a"
        }
    }
}

let tmp = 3213123.printSecondsToHoursMinutesSeconds() // "892 hr 32 min 3 sec"
xGoPox
la source
3

Voici ce que j'utilise pour mon lecteur de musique dans Swift 4+. Je convertis les secondes Int en format String lisible

extension Int {
    var toAudioString: String {
        let h = self / 3600
        let m = (self % 3600) / 60
        let s = (self % 3600) % 60
        return h > 0 ? String(format: "%1d:%02d:%02d", h, m, s) : String(format: "%1d:%02d", m, s)
    }
}

Utilisez comme ceci:

print(7903.toAudioString)

Production: 2:11:43

Maksim Kniazev
la source
3

La réponse de @ r3dm4n était géniale. Cependant, il me fallait aussi une heure. Juste au cas où quelqu'un d'autre en aurait besoin, voici:

func formatSecondsToString(_ seconds: TimeInterval) -> String {
    if seconds.isNaN {
        return "00:00:00"
    }
    let sec = Int(seconds.truncatingRemainder(dividingBy: 60))
    let min = Int(seconds.truncatingRemainder(dividingBy: 3600) / 60)
    let hour = Int(seconds / 3600)
    return String(format: "%02d:%02d:%02d", hour, min, sec)
}
Vetuka
la source
3

Dernier code: XCode 10.4 Swift 5

extension Int {
    func timeDisplay() -> String {
        return "\(self / 3600):\((self % 3600) / 60):\((self % 3600) % 60)"
    }
}
Saranjith
la source
2

Swift 5 & String Response, au format présentable

public static func secondsToHoursMinutesSecondsStr (seconds : Int) -> String {
      let (hours, minutes, seconds) = secondsToHoursMinutesSeconds(seconds: seconds);
      var str = hours > 0 ? "\(hours) h" : ""
      str = minutes > 0 ? str + " \(minutes) min" : str
      str = seconds > 0 ? str + " \(seconds) sec" : str
      return str
  }

public static func secondsToHoursMinutesSeconds (seconds : Int) -> (Int, Int, Int) {
        return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)
 }

Usage:

print(secondsToHoursMinutesSecondsStr(seconds: 20000)) // Result = "5 h 33 min 20 sec"
Asad Ali Choudhry
la source
1

Le moyen le plus simple à mon humble avis:

let hours = time / 3600
let minutes = (time / 60) % 60
let seconds = time % 60
return String(format: "%0.2d:%0.2d:%0.2d", hours, minutes, seconds)
Gamec
la source
Cela peut être ldpour double au lieu de dpour int.
Nik Kov
1

NSTimeIntervalest Doublele faire avec l'extension. Exemple:

extension Double {

    var formattedTime: String {

        var formattedTime = "0:00"

        if self > 0 {

            let hours = Int(self / 3600)
            let minutes = Int(truncatingRemainder(dividingBy: 3600) / 60)

            formattedTime = String(hours) + ":" + (minutes < 10 ? "0" + String(minutes) : String(minutes))
        }

        return formattedTime
    }
}
Bartłomiej Semańczyk
la source
0

Je suis allé de l'avant et j'ai créé une fermeture pour cela (dans Swift 3).

let (m, s) = { (secs: Int) -> (Int, Int) in
        return ((secs % 3600) / 60, (secs % 3600) % 60) }(299)

Cela donnera m = 4 et s = 59. Vous pouvez donc formater cela comme vous le souhaitez. Vous pouvez bien sûr ajouter des heures, sinon plus d'informations.

Torbjørn Kristoffersen
la source
0

Swift 4 J'utilise cette extension

 extension Double {

    func stringFromInterval() -> String {

        let timeInterval = Int(self)

        let millisecondsInt = Int((self.truncatingRemainder(dividingBy: 1)) * 1000)
        let secondsInt = timeInterval % 60
        let minutesInt = (timeInterval / 60) % 60
        let hoursInt = (timeInterval / 3600) % 24
        let daysInt = timeInterval / 86400

        let milliseconds = "\(millisecondsInt)ms"
        let seconds = "\(secondsInt)s" + " " + milliseconds
        let minutes = "\(minutesInt)m" + " " + seconds
        let hours = "\(hoursInt)h" + " " + minutes
        let days = "\(daysInt)d" + " " + hours

        if daysInt          > 0 { return days }
        if hoursInt         > 0 { return hours }
        if minutesInt       > 0 { return minutes }
        if secondsInt       > 0 { return seconds }
        if millisecondsInt  > 0 { return milliseconds }
        return ""
    }
}

utilisation

// assume myTimeInterval = 96460.397    
myTimeInteval.stringFromInterval() // 1d 2h 47m 40s 397ms
caesss
la source
0

réponse de neek n'est pas correcte.

voici la bonne version

func seconds2Timestamp(intSeconds:Int)->String {
   let mins:Int = (intSeconds/60)%60
   let hours:Int = intSeconds/3600
   let secs:Int = intSeconds%60

   let strTimestamp:String = ((hours<10) ? "0" : "") + String(hours) + ":" + ((mins<10) ? "0" : "") + String(mins) + ":" + ((secs<10) ? "0" : "") + String(secs)
   return strTimestamp
}
Ali H Essayli
la source
0

Une autre façon serait de convertir les secondes en date et de prendre les composants, c'est-à-dire les secondes, les minutes et les heures à partir de la date elle-même. Cette solution est limitée jusqu'à 23:59:59

Wasim
la source