How to format time in "h m s" format in swift using `Duration.TimeFormatStyle`

148 Views Asked by At

I wrote an extension of TimeInterval which returns a string in the format "h m s". For example, 5 hours, 33 minutes and 15 seconds would be written as "5h 33m 15s". This is the code:

extension TimeInterval {
  init(hours: Int, minutes: Int, seconds: Int) {
    self = Double(hours * 3600 + minutes * 60 + seconds)
  }

  func toString() -> String {
    let totalSeconds = Int(self)
    let hours = totalSeconds / 3600
    let minutes = (totalSeconds % 3600) / 60
    let seconds = totalSeconds % 60

    if hours >= 1 {
      return "\(hours)h \(minutes)m"
    } else if minutes >= 1 {
      return "\(minutes)m \(seconds)s"
    } else {
      return "\(seconds)s"
    }
  }
}

I'd like to achieve this same functionality using Apple's Duration.TimeFormatStyle fashion. For example,I can do the following:

let style = Duration.TimeFormatStyle(pattern: .hourMinuteSecond)
var formattedTime = Duration.seconds(someInterval).formatted(style)

which I think is better practice than writing a toString() function as an extension of TimeInterval, but I'm not sure how to go about creating this style. Any guidance would be great.

2

There are 2 best solutions below

4
Leo Dabus On BEST ANSWER

What you are looking for is called Duration UnitsFormatStyle units with narrow width:

let duration: Duration = .seconds(15 + 33 * 60 + 5 * 3600)
let string = duration.formatted(.units(width: .narrow)) // "5h 33m 15s"
0
Sweeper On

You can use the units format style.

From your toString code, it seems like

  • you only want to display the units hours, minutes and seconds
  • show no more than 2 units at a time
  • even if a unit is 0, show "0" as well

Therefore, you can pass these options to units:

let formattedTime = Duration.seconds(someDuration).formatted(
    .units(
        // by default, the allowed units are hour, minute and second
        width: .narrow, // for the h, m, s unit names
        maximumUnitCount: 2, // show no more than 2 units
        zeroValueUnits: .show(length: 1) // show 0s as "0"
    )
)