What am I doing wrong with allowsFractionalUnits on NSDateComponentsFormatter? - ios

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).

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.

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.

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

Related

iOS Swift NSDateFormatter gives different output

Following piece of code in Swift to generate time stamp of current time, but sometime it gives wrong output like different Year 2016 instead of 2015.
let todaysDate: NSDate = NSDate()
let dateFormatter:NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
var currentTimeStamp = dateFormatter.stringFromDate(todaysDate)
print("** Current time stamp: " + currentTimeStamp)
I don't understand why does same piece of code give different output?
This was fixed for me by changing YYYY to yyyy as suggested by BhavukJain in the comments.
From https://8mobile.wordpress.com/2015/05/28/nsdateformatter-nsdate-wrong-year/
y = Year, normally the length specifies the padding, but for two letters it also specifies the maximum length.
Y = Year (in “Week of Year” based calendars), this year designation is used in ISO year-week calendar as defined by ISO 8601, but can be used in non-Gregorian based calendar systems where week date processing is desired. May not always be the same value as calendar year.

Changing to 24-Hour Time on iPhone 6, iOS 9 leads to app crash

I developed an application in which I use a singleton for keeping a single instance of an NSDateFormatter.
My date formatter is initialized as below:
timeDateFormatter = NSDateFormatter()
timeDateFormatter.locale = NSLocale(localeIdentifier:EN_US_POSIX_LocaleIdentifier)
timeDateFormatter.dateFormat = StringDateType.DetailSigningHour.rawValue
let EN_US_POSIX_LocaleIdentifier = "en_US_POSIX"
With 24-Hour Time turned off the application runs as it should, but when going to Settings-->General-->Date&Time and turning on 24-Hour Time, then going and tapping on the app icon, the app tries to come up and then immediately exit.
I read that this may be an unknown issue for Apple.
Can you help me with some more information about this?
Update
When the system time was changed from 12-hour format to 24-hour format, my date formatter was messed up.
The good part is that the system is sending a notification (NSCurrentLocaleDidChangeNotification) letting you know that the locale has changed. All what I have done was to add an observer for this notification and to re-initialize my date formatter.
Setting the locale to en_US_POSIX, you force the 12-hour mode, regardless of the user's 24/12-hour mode setting.
I'm going to assume that you're force-unwrapping the result of timeDateFormatter.dateFromString with a wrong date format.
If you do something like this:
let timeDateFormatter = NSDateFormatter()
timeDateFormatter.locale = NSLocale(localeIdentifier:"en_US_POSIX")
timeDateFormatter.dateFormat = "hh:mm"
And force-unwrap the result:
let d = timeDateFormatter.dateFromString("11:42")!
You will get a crash at runtime if the date string is more than 12h:
let d = timeDateFormatter.dateFromString("13:42")! // crash
because "hh:mm" deals with 12h format only.
To use 24h format you should use "HH:mm":
timeDateFormatter.dateFormat = "HH:mm"
And to avoid crashes, avoid force-unwrapping:
if let d = timeDateFormatter.dateFromString("11:42") {
print(d)
} else {
// oops
}
If my diagnostic is wrong, please add details to your question. :)
I do not know that issue, however I always used my formatters this way, maybe this is also convenient to you.
let formatter = NSDateFormatter()
formatter.timeStyle = NSDateFormatterStyle.ShortStyle // here are different values possible, and all are good declared.
formatter.dateStyle = .MediumStyle // Same possible values like in timeStyle.

iOS localized string constants for time symbols ("MINUTE(S)", "HOUR(S)" etc.)

I am writing an app which need to show dates with corresponding time symbols like "hours", "minutes" etc.
There are cool localized constants for months and weekdays names in NSDateFormatter like monthSymbols (January, February etc). However, I can't find anything like this for such symbols as "hour", "minute" itself. Does such constants exists or I should create and localize these symbols by myself?
UPD:
My goal is a text for label under "30" - only localized "MIN" string without any numbers I should get rid of before placing text from date formatter in the label at the bottom.
Depending on the details of what you need, NSDateComponentsFormatter might be the solution. It includes NSDateComponentsFormatterUnitsStyleSpellOut, which renders strings in formats like “One hour, ten minutes”.
Update: After reading comments I'm still not sure what kind of formatting you really need. But hopefully this will be a useful example:
let formatter = NSDateComponentsFormatter()
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyle.Full
formatter.allowedUnits = [ NSCalendarUnit.Minute, NSCalendarUnit.Hour ]
let date = NSDate()
let dateComponents = NSCalendar.currentCalendar().components([.Minute, .Hour], fromDate: date)
let dateString = formatter.stringFromDateComponents(dateComponents)
At this point, dateString will be something like "15 hours, 11 minutes".
If you need just the min string localized you can use a MeasurementFormatter()
let measurementFormatter: MeasurementFormatter = {
let measurementFormatter = MeasurementFormatter()
measurementFormatter.locale = Locale.current
measurementFormatter.unitOptions = .providedUnit
measurementFormatter.unitStyle = .short
return measurementFormatter
}()
label.text = measurementFormatter.string(from: UnitDuration.minutes)

Format to MM:SS using NSDateComponentsFormatter

I'm trying to format to MM:SS using NSDateComponentsFormatter:
let formatter = NSDateComponentsFormatter()
formatter.zeroFormattingBehavior = .Pad
formatter.allowedUnits = .CalendarUnitMinute | .CalendarUnitSecond
let time: NSTimeInterval = 5
let timeText = formatter.stringFromTimeInterval(time)
The problem is, that even though I specified .Pad, the result is 0:05 instead of 00:05.
Here's the relevant piece from Apple's documentation:
static var Pad: NSDateComponentsFormatterZeroFormattingBehavior { get }
// Off: "1:0:10", On: "01:00:10"
I've tried to add .CalendarUnitHour just to try it and got H:MM:SS instead of HH:MM:SS despite what the documentation says.
How can I pad the very first number as shown in the documentation?
Sorry for wrong initial answer, there is no way to add leading zeros to hours:
When days, hours, minutes, and seconds are allowed, the value is displayed as “0d 1:00:00” using the positional style, and as “0d 1h 0m 0s” using the abbreviated style.
If you simply need to format NSTimeInterval into string like HH:MM:SS check this answer

NSDateFormatter still parsing instead having incorrect format

Having some problems parsing date. I have an array of supported formats and once I receive the date (string) from API, I try to parse it iterating through the formats until I get a valid NSDate object.
A snippet from Xcode Playground --
let dateString = "02/06/1987" // --> want to parse into this Feb 6, not Jun 2
let dateFormatIncorrect = "dd.MM.yyyy"
let dateFormatCorrect = "MM/dd/yyyy"
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = dateFormatIncorrect
let date = dateFormatter.dateFromString(dateString)! // "Jun 2, 1987, 12:00 AM"
dateFormatter.dateFormat = dateFormatCorrect
let date2 = dateFormatter.dateFromString(dateString)! // "Feb 6, 1987, 12:00 AM"
Why does it parse the date even though the format is clearly incorrect for a given string? Could not find anything in the docs regarding date formatter ignoring separators.
I realise the proper solution would be to have a fixed format returned from API but was wondering what is happening here?
Thanks.
It seems that NSDateFormatter is extremely lenient when parsing a date string.
Unfortunately, I could not find a reference for this, but even with
dateFormatIncorrect = "'aaa'dd'bbb'MM'ccc'yyyy'ddd'"
the date string "02/06/1987" is successfully parsed. There is a lenient property,
but that is false by default, and setting it explicitly makes no difference.
As a workaround, you could convert the parsed date back to a string, and only if
the result is equal to the original string, the date is accepted:
extension NSDateFormatter {
func checkedDateFromString(string : String) -> NSDate? {
if let date = self.dateFromString(string) {
if self.stringFromDate(date) == string {
return date
}
}
return nil
}
}
Using this custom extension,
dateFormatter.checkedDateFromString(dateString)
returns nil for the incorrect date format.
Generally, if you work with fixed date formats, you should also set the locale
to "en_US_POSIX"
dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
(see What is the best way to deal with the NSDateFormatter locale "feechur"?). However, this makes no difference for this
particular problem.
Update for Swift 3:
extension DateFormatter {
func checkedDate(from: String) -> Date? {
if let date = date(from: from), string(from: date) == from {
return date
}
return nil
}
}
This could be related to the fact that NSDateFormatter will anyways respects the users settings when using fixed formats
Although in principle a format string specifies a fixed format, by
default NSDateFormatter still takes the user’s preferences (including
the locale setting) into account
So may be the locale defined in your preference uses '/' for separator and satisfies the 'incorrect format'. Even if that is not the case, apple noted in several places that NSDateFormatter might not act consistently. So try setting a fixed locale as below and see if that helps
NSLocale *locale = [[NSLocale alloc]
initWithLocaleIdentifier:#"en_US_POSIX"];
[dateFormatter setLocale:locale];
See these links for detail: apple tech note . Note directly related to separators, but that could be related.
Had a similar issue:
NSDateFormatter returns date object from invalid input
Filed a bug report at Apple.
Result: Will not be fixed, as the change could break working code, in addition it is more error tolerant and thus provides some kind of convenience.
Please know that our engineering team has determined that this issue
behaves as intended based on the information provided.
It appears that ICU’s udat_parseCalendar() is very lenient and still
is able to parse even if the string doesn’t exactly match the format.
We understand preferring that the formatter return nil in these cases
but (1) there’s no easy way for us to know that the input string
doesn’t match the format since ICU allows it and doesn’t throw an
error and (2) suddenly returning nil in these cases would almost
certainly be a bincompat issue.
In my case I had the option to either modify the unit tests and be more tolerant in case of invalid input or have an additional checkup (based on the recommended approach, which is the accepted answer for the post) whether the resulting NSDate's string fits to the input string.

Resources