Swift - konwersja liczby całkowitej na godziny / minuty / sekundy

141

Mam (trochę?) Podstawowe pytanie dotyczące konwersji czasu w języku Swift .

Mam liczbę całkowitą, którą chciałbym przekonwertować na godziny / minuty / sekundy.

Przykład: Int = 27005 dałoby mi:

7 Hours  30 Minutes 5 Seconds

Wiem, jak to zrobić w PHP, ale niestety, swift to nie PHP :-)

Wszelkie wskazówki, jak szybko to osiągnąć, byłyby fantastyczne! Z góry dziękuję!

Joe
źródło

Odpowiedzi:

306

Definiować

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

Posługiwać się

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

lub

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

Powyższa funkcja wykorzystuje krotki Swift do zwracania trzech wartości jednocześnie. Niszczysz krotkę za pomocą let (var, ...)składni lub w razie potrzeby możesz uzyskać dostęp do poszczególnych członków krotki.

Jeśli naprawdę chcesz wydrukować go ze słowami Hoursitp., Użyj czegoś takiego:

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

Zauważ, że powyższa implementacja secondsToHoursMinutesSeconds()działa dla Intargumentów. Jeśli chcesz mieć Doublewersję, musisz zdecydować, jakie są zwracane wartości - mogą być (Int, Int, Double)lub mogą być (Double, Double, Double). Możesz spróbować czegoś takiego:

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
źródło
14
Ostatnia wartość (seconds % 3600) % 60może zostać zoptymalizowana do seconds % 60. Nie ma potrzeby wcześniejszego wyodrębniania godzin.
zisoft
@GoZoner - nie wydaje mi się, aby funkcja printSecondsToHoursMinutesSeconds działała poprawnie. To jest to, co mam na placu zabaw, ale printSecondsToHoursMinutesSeconds nie zwraca niczego: 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 (seconds: Int) -> () {let (h, m, s) = secondsToHoursMinutesSeconds (sekundy) println ("(h ) Godziny, (m) Minuty, (s) Sekundy ”)}
Joe
printSecondstoHoursMinutesSeconds()nic nie zwraca (patrz -> ()typ zwracany w deklaracji funkcji). Funkcja wypisuje coś; który nie pojawia się na placu zabaw. Jeśli chcesz, aby coś zwróciło, powiedz a, a Stringnastępnie usuń println()wywołanie i popraw typ zwracania funkcji.
GoZoner
@GoZoner - tylko krótkie pytanie w Twojej składni powyżej. Jak zadeklarować to 27005 w zmiennej? Pracuję teraz nad narzędziem, które pozwoli szybko zmoczyć stopy. Mam stałą, która wyświetla liczbę sekund (wygenerowaną po moich podstawowych obliczeniach). niech cocSeconds = cocMinutes * 60. (cocSeconds jest tym, czego chcę użyć zamiast 27005.) Myślę, że moim problemem jest to, że cocSeconds jest podwójny, aw twojej składni używasz Ints. Jak bym się zajął dostosowaniem tego kodu, aby wstawić zmienną zamiast 27005? Z góry bardzo dziękuję!!!
Joe
Rozwiązanie, które podałem, działa dla Inttypów ze względu na charakter funkcji /i %. To oznacza, /że część frakcyjna spada. Ten sam kod nie będzie działać Double. W szczególności 2 == 13/5 ale 2,6 == 13,0 / 5. Dlatego potrzebowałbyś innej implementacji Double. Zaktualizowałem moją odpowiedź uwagą na ten temat.
GoZoner
188

W macOS 10.10+ / iOS 8.0+ (NS)DateComponentsFormatterwprowadzono tworzenie czytelnego ciągu.

Uwzględnia ustawienia regionalne i język użytkownika.

let interval = 27005

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

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

Dostępne są jednostkowe style positional, abbreviated, short, full, spellOuti brief.

Aby uzyskać więcej informacji, przeczytaj dokumentację .

vadian
źródło
22
Używanie formatter.unitsStyle = .positionaldaje dokładnie to, czego chcę (czyli 7:30:05)! Najlepsza odpowiedź IMO
Sam
14
I możesz dodać zero `` formatter.zeroFormattingBehavior = .pad ''
Eduardo Irias,
Jak to uzyskać na odwrót. Oto jak zamienić godzinę, minutę na sekundy
Angel F Syrus,
1
@AngelFSyrus Simplyhours * 3600 + minutes * 60
vadian
66

Opierając się na odpowiedzi Vadiana , napisałem rozszerzenie, które pobiera Double(którego TimeIntervaljest aliasem typu) i wypluwa ciąg sformatowany jako czas.

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

Oto, DateComponentsFormatter.UnitsStylejak wyglądają różne opcje:

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
źródło
@MaksimKniazev Zwykle wartości zorientowane na czas są reprezentowane przez DoublesSwift.
Adrian
@Adrian dziękuję za rozszerzenie. Podoba mi się .pozycyjny UnitStyle, ale chciałbym wyświetlić np .: 9 sekund jako „00:09” zamiast „9”, 1 min 25 sekund jako „01:25” zamiast „1:25”. Jestem w stanie wykonać to obliczenie ręcznie, w oparciu o wartość Double i zastanawiałem się, czy istnieje sposób na włączenie do samego rozszerzenia? Dziękuję Ci.
Vetuka,
FYI: jeśli chcesz zachować 0 przed wartością jednocyfrową (lub po prostu jeśli wartość wynosi 0), musisz to dodać formatter.zeroFormattingBehavior = .pad. To dałoby nam:3660.asString(style: .positional) // 01:01:00
Moucheg
27

Stworzyłem połączenie istniejących odpowiedzi, aby uprościć wszystko i zmniejszyć ilość kodu potrzebnego do 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)"
}

Stosowanie:

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

Wydruki:

00:01:40

David Seek
źródło
5
Z mojej perspektywy dodanie zamknięcia niczego tak naprawdę nie upraszcza. Czy jest jakiś dobry powód do zamknięcia?
derpoliuk
@derpoliuk Potrzebowałem zamknięcia do moich konkretnych potrzeb w mojej aplikacji
David Seek
24

Oto bardziej ustrukturyzowane / elastyczne podejście: (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)

Takie podejście umożliwia dodanie wygodnej analizy w następujący sposób:

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

Należy zauważyć, że podejścia oparte wyłącznie na liczbach całkowitych nie uwzględniają dnia / sekund przestępnych. Jeśli przypadek użycia dotyczy rzeczywistych dat / godzin, należy użyć daty i kalendarza .

NoLongerContributingToSE
źródło
Czy mógłbyś dodać coś monthdo swojej realizacji? Z góry dziękuję!
IXany
3
Do czegoś takiego powinieneś użyć NSCalendar (Calendar).
NoLongerContributingToSE,
Możesz zastąpić return number < 10 ? "0\(number)" : "\(number)", używając programu formatującego ciąg, return String(format: "%02d", number)który automatycznie dodaje zero, gdy liczba jest mniejsza niż 10
Continuum
20

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

Aby wywołać funkcję

    timeString(time: TimeInterval(i))

Powróci 02:44:57

DialDT
źródło
Wspaniały! Właśnie tego potrzebowałem. Zmieniłem 9897 na zmienną Int i uzyskałem pożądane wyniki. Dziękuję Ci!
David_2877
To nie działa dla mnie w Swift 5.3. Wysyłam mu wartość TimeInterval 217368 milisekund i zwraca 60:22:48. 217368 milisekund to 10:02.
SouthernYankee65
13

Szybki 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
źródło
Co to jest truncatingRemainder? Xcode nie rozpoznaje go za mnie.
Ahmadreza,
11

Oto kolejna prosta implementacja w 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
źródło
9

Rozwiązanie SWIFT 3.0 oparte w przybliżeniu na powyższym z rozszerzeniami.

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

  }
}

Użyć go z AVPlayerem nazywającym to w ten sposób?

 let dTotalSeconds = self.player.currentTime()
 playingCurrentTime = dTotalSeconds.durationText
user3069232
źródło
8

Odpowiedziałem na podobne pytanie , ale nie musisz wyświetlać milisekund w wyniku. Dlatego moje rozwiązanie wymaga systemu iOS 10.0, tvOS 10.0, watchOS 3.0 lub macOS 10.12.

Powinieneś zadzwonić func convertDurationUnitValueToOtherUnits(durationValue:durationUnit:smallestUnitDuration:)z odpowiedzi, o której już tutaj wspomniałem:

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
źródło
8

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

Stosowanie:

let seconds : Int = 119
print(seconds.secondsToTime()) // Result = "00:01:59"
ZAFAR007
źródło
3

Zgodnie z odpowiedzią GoZonera napisałem rozszerzenie, aby uzyskać formatowanie czasu według godzin, minut i sekund:

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
źródło
3

Oto, czego używam w moim odtwarzaczu muzycznym w Swift 4+. Konwertuję Int sekund na czytelny format 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)
    }
}

Użyj w ten sposób:

print(7903.toAudioString)

Wynik: 2:11:43

Maksim Kniazev
źródło
3

Odpowiedź @ r3dm4n była świetna. Jednak potrzebowałem też godziny. Na wypadek, gdyby ktoś inny też potrzebował tutaj, jest:

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
źródło
3

Najnowszy kod: XCode 10.4 Swift 5

extension Int {
    func timeDisplay() -> String {
        return "\(self / 3600):\((self % 3600) / 60):\((self % 3600) % 60)"
    }
}
Saranjith
źródło
2

Szybka odpowiedź 5 i ciągów, w reprezentacyjnym formacie

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

Stosowanie:

print(secondsToHoursMinutesSecondsStr(seconds: 20000)) // Result = "5 h 33 min 20 sec"
Asad Ali Choudhry
źródło
1

Najprostszy sposób imho:

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
źródło
Może to być lddouble zamiast dfor int.
Nik Kov
1

NSTimeIntervalto Doublezrobić to z rozszerzeniem. Przykład:

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
źródło
0

Poszedłem do przodu i stworzyłem dla tego zamknięcie (w Swift 3).

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

To da m = 4 i s = 59. Możesz więc sformatować to, jak chcesz. Możesz oczywiście dodać godziny, jeśli nie więcej informacji.

Torbjørn Kristoffersen
źródło
0

Swift 4 Używam tego rozszerzenia

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

użytkowanie

// assume myTimeInterval = 96460.397    
myTimeInteval.stringFromInterval() // 1d 2h 47m 40s 397ms
caesss
źródło
0

odpowiedź neeka jest nieprawidłowa.

oto poprawna wersja

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
źródło
0

Innym sposobem byłoby przeliczenie sekund na datę i pobranie składników, tj. Sekund, minut i godzin od samej daty. To rozwiązanie ma ograniczenia tylko do 23:59:59

Wasim
źródło
0

zamień liczbę na czas jako ciąg

func convertToHMS(number: Int) -> String {
  let hour    = number / 3600;
  let minute  = (number % 3600) / 60;
  let second = (number % 3600) % 60 ;
  
  var h = String(hour);
  var m = String(minute);
  var s = String(second);
  
  if h.count == 1{
      h = "0\(hour)";
  }
  if m.count == 1{
      m = "0\(minute)";
  }
  if s.count == 1{
      s = "0\(second)";
  }
  
  return "\(h):\(m):\(s)"
}
print(convertToHMS(number:3900))
Sami
źródło