NSDateComponentsFormatter's stringFromDate(_, toDate:) returns nil
From the headerdoc:
/* Bitmask of units to include. Set to 0 to get the default behavior.
Note that, especially if the maximum number of units is low, unit
collapsing is on, or zero dropping is on, not all allowed units may
actually be used for a given NSDateComponents. Default value is the
components of the passed-in NSDateComponents object, or years |
months | weeks | days | hours | minutes | seconds if passed an
NSTimeInterval or pair of NSDates.
Allowed units are:
NSCalendarUnitYear
NSCalendarUnitMonth
NSCalendarUnitWeekOfMonth (used to mean "quantity of weeks")
NSCalendarUnitDay
NSCalendarUnitHour
NSCalendarUnitMinute
NSCalendarUnitSecond
Specifying any other NSCalendarUnits will result in an exception.
*/
var allowedUnits: NSCalendarUnit
So:
let formatter = NSDateComponentsFormatter()
formatter.calendar = NSCalendar.currentCalendar()
formatter.allowedUnits = nil
formatter.allowedUnits |= .CalendarUnitYear
formatter.allowedUnits |= .CalendarUnitMonth
formatter.allowedUnits |= .CalendarUnitWeekOfMonth
formatter.allowedUnits |= .CalendarUnitDay
formatter.allowedUnits |= .CalendarUnitHour
formatter.allowedUnits |= .CalendarUnitMinute
formatter.allowedUnits |= .CalendarUnitSecond
let referenceDate = NSDate(timeIntervalSinceReferenceDate: 0)
let intervalDate = NSDate(timeInterval: 214458810, sinceDate: referenceDate)
let string = formatter.stringFromDate(referenceDate, toDate: intervalDate)
// -> "6y 9m 2w 4d 3:53:30"
When we omit one of them in the middle:
formatter.allowedUnits = nil
formatter.allowedUnits |= .CalendarUnitYear
formatter.allowedUnits |= .CalendarUnitMonth
formatter.allowedUnits |= .CalendarUnitWeekOfMonth
formatter.allowedUnits |= .CalendarUnitDay
// formatter.allowedUnits |= .CalendarUnitHour
formatter.allowedUnits |= .CalendarUnitMinute
formatter.allowedUnits |= .CalendarUnitSecond
'Specifying positional units with gaps is ambiguous, and therefore unsupported'
error :) That is the "gap" means, I think.
This works for me to get rid of the "gaps":
formatter.allowedUnits = .DayCalendarUnit | .HourCalendarUnit | .MinuteCalendarUnit | .SecondCalendarUnit
Not specifying a unit style will cause this in Swift 4 and 5.
let componentsFormatter = DateComponentsFormatter()
componentsFormatter.unitsStyle = .full
Other unit styles: .spellOut
, .short
, .brief
, .abbreviated
, .positional