3

So, I'm using a DateComponentsFormatter in a situation where I need to format only the nanoseconds of a given TimeInterval. The problem is that it just does not work as intended, or I'm confusing something here that I'm not aware of.

Here are 3 quick tests to illustrate what I'm saying.

1st Scenario

let fooFormatter = DateComponentsFormatter()

fooFormatter.allowedUnits = [.second, .nanosecond]
fooFormatter.unitsStyle = .full
fooFormatter.collapsesLargestUnit = true
fooFormatter.allowsFractionalUnits = true

print(fooFormatter.string(from: 42.42424242424242424242424242424242))

Output: Optional("42 seconds").

Expected: Because this collapses the "largest unit" - seconds in this case -, it's expected to be something like (Optional("42.42424242424242424242424242424242 * 1e^9")).

2nd Scenario

let fooFormatter = DateComponentsFormatter()

fooFormatter.allowedUnits = [.second, .nanosecond]
fooFormatter.unitsStyle = .full
fooFormatter.allowsFractionalUnits = true

print(fooFormatter.string(from: 42.42))

Output: Optional("42 seconds").

Expected: Even without opting to not collapse the .second, I expected anything close to (Optional("42 seconds 0.42 * 1e^9 nanoseconds")).

3rd Scenario

let fooFormatter = DateComponentsFormatter()
fooFormatter.allowedUnits = [.nanosecond]
fooFormatter.unitsStyle = .full
fooFormatter.allowsFractionalUnits = true

print(fooFormatter.string(from: 42.424242))

Output: nil.

Expected: Now it won't even recognize the TimeInterval as valid - when it's defined as the smallest fraction possible - and obviously expected everything in nanoseconds.

It's important to notice that I've used allowsFractionalUnits = true, which is even more alarming, because this behavior is also not working in the outputs given above (see the intended behavior here).

Thanks in advance.

Guilherme Matuella
  • 2,193
  • 1
  • 17
  • 32

1 Answers1

5

DateComponentsFormatter does not support nanoseconds.

See the documentation for allowedUnits, which does not include nanoseconds. It says:

The allowed calendar units are:

  • year
  • month
  • weekOfMonth
  • day
  • hour
  • minute
  • second

Assigning any other calendar units to this property results in an exception.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • So, allowing `.nanosecond` here is a bad design, right? I mean, why would you allow a typed `NSCalendar.Unit` where you can't exactly know how to deal with? There is no problem with backwards compatibility as well, because it just subclasses the `Formatter` class. Also, because of this, the `.allowsFractionalUnits` does not work to separate into anything less than `second`, correct? This API is not clear at all for me. – Guilherme Matuella Feb 15 '19 at 21:10
  • 1
    Yeah, there’s lots I’d quibble with: Why does it say it will raise an exception, but doesn’t? Why don’t they at least log some error message? Shouldn’t `allowsFractionalUnits` be a little more consistent and be married with something about `maximumFractionalDigits` or something like that? Like you, I once wasted more time than I’d like, trying to figure out what was going on here. That is an hour or two I wish I could get back. My new SOP is to double check both documentation and header comments before investing too much time diagnosing strange behavior. – Rob Feb 15 '19 at 21:25
  • 1
    Ok, that makes sense - and I'm in the same boat as you. Thanks for your time to answer me. – Guilherme Matuella Feb 16 '19 at 17:01
  • macOS 12.6.5 / Xcode 14.2: assigning `.nanoseconds` crashes. At least that is consistent now ... – soundflix Jul 04 '23 at 20:10