Swift - Conversión de enteros a horas / minutos / segundos


132

Tengo una pregunta básica (¿algo?) Con respecto a las conversiones de tiempo en Swift .

Tengo un número entero que me gustaría convertir en Horas / Minutos / Segundos.

Ejemplo: Int = 27005 me daría:

7 Hours  30 Minutes 5 Seconds

Sé cómo hacer esto en PHP, pero, por desgracia, Swift no es PHP :-)

¡Algún consejo sobre cómo puedo lograr esto rápidamente sería fantástico! ¡Gracias de antemano!

Respuestas:


296

Definir

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

Utilizar

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

o

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

La función anterior hace uso de las tuplas Swift para devolver tres valores a la vez. Puede desestructurar la tupla utilizando la let (var, ...)sintaxis o puede acceder a miembros individuales de la tupla, si es necesario.

Si realmente necesita imprimirlo con las palabras, Hoursetc., use algo como esto:

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

Tenga en cuenta que la implementación anterior de secondsToHoursMinutesSeconds()trabajos para Intargumentos. Si desea una Doubleversión, deberá decidir cuáles son los valores de retorno: podría ser (Int, Int, Double)o podría ser (Double, Double, Double). Podrías probar algo como:

func secondsToHoursMinutesSeconds (seconds : Double) -> (Double, Double, Double) {
  let (hr,  minf) = modf (seconds / 3600)
  let (min, secf) = modf (60 * minf)
  return (hr, min, 60 * secf)
}

14
El último valor (seconds % 3600) % 60puede optimizarse para seconds % 60. No es necesario extraer las horas primero.
zisoft

@GoZoner: parece que no puedo obtener la función printSecondsToHoursMinutesSeconds para que funcione correctamente. Esto es lo que tengo en un patio de recreo, pero printSecondsToHoursMinutesSeconds no devuelve nada: importar UIKit func segundosToHoursMinutesSeconds (segundos: Int) -> (Int, Int, Int) {return (segundos / 3600, (segundos% 3600) / 60, (segundos % 3600)% 60)} let (h, m, s) = segundosToHoursMinutesSeconds (27005) func printSecondsToHoursMinutesSeconds (segundos: Int) -> () {let (h, m, s) = segundosToHoursMinutesSeconds (segundos) println ("(h ) Horas, (m) Minutos, (s) Segundos ")}
Joe

printSecondstoHoursMinutesSeconds()no devuelve nada (vea el -> ()tipo de retorno en la declaración de función). La función imprime algo; que no aparece en un patio de juegos. Si desea que devuelva algo, diga a y Stringluego elimine la println()llamada y arregle el tipo de retorno de la función.
GoZoner

@GoZoner: solo una pregunta rápida en la sintaxis anterior. ¿Cómo declararía que 27005 en una variable? Estoy trabajando en una herramienta ahora mismo para mojarme los pies con rapidez. Tengo una constante que muestra la cantidad de segundos (generada después de mis cálculos básicos). let cocSeconds = cocMinutes * 60. (cocSeconds es lo que quiero usar en lugar de 27005.) Creo que el problema que tengo es que cocSeconds es doble, y en tu sintaxis, estás usando Ints. ¿Cómo haría para ajustar este código para poner una variable en lugar de 27005? ¡¡¡Muchas gracias de antemano!!!
Joe

La solución que di funciona para los Inttipos debido a la naturaleza de las funciones /y %. Es decir, /cae la parte faccional. El mismo código no funcionará Double. Específicamente 2 == 13/5 pero 2.6 == 13.0 / 5. Por lo tanto, necesitaría una implementación diferente para Double. He actualizado mi respuesta con una nota sobre esto.
GoZoner

172

En macOS 10.10+ / iOS 8.0+ (NS)DateComponentsFormatterse ha introducido para crear una cadena legible.

Considera la configuración regional y el idioma del usuario.

let interval = 27005

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

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

Los estilos disponibles son unitarios positional, abbreviated, short, full, spellOuty brief.

Para obtener más información, lea la documentación .


19
¡Usar formatter.unitsStyle = .positionalda exactamente lo que quiero (que es 7:30:05)! Mejor respuesta OMI
Sam

11
Y puede agregar el cero `` `` formatter.zeroFormattingBehavior = .pad '' ``
Eduardo Irias

Cómo obtener el viceversa de esto. Así es como convertir horas, minutos a segundos
Angel F Syrus

1
@AngelFSyrus Simplyhours * 3600 + minutes * 60
vadian

59

Sobre la base de la respuesta de Vadian , escribí una extensión que toma un Double(de los cuales TimeIntervales un alias de tipo) y escupe una cadena con formato de tiempo.

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

Estas son las diferentes DateComponentsFormatter.UnitsStyleopciones:

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

@MaksimKniazev Por lo general, los valores orientados al tiempo se representan Doublesen Swift.
Adrian

@Adrian gracias por la extensión. Me gusta el estilo de unidad .posicional, sin embargo, me gustaría mostrar, es decir: 9 segundos como "00:09" en lugar de "9", 1 minuto y 25 segundos como "01:25" en lugar de "1:25". Puedo lograr este cálculo manualmente, basado en el valor Doble y me preguntaba si hay alguna forma de incorporarlo a la extensión misma. Gracias.
Vetuka

FYI: si desea mantener los 0 antes de un valor de un solo dígito (o simplemente si el valor es 0) debe agregar esto formatter.zeroFormattingBehavior = .pad. Esto nos daría:3660.asString(style: .positional) // 01:01:00
Moucheg

27

He creado una combinación de respuestas existentes para simplificar todo y reducir la cantidad de código necesario para 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)"
}

Uso:

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

Huellas dactilares:

00:01:40


55
Desde mi punto de vista, agregar un cierre no simplifica realmente nada. ¿Hay alguna buena razón para el cierre?
derpoliuk

@derpoliuk He necesitado el cierre para mis necesidades específicas en mi aplicación
David Seek

24

Aquí hay un enfoque más estructurado / 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)

Tener un enfoque como este permite agregar un análisis de conveniencia como este:

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

Cabe señalar que los enfoques basados ​​exclusivamente en enteros no tienen en cuenta los días bisiestos / segundos. Si el caso de uso se trata de fechas / horas reales, se debe usar Fecha y Calendario .


¿Podría agregar montha su implementación? ¡Gracias por adelantado!
ixany

3
Deberías usar NSCalendar (Calendario) para algo así.
NoLongerContributingToSE

Puede reemplazarlo return number < 10 ? "0\(number)" : "\(number)"usando un formateador de cadena return String(format: "%02d", number)que agrega automáticamente un cero cuando el número está por debajo de 10
Continuo

19

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

Para llamar a la función

    timeString(time: TimeInterval(i))

Volveremos 02:44:57


¡Magnífico! Justo lo que necesitaba. Cambié el 9897 por una variable Int y obtuve los resultados deseados. ¡Gracias!
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)
}

¿Qué es truncatingRemainder? Xcode no lo reconoce por mí.
Alfi

10

Aquí hay otra implementación simple en 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

Solución SWIFT 3.0 basada aproximadamente en la anterior usando extensiones.

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

  }
}

¿Usarlo con AVPlayer llamándolo así?

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

8

Respondí a una pregunta similar , sin embargo, no es necesario mostrar milisegundos en el resultado. Por lo tanto, mi solución requiere iOS 10.0, tvOS 10.0, watchOS 3.0 o macOS 10.12.

Debería llamar func convertDurationUnitValueToOtherUnits(durationValue:durationUnit:smallestUnitDuration:)por la respuesta que ya mencioné aquí:

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

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

Uso:

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

3

Según la respuesta de GoZoner , escribí una extensión para formatear el tiempo de acuerdo con las horas, minutos y segundos:

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

Esto es lo que uso para mi reproductor de música en Swift 4+. Estoy convirtiendo segundos Int a formato de cadena legible

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

Usar así:

print(7903.toAudioString)

Salida: 2:11:43


3

La respuesta de @ r3dm4n fue genial. Sin embargo, también necesitaba una hora. En caso de que alguien más lo necesite también, aquí está:

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

3

Último código: XCode 10.4 Swift 5

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

2

Swift 5 & String Response, en formato presentable

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

Uso:

print(secondsToHoursMinutesSecondsStr(seconds: 20000)) // Result = "5 h 33 min 20 sec"

1

La forma más simple en mi humilde opinión:

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)

Puede ser ld para doble en lugar de dint.
Nik Kov

1

NSTimeIntervales Doublehacerlo con extensión. Ejemplo:

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

0

Seguí adelante y creé un cierre para esto (en Swift 3).

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

Esto dará m = 4 ys = 59. Entonces puede formatearlo como desee. Por supuesto, es posible que desee agregar horas también, si no más información.


0

Swift 4 estoy usando esta extensión

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

uso

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

0

respuesta de Neek no es correcta.

aquí está la versión correcta

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

Otra forma sería convertir segundos a la fecha y tomar los componentes, es decir, segundos, minutos y horas desde la fecha misma. Esta solución tiene limitación solo hasta las 23:59:59

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.