I'm using NSDateComponentsFormatter on iOS 8 and i've noticed a possible bug in the implementation of the stringFromTimeInterval:
method, but I can't say it for sure.
This is how I have set up the formatter:
NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
formatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorDefault;
formatter.allowedUnits = NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
formatter.unitsStyle = NSDateComponentsFormatterUnitsStylePositional;
Now when I pass anything less than 60 seconds to the stringFromTimeInterval:
method, the formatter returns either 1
or 01
, which is obviously wrong, it should return something like 0:XX
.
What I have also noticed is that by changing the unitsStyle
to anything but NSDateComponentsFormatterUnitsStylePositional
the method returns a correct string!
Example:
With unitsStyle = NSDateComponentsFormatterUnitsStylePositional
:
(lldb) po [formatter stringFromTimeInterval:12]
= "1"
=> Should return "0:12"!!
With unitsStyle = NSDateComponentsFormatterUnitsStyleAbbreviated
:
(lldb) po [formatter stringFromTimeInterval:12]
= "12 s"
=> Correct!
This lets me think that it is a bug in Apple's implementation of the API, or am I missing something?
This seems to still be an issue with iOS 8.3 and Xcode 6.3. There is a way to work around it however, which, if not perfect, at least allows you to get proper formatting without having to deal with splitting time relevant components yourself.
Here is an example workaround in Swift (which you can test in a Playground)
let formatter = NSDateComponentsFormatter()
formatter.unitsStyle = .Positional
formatter.zeroFormattingBehavior = .Pad
formatter.allowedUnits = .CalendarUnitMinute | .CalendarUnitSecond
Calling let string = formatter.stringFromTimeInterval(4)
will print "00:04".
Calling let string = formatter.stringFromTimeInterval(88)
will print "01:28".
Now you can play with the allowedUnits
depending on whether you have more than 1 hour or 1 day. For example in a class which has a formatter
property:
func updateTimeFormatterForTime(time: NSTimeInterval) {
if time > 3600 && time < 24 * 3600 {
self.formatter.allowedUnits = .CalendarUnitHour | .CalendarUnitMinute | .CalendarUnitSecond
} else if time > 24 * 3600 {
self.formatter.allowedUnits = .CalendarUnitDay | .CalendarUnitHour | .CalendarUnitMinute | .CalendarUnitSecond
}
}
NSDateComponentsFormatterZeroFormattingBehaviorDefault means drop leading zeros, pad other zeros for Positional units style.
e.g. with the allowedUnits values in your code, [formatter stringFromTimeInterval:120] result is "2:00" in instead of "0d 0:02:00", because formatter object removed all leading zero values.
If you want to reserve the zero value places, you can use NSDateComponentsFormatterZeroFormattingBehaviorNone
For why [formatter stringFromTimeInterval:12] result is 1, I think formatter object finds "12" alone is ambiguous as it has no trailing values or indicator for meaning like in "1d" for example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With