Thorsten Karrer
Thorsten Karrer

Reputation: 1365

NSDateComponentsFormatter leaves out units. Why?

I am trying to use NSDateComponentsFormatter to generate user readable strings of durations. While it generally works quite well, sometimes the results are strange (or just plain wrong):

// 22d 23h 15m 34.123s
let timeInterval: NSTimeInterval = 34.123 + 60.0 * (15.0 + 60.0 * (23.0 + 24.0 * 22.0))

let formatter = NSDateComponentsFormatter()
formatter.allowedUnits = [NSCalendarUnit.Year, NSCalendarUnit.Day, NSCalendarUnit.Hour, NSCalendarUnit.Minute, NSCalendarUnit.Second]
formatter.maximumUnitCount = 2
formatter.unitsStyle = .Abbreviated
formatter.zeroFormattingBehavior = .Default

// expected result:     22d 23h
// actual result:       23d 16m
let result = formatter.stringFromTimeInterval(timeInterval)

Not only does it leave out the hours field in this example, but it also rounds up the days and the minutes. I would assume that once the days are rounded up, it should 'use up' the remaining minutes in the rounding. From the result, a user will think that the duration is definitely more than 23 days, while in reality it is not.

Upvotes: 1

Views: 270

Answers (2)

vadian
vadian

Reputation: 285069

Consider that on March 27, 2016 daylight saving time changes in many countries.

The absolute time interval might not be the same as the calendar calculation based on calendar units.

Whenever possible avoid date math with literal numbers.

From the NSDateComponentsFormatter documentation regarding its calendar property

The formatter uses the calendar in this property to format values that do not have an inherent calendar of their own. For example, the formatter uses this calendar when formatting an NSTimeInterval value.

Update:

See also this example, it creates a date March 27, 2016, adds add one day via date components and calculates the interval

let calendar = NSCalendar.currentCalendar()
let startDate = calendar.dateWithEra(1, year: 2016, month: 3, day: 27, hour: 0, minute: 0, second: 0, nanosecond: 0)!
let endDate = calendar.dateByAddingUnit(.Day, value: 1, toDate: startDate, options: [])!

let formatter = NSDateComponentsFormatter()
formatter.allowedUnits = [.Hour, .Minute, .Second]
formatter.unitsStyle = .Abbreviated

let result = formatter.stringFromDate(startDate, toDate: endDate)
print(result) // 23h

Upvotes: 4

Duncan C
Duncan C

Reputation: 131408

Based on the limited testing I did, NSDateComponentsFormatter is buggy in iOS versions < iOS 9, and in Mac OS < 10.11.

I got flat wrong values for some date components, where it was not a rounding issue.

See my question at this link:

Getting screwy results from NSDateComponentsFormatter

Upvotes: 2

Related Questions