16

I want to print, e.g., the value 1.5 as "1.5 sec", like Safari's timeline does, and I'm trying to use DateComponentsFormatter for it.

Unfortunately, its .allowedUnits only goes down to .second (even though the enum has .nanosecond). I tried setting .allowsFractionalUnits = true, but I'm still getting "0 sec".

Is there any way to get fractional seconds out of DateComponentsFormatter?

Ssswift
  • 916
  • 10
  • 20
  • Which method do you use to format what? – Willeke Apr 09 '17 at 13:29
  • Willeke: The only method which accepts the parameter `1.5`: `string(from:​ Time​Interval)`. – Ssswift Apr 09 '17 at 17:33
  • The time interval is internally translated into NSDateComponents. – Willeke Apr 09 '17 at 22:42
  • Willeke: Yes, I assumed that was the case. That's the logical way for them to have implemented it. So? – Ssswift Apr 10 '17 at 04:20
  • Did you find a way to get "1.5 sec" from the DateComponentsFormatter? Can't be true that Apple forgot to implement this. – Daniel Jun 20 '19 at 20:20
  • 1
    @Daniel Sadly, it appears to be very true, as of iOS 13.3.I haven't found a way of getting a localised output of "1.5 sec" from DateComponentsFormatter. It will only output "1.5". Super-annoying. – Womble Mar 10 '20 at 04:55

1 Answers1

2

For positional units style, DateComponentsFormatter can be used together with NumberFormatter to get locale-aware string.

func format(_ seconds: TimeInterval) -> String {
  let components = DateComponentsFormatter()
  components.allowedUnits = [.minute, .second]
  components.allowsFractionalUnits = true // does not work as expected
  components.unitsStyle = .positional
  components.zeroFormattingBehavior = .pad

  let fraction = NumberFormatter()
  fraction.maximumIntegerDigits = 0
  fraction.minimumFractionDigits = 0
  fraction.maximumFractionDigits = 3
  fraction.alwaysShowsDecimalSeparator = false

  let fractionalPart = NSNumber(value: seconds.truncatingRemainder(dividingBy: 1))

  return components.string(from: seconds)! + fraction.string(from: fractionalPart)!
}

print(Locale.current) // ru_RU (current)
print(format(600.5))  // 10:00,5 <- note locale specific decimal separator

Unfortunately, using this method for other date styles is even more hacky.

nikstar
  • 51
  • 4