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!

Joe
fuente

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)
}
GoZoner
fuente
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 .

vadian
fuente
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
Adrian
fuente
@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

David Seek
fuente
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 .

NoLongerContributingToSE
fuente
¿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

DialDT
fuente
¡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)
}
r3dm4n
fuente
¿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
}
Neeks
fuente
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
usuario3069232
fuente
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
Roman Podymov
fuente
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"
ZAFAR007
fuente
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"
xGoPox
fuente
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

Maksim Kniazev
fuente
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)
}
Vetuka
fuente
3

Último código: XCode 10.4 Swift 5

extension Int {
    func timeDisplay() -> String {
        return "\(self / 3600):\((self % 3600) / 60):\((self % 3600) % 60)"
    }
}
Saranjith
fuente
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"
Asad Ali Choudhry
fuente
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)
Gamec
fuente
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
    }
}
Bartłomiej Semańczyk
fuente
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.

Torbjørn Kristoffersen
fuente
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
caesss
fuente
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
}
Ali H Essayli
fuente
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

Wasim
fuente