35

Basically what I want is to get the value of a time interval represented in hours only, without rounding it to full hours (using NSDateComponentsFormatter to get it properly formatted and localized). I don't know if I misunderstand the use of NSDateComponentsFormatter.allowsFractionalUnits, but I can't get the formatter to give me a decimal value. Can anyone help me spot my error or tell me in what way I misunderstand this?

From Apple docs about allowsFractionalUnits property:

Fractional units may be used when a value cannot be exactly represented using the available units. For example, if minutes are not allowed, the value “1h 30m” could be formatted as “1.5h”.

Swift example code:

let formatter = NSDateComponentsFormatter()
formatter.unitsStyle = .Abbreviated
formatter.allowedUnits = .Hour
formatter.allowsFractionalUnits = true

let onePointFiveHoursInSeconds = NSTimeInterval(1.5 * 60.0 * 60.0)
print(formatter.stringFromTimeInterval(onePointFiveHoursInSeconds)!)
//"1h" instead of expected "1.5h"

Same example in Objective-C code:

NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleAbbreviated;
formatter.allowedUnits = NSCalendarUnitHour;
formatter.allowsFractionalUnits = YES;

NSTimeInterval onePointFiveHoursInSeconds = 1.5 * 60.0 * 60.0;
NSLog(@"%@", [formatter stringFromTimeInterval:onePointFiveHoursInSeconds]);
//"1h" instead of expected "1.5h"

Update: I have reported a bug to Apple about this problem (rdar://22660145).

gmw
  • 417
  • 3
  • 15
osanoj
  • 1,035
  • 10
  • 9
  • 6
    Interesting problem. It seems that allowsFractionalUnits does not work at all. – Martin R Sep 11 '15 at 12:40
  • I think that is basically bad idea. For example, 1.33h, how much is that? This could be misleading in case it is formatted for end user. I would rather display 1:20 – Krešimir Prcela Jan 21 '16 at 23:44
  • @Prcela: This is my simplified example, my motivation for using it is a bit irrelevant here. Since it is documented that the NSDateComponentsFormatter is supposed to support this it should be possible to get that output. – osanoj Jan 26 '16 at 21:45
  • Still appears to be the case in Xcode 10.2.1 (i.e. Swift 5, or rather its Foundation equivalent) – gmw May 05 '19 at 18:36
  • looks like it is still broken in XCode 13.2.1 (I cant say I am surprised, I cant even muster the energy to be disappointed) – Gavin May 14 '22 at 12:41
  • I actually got a response on my radar back in 2019: "Unfortunately, the fix here is nontrivial, and other tasks have taken precedence. This is something we'd still like to resolve, and we’ll be tagging this for a more prioritized fix." – osanoj Sep 23 '22 at 11:10

3 Answers3

11

According to Open Radar #32024200:

After doing some digging (disassembling Foundation), it looks like every call to -[_unitFormatter stringFromNumber:] in -[NSDateComponentsFormatter _stringFromDateComponents:] is passed an +[NSNumber numberWithInteger:] which drops floating point data.

You're not doing anything wrong. The flag is simply broken.

Ssswift
  • 916
  • 10
  • 20
2

Looking at the documentation, it’s using a lot of interesting language (emphasis mine):

Fractional units may be used when a value cannot be exactly represented using the available units. For example, if minutes are not allowed, the value “1h 30m” could be formatted as “1.5h”.

While to me it seems that it would only make sense for the values in the documentation to be values that actually work, it’s certainly possible that there is some combination of time value, formatter options, and calendar/locale settings that would make this work. Definitely worth filing a Radar on both the functionality and the documentation.

Jeff Kelley
  • 19,021
  • 6
  • 70
  • 80
  • I have so far not had any progress with my bug report, so if anyone feels like filing a radar themselves, please do so, since it might help bring some Apple attention to this issue. – osanoj May 18 '16 at 08:57
  • One would expect that if 1h 30m becomes 1.5h then 8h 30m should become 8.5h, spoiler; it doesnt. The example is also interesting, as the only reason 1h 30m cant be displayed is due to minutes being disallowed. Personally I feel thats Swifts whole time/date library is sub par, its API is certainly hugely inconsistent in my opinion, – Gavin May 14 '22 at 12:46
-5

enter code hereAs far as i understood , You want to display time in 12-Hour formate Right ? Below is the code : Swift->

let dateAsString = "20:30"
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HH:mm"
let date = dateFormatter.dateFromString(dateAsString)

dateFormatter.dateFormat = "h:mm a"
let date12 = dateFormatter.stringFromDate(date!)
txtText.text = date12 
vivek agravat
  • 251
  • 2
  • 10