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ę!

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

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ę .


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

@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


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 .


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


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

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
}

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

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

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"

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"

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


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

3

Najnowszy kod: XCode 10.4 Swift 5

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

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"

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)

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

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.


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

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
}

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


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))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.