1

I use a DateComponentsFormatter to get a String to display a TimeInterval. In this particular case I want to show only minutes and hours (if an hour or greater).

But if I use the same instance of DateComponentsFormatter (trying to prevent repetitive re-initialization, using a lazy var) and first call the method with a duration greater than or equal to an hour, the second time if it's called with a value less than 10 minutes it always has a leading zero.

class DurationStringHelper {
    private lazy var timeFormatter: DateComponentsFormatter = {
        let formatter = DateComponentsFormatter()
        formatter.zeroFormattingBehavior = .dropLeading
        formatter.allowedUnits = [.hour, .minute]

        return formatter
    }()

    public func duration(seconds: TimeInterval) -> String {        
        return timeFormatter.string(from: seconds)!
    }
}

// CASE 1:
let c1 = DurationStringHelper()
c1.duration(seconds: 3600) // "1:00"
c1.duration(seconds: 60) // "01"  ARGH WHY GOD WHY!!!

// CASE 2: (first call less than an hour)
let c2 = DurationStringHelper()
c2.duration(seconds: 3599) // "59"
c2.duration(seconds: 60) // "1"

My current approach is just to check if there's an unwanted leading zero (checking for the presence of the character and checking whether the seconds falls into a certain range) and removing it. This is an ugly hack though.

This seems like a bug to me, so I submitted a report to Apple. Don't see why one call to a formatter should affect later calls.

Note: the same thing doesn't seem to happen if you include .day in allowed units, and first call it with a time interval of a day or more, and then again with less than 10 hours. Although it does happen if you set allowedUnits to [.minute, .second] and then call it first with 60 seconds and then with 9 seconds.

shim
  • 9,289
  • 12
  • 69
  • 108
  • Have you tried `DateComponentsFormatter.ZeroFormattingBehavior.dropAll`? – l'L'l Mar 03 '17 at 17:34
  • Yes the zero formatting behavior doesn't seem to make a difference – shim Mar 03 '17 at 17:35
  • Did you ever figure out the answer, @shim? – epologee Jan 19 '18 at 15:52
  • I think I just stuck with my hack mentioned in the post above. – shim Jan 19 '18 at 16:24
  • try to compute the formatter every time like `private var timeFormatter: DateComponentsFormatter { ... }`, because probably something funky is happening after it formats the hour `1` and because the hour `0` will be omitted as leading zero at `c1` not the leading zero of the minutes; it seems like bug to me, but I wouldn't make sudden judges. – holex Jan 19 '18 at 17:19
  • Initializing a formatter is [inefficient](https://stackoverflow.com/questions/8832768/why-is-allocating-or-initializing-nsdateformatter-considered-expensive) so you don't want to repeatedly initialize a formatter depending on the situation (e.g. rows in a table). – shim Jan 19 '18 at 20:00

0 Answers0