Reputation: 79
NumberFormat is incorrectly parsing a number for locale other than the default en_US. For example parsing for fr_CA gives incorrect results.
NumberFormat nf = NumberFormat.getNumberInstance(new Locale("fr_CA"));
Number num = nf.parse("2.302,52");
Output is: 2.302
Expected output: 2302,52
or 2302.52
It is basically changing the value of this number from 2 thousands something to just 2 something, which is incorrect.
See the below screenshot for details:
What is going wrong I'm unable to identify, please help !
Upvotes: 0
Views: 1635
Reputation: 3884
You have 2 problems here,
1) You're creating the locale incorrectly. Use Locale.CANADA_FRENCH
.
2) The given number is invalid in both the default and Canadian locales. As per ICU, in fr_CA
, the grouping separator is empty, and the decimal separator is ,
.
As such, in your example, that uses the default locale, it treats .
as a decimal separator and bails at ,
, where as in the correct locale it will bail at .
and return simply 2
.
The correct way to write this number in fr_CA
is 2302,52
So, really, Java is correct, but your assumption on the format of this number is incorrect.
Upvotes: 0
Reputation: 79
I was able to use DecimalFormat to correctly parse non default locale values by setting the decimal and grouping separator as:
/**
* Testing DF parse
*/
DecimalFormat df = new DecimalFormat();
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator(',');
symbols.setGroupingSeparator('.');
df.setDecimalFormatSymbols(symbols);
Number num = df.parse("2.302,52"); // OUTPUT is: 2302.52 -> correctly parsed
/**
* Round off and format
*/
double d = num.doubleValue();
double roundedVal = Math.round(d); // OUTPUT is: 2303.0 -> correctly rounded off
String localeAwareVlue = df.format(roundedVal); // OUTPUT is: 2.303 -> using correct grouping separator
valueString = localeAwareVlue;
But I really want to be able to do this dynamically at run time without having to set the separators for each language/country combination I might receive. Please help with any suggestions.
I ended up with the following final code to be able to dynamically parse and format localized values. This works for patterns like ###,###.## and ###.###,## - assumptions being -
grouping separator is either a period or a comma
String localValue = args.get(0).toString();
// String locale = args.get(1).toString();
char decChar = localValue.charAt(localValue.length()-3);
char grpChar = ',';
if(decChar == ',') {
grpChar = '.';
}
DecimalFormat df = new DecimalFormat();
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator(decChar);
symbols.setGroupingSeparator(grpChar);
df.setDecimalFormatSymbols(symbols);
Number parsedValue = 0;
try {
parsedValue = df.parse(localValue);
}
catch (ParseException pe) {
pe.printStackTrace();
}
/**
* Round off and format to localize
*/
double roundedValue = Math.round(parsedValue.doubleValue());
String localeAwareValue = df.format(roundedValue);
// TODO - use Locale with nf instead of above method
// currently facing issue with incorrect parse for non default locale
/*
NumberFormat nf = NumberFormat.getInstance(new Locale(locale));
Number num = nf.parse(localValue);
*/
Needless to say I'm not satisfied with the solution, which is more of a workaround until I get a solution to the main problem - NumberFormat incorrect parse for non default locale. Therefore any hints, suggestions, solutions are still welcome.
Upvotes: 1