Swift - conversão de número inteiro para horas / minutos / segundos


131

Tenho uma pergunta (um pouco?) Básica sobre conversões de tempo no Swift .

Eu tenho um número inteiro que eu gostaria de converter em horas / minutos / segundos.

Exemplo: Int = 27005 me daria:

7 Hours  30 Minutes 5 Seconds

Eu sei como fazer isso em PHP, mas infelizmente, swift não é PHP :-)

Quaisquer dicas sobre como eu posso conseguir isso rapidamente serão fantásticas! Agradeço antecipadamente!

Respostas:


296

Definir

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

Usar

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

ou

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

A função acima utiliza as tuplas Swift para retornar três valores ao mesmo tempo. Você desestrutura a tupla usando a let (var, ...)sintaxe ou pode acessar membros individuais, se necessário.

Se você realmente precisar imprimi-lo com as palavras, Hoursetc, use algo como isto:

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

Observe que a implementação acima secondsToHoursMinutesSeconds()funciona para Intargumentos. Se você deseja uma Doubleversão, precisará decidir quais são os valores de retorno - podem (Int, Int, Double)ou podem ser (Double, Double, Double). Você pode tentar 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
O último valor (seconds % 3600) % 60pode ser otimizado para seconds % 60. Não há necessidade de extrair as horas primeiro.
zisoft 7/07

@GoZoner - não consigo obter a função printSecondsToHoursMinutesSeconds para funcionar corretamente. É isso que eu tenho em um playground, mas printSecondsToHoursMinutesSeconds não está retornando nada: importar UIKit func secondsToHoursMinutesSeconds (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) = segundos (ToHoursMinutesSeconds (segundos) println ("(h) ) Horas, (m) minutos, (s) segundos ")}
Joe

printSecondstoHoursMinutesSeconds()não retorna nada (veja o -> ()tipo de retorno na declaração da função). A função imprime alguma coisa; que não aparece em um Playground. Se você quiser retornar algo, diga a String, elimine a println()chamada e corrija o tipo de retorno da função.
GoZoner

@GoZoner - apenas uma pergunta rápida na sua sintaxe acima. Como eu declararia que 27005 em uma variável? Agora estou trabalhando em uma ferramenta para molhar os pés rapidamente. Eu tenho uma constante que exibe a quantidade de segundos (gerada após meus cálculos básicos). deixe cocSeconds = cocMinutes * 60. (cocSeconds é o que eu quero usar no lugar de 27005.) Acho que o problema que estou tendo é cocSeconds é um duplo e, na sua sintaxe, você está usando Ints. Como eu ajustaria esse código para colocar uma variável no lugar de 27005? Muito obrigado antecipadamente!!!
31414 Joe

A solução que eu dei funciona para Inttipos devido à natureza das funções /e %. Ou seja, /cai a parte faccional. O mesmo código não funcionará Double. Especificamente 2 == 13/5, mas 2,6 == 13,0 / 5. Portanto, você precisaria de uma implementação diferente para Double. Atualizei minha resposta com uma observação sobre isso.
GoZoner 29/11

171

No macOS 10.10+ / iOS 8.0+ (NS)DateComponentsFormatterfoi introduzido para criar uma sequência legível.

Ele considera o idioma e o idioma do usuário.

let interval = 27005

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

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

Os estilos de unidades disponíveis são positional, abbreviated, short, full, spellOute brief.

Para mais informações, leia a documentação .


19
Usando formatter.unitsStyle = .positionaldá exatamente o que eu quero (que é 7:30:05)! Melhor resposta IMO
Sam

11
E você pode adicionar zero `` `formatter.zeroFormattingBehavior = .pad` `
Eduardo Irias

Como obter o vice-versa disso. Isso é como converter hora, minuto a segundos
Anjo F Syrus

1
@AngelFSyrus Simplyhours * 3600 + minutes * 60
vadian

59

Com base na resposta de Vadian , escrevi uma extensão que pega um Double(que TimeIntervalé um alias de tipo) e cospe uma string formatada como tempo.

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

Aqui estão as várias DateComponentsFormatter.UnitsStyleopções:

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 Normalmente, os valores orientados ao tempo são representados por Doublesno Swift.
Adrian

@ Adrian obrigado pela extensão. Eu gosto do .positional UnitStyle, no entanto, gostaria de exibir ie: 9 segundos como "00:09" em vez de "9", 1min 25 segundos como "01:25" em vez de "1:25". Eu sou capaz de realizar esse cálculo manualmente, com base no valor Double e queria saber se existe uma maneira de incorporar a própria extensão? Obrigado.
Vetuka 7/02/19

FYI: se você deseja manter os 0s antes de um valor de um dígito (ou simplesmente se o valor for 0), é necessário adicionar isso formatter.zeroFormattingBehavior = .pad. Isso nos daria:3660.asString(style: .positional) // 01:01:00
Moucheg

27

Criei um mashup de respostas existentes para simplificar tudo e reduzir a quantidade de código necessária para o 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)")                
}

Impressões:

00:01:40


5
Do meu ponto de vista, adicionar fechamento não simplifica nada. Existe alguma boa razão para o fechamento?
derpoliuk

@derpoliuk Eu precisava do fechamento para as minhas necessidades específicas em meu aplicativo
David Procure

24

Aqui está uma abordagem mais estruturada / flexível: (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)

Ter uma abordagem como essa permite adicionar análises de conveniência como estas:

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

Deve-se notar que abordagens baseadas em números inteiros não levam em consideração o dia / segundo bissexto. Se o caso de uso estiver lidando com datas / horas reais, Data e calendário devem ser usados.


Você poderia adicionar monthà sua implementação? Desde já, obrigado!
Ixany

3
Você deve usar o NSCalendar (calendário) para algo assim.
NoLongerContributingToSE

Você pode substituir return number < 10 ? "0\(number)" : "\(number)"usando um formatador de string return String(format: "%02d", number)que adiciona automaticamente um zero quando o número estiver abaixo de 10
Continuum

19

No 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 chamar a função

    timeString(time: TimeInterval(i))

Voltará 02:44:57


Soberbo! Exatamente o que eu exigi. Alterei o 9897 para uma variável Int e obtive os resultados desejados. Obrigado!
David_2877 6/07

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

O que é truncatingRemainder? O Xcode não reconhece isso para mim.
Alfi

10

Aqui está outra implementação simples no 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

Solução SWIFT 3.0 baseada aproximadamente na acima, usando extensões.

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

  }
}

Usá-lo com o AVPlayer chamando assim?

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

8

Eu respondi a uma pergunta semelhante , no entanto, você não precisa exibir milissegundos no resultado. Portanto, minha solução requer iOS 10.0, tvOS 10.0, watchOS 3.0 ou macOS 10.12.

Você deve ligar func convertDurationUnitValueToOtherUnits(durationValue:durationUnit:smallestUnitDuration:)da resposta que eu já mencionei aqui:

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

De acordo com a resposta do GoZoner , escrevi uma extensão para obter a hora formatada de acordo com as horas, minutos e 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

Aqui está o que eu uso para o meu Music Player no Swift 4+. Estou convertendo segundos Int em formato de String legível

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

Use assim:

print(7903.toAudioString)

Resultado: 2:11:43


3

A resposta de @ r3dm4n foi ótima. No entanto, eu também precisava de uma hora. Apenas no caso de alguém precisar também aqui 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

Código mais recente: XCode 10.4 Swift 5

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

2

Resposta rápida 5 & String, em formato apresentável

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

A maneira mais simples:

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)

Pode ser ldpara double em vez de dpara int.
Nik Kov

1

NSTimeIntervalé Doublefazê-lo com extensão. Exemplo:

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

Fui em frente e criei um encerramento para isso (no Swift 3).

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

Isso fornecerá m = 4 es = 59. Portanto, você pode formatar isso como desejar. Você também pode querer adicionar horas, se não mais informações.


0

Swift 4 eu estou usando esta extensão

 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

a resposta de neek não está correta.

aqui está a versão correta

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

Outra maneira seria converter segundos para data e levar os componentes, ou seja, segundos, minutos e hora da própria data. Esta solução tem limitação apenas até 23:59:59

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.