web-dev-qa-db-fra.com

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

J'ai une question de base (un peu?) 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 :-)

Tous les conseils sur la façon dont je peux atteindre cet objectif dans Swift seraient fantastiques! Merci d'avance!

76
Joe

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 les tuples rapides pour renvoyer trois valeurs à la fois. Vous pouvez déstructurer le tuple à l'aide de la syntaxe let (var, ...) ou accéder aux membres individuels du tuple, le cas échéant.

Si vous avez réellement besoin de l’imprimer avec les mots Hours etc etc, 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 de secondsToHoursMinutesSeconds() fonctionne pour les arguments Int. Si vous voulez une version Double, vous devez déterminer quelles sont les valeurs de retour. Il peut s'agir de (Int, Int, Double) ou de (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)
}
215
GoZoner

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

Il considère 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é disponibles sont positional, abbreviated, short, full, spellOut et brief.

Pour plus d'informations, veuillez lire la documenation .

103
vadian

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

25
David Seek

S'appuyant sur La réponse de Vadian , j'ai écrit une extension qui prend une Double (dont TimeInterval est un alias de type) et crache une chaîne au format time.

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 options DateComponentsFormatter.UnitsStyle:

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
18
Adrian

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 de commodité 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 entières ne prennent pas en compte les jours bissextiles. Si le cas d'utilisation concerne des dates/heures réelles Date et Calendrier doit être utilisé.

14
PeejWeej

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
}
9
neeks

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

Solution Swift 3.0 basée à peu près sur celle ci-dessus 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 ainsi?

 let dTotalSeconds = self.player.currentTime()
 playingCurrentTime = dTotalSeconds.durationText
5
user3069232

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

Appeler la fonction

    timeString(time: TimeInterval(i))

Je reviendrai 2:44:57

5
DialDT

Si la cible de votre application est iOS 10.0 ou plus récent, vous pouvez utiliser Measurement pour convertir le nombre de secondes en heures, minutes et secondes. Dans Swift 4.2.1/Xcode 10.1, vous pouvez utiliser cette fonction pour convertir la valeur de la durée d'une unité en secondes:

func convert<MeasurementType: BinaryInteger>(measurementValue: MeasurementType,
                                             unitDuration: UnitDuration) -> (MeasurementType, MeasurementType) {
    let measurementSeconds = Measurement<UnitDuration>(value: Double(measurementValue),
                                                       unit: .seconds)
    let secondsCount = MeasurementType(measurementSeconds.converted(to: unitDuration).value)
    let measurementCurrentUnit = Measurement(value: Double(secondsCount),
                                             unit: unitDuration)
    let currentUnitCount = MeasurementType(measurementCurrentUnit.converted(to: .seconds).value)
    return (secondsCount, measurementValue - currentUnitCount)
}

Pour obtenir une valeur requise, appelez-la deux fois:

func convertSecondsToHoursMinutesSeconds<MeasurementType: BinaryInteger>(seconds: MeasurementType) -> (MeasurementType, MeasurementType, MeasurementType) {
    let hoursAndRestSeconds = convert(measurementValue: seconds, unitDuration: .hours)
    let minutesAndRestSeconds = convert(measurementValue: hoursAndRestSeconds.1, unitDuration: .minutes)
    return (hoursAndRestSeconds.0, minutesAndRestSeconds.0, minutesAndRestSeconds.1)
}

Comment l'utiliser:

let result = convertSecondsToHoursMinutesSeconds(seconds: 27005)
print("\(result.0) Hours \(result.1) Minutes \(result.2) Seconds")

Comme vous pouvez le constater, je n’ai pas utilisé 60, 3600 ou d’autres constantes numériques dans ma solution.

4
Roman Podymov

Selon GoZoner answer, j'ai écrit une extension pour que le heure soit formaté 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"
3
xGoPox

Réponse de @ r3dm4n était super. Cependant, j'avais aussi besoin d'une heure. Juste au cas où quelqu'un aurait besoin de vous aussi, 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)
}
2
sc13

NSTimeInterval est Double faire avec 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
    }
}

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)
1
Gamec

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

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)

Sortie: 2:11:43

0
Maksim Kniazev

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
0
caesss

Je suis allé de l'avant et 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 à votre guise. Vous pouvez bien sûr vouloir ajouter des heures aussi, sinon plus d'informations.

La 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
}
0
Ali H Essayli