I found a bug in my app where my currency strings were not showing the correct grouping and decimal separators when using an international format. I extracted the code to try to isolate and demonstrate the problem. In the code below, I create a US formatter and a World formatter. They both use the same currency pattern, but the USA formatter uses a period as the decimal separator and the World formatter uses a comma as the decimal separator.
When I ask the World formatter to tell me what its separators are, it reports them correctly. However, when I have it generate the string for my value, it doesn't do it properly. It strangely uses the period for both the grouping AND the decimal separator.
public static void main(String[] args) throws Exception {
String currencyFormat = "¤#,##0.00;-¤#,##0.00";
NumberFormat currencyUSA = NumberFormat.getCurrencyInstance();
DecimalFormat decimalCurrencyUSA = (DecimalFormat)currencyUSA;
decimalCurrencyUSA.applyPattern(currencyFormat);
NumberFormat currencyWorld = NumberFormat.getCurrencyInstance();
DecimalFormat decimalCurrencyWorld = (DecimalFormat)currencyWorld;
decimalCurrencyWorld.applyPattern(currencyFormat);
DecimalFormatSymbols decimalFormatSymbols = decimalCurrencyWorld.getDecimalFormatSymbols();
//DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(Locale.GERMAN);
System.out.println(String.format("Original grouping: %c decimal: %c", decimalFormatSymbols.getGroupingSeparator(), decimalFormatSymbols.getDecimalSeparator()));
decimalFormatSymbols.setDecimalSeparator(',');
decimalFormatSymbols.setGroupingSeparator('.');
decimalCurrencyWorld.setDecimalFormatSymbols(decimalFormatSymbols);
System.out.println(String.format(" After grouping: %c decimal: %c", decimalFormatSymbols.getGroupingSeparator(), decimalFormatSymbols.getDecimalSeparator()));
String moneyUSA = currencyUSA.format(1500.75);
String moneyWorld = currencyWorld.format(1500.75);
DecimalFormatSymbols decimalFormatSymbols2 = decimalCurrencyWorld.getDecimalFormatSymbols();
System.out.println(String.format(" Real grouping: %c decimal: %c", decimalFormatSymbols2.getGroupingSeparator(), decimalFormatSymbols2.getDecimalSeparator()));
System.out.println(String.format(" USA String: %s", moneyUSA));
System.out.println(String.format("World String: %s", moneyWorld));
Number numberUSA = currencyUSA.parse(moneyUSA);
Number numberWorld = currencyWorld.parse(moneyWorld);
System.out.println(String.format(" USA Number: %f", numberUSA));
System.out.println(String.format("World Number: %f", numberWorld));
}
When I run the above code (on my Mac OS X 10.9, Eclipse Indigo, Java 1.6), the output is:
Original grouping: , decimal: .
After grouping: . decimal: ,
Real grouping: . decimal: ,
USA String: $1,500.75
World String: $1.500.75
USA Number: 1500.750000
World Number: 1.500000
The World currency value should be $1.500,75 however it used a period in both places.
EDIT:
I need to control the separators specifically. I can respect the user's Locale when it comes to the currency symbol, but due to the requirements of this app, I need to provide the user the ability to swap the period/comma when it comes to the separators. I can't just rely on the Locale to control these things.
The problem is that I'm clearly setting those values in my formatters and they aren't being respected.
EDIT 2: I added code to then parse the previously generated money string to see if the Decimal separators were being respected for parsing, but not for formatting. It appears that during parsing, the period is still being treated as the decimal separator as well because the World Number came out as 1.5 instead of 1500.75.
From Customizing Formats
¤ currency sign; replaced by currency symbol; if doubled, replaced by international currency symbol; if present in a pattern, the monetary decimal separator is used instead of the decimal separator
So:
decimalFormatSymbols.setMonetaryDecimalSeparator(',');
Will do it.
If you change
DecimalFormatSymbols decimalFormatSymbols = decimalCurrencyWorld.getDecimalFormatSymbols();
to
DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(Locale.GERMAN);
(or some other Locale) you will find that the separators work properly.
decimalFormatSymbols.setDecimalSeparator(','); apparently does not change the format (although setGroupingSeparator() does, as you have discovered).
Related
I have a method that convert a number into a currency, for "USD" and "GBP" it's working good, but with "EUR" the NumberFormat it's rendering a string with a space between the symbol and the number € 1.207.987,00 rather then dollar and pound "$1,207,987.00", "£1,207,987.00". I tried use replace and replace all to remove this but nothing works for me, follow the code:
public static void main(String[] argsd) {
Number rawNumber = 120798700;
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(Locale.forLanguageTag("nl"));
Currency currency = Currency.getInstance("EUR");
numberFormat.setCurrency(currency);
String numberRemoveSpaces = numberFormat.format((rawNumber.floatValue() / 100)).replaceAll("\\s+", "");
System.out.println(numberRemoveSpaces);
}
If you want to remove it, try this:
String numberRemoveSpaces = numberFormat.format((rawNumber.floatValue() / 100)).replaceAll("\\p{Z}","");
That removes any kind of whitespace or invisible separator.
You can use the following, instead of your current replaceAll():
replaceFirst("\\u00A0", "")
The Unicode value of U+00A0 is a non-breaking space (see here). This is the specific character being used to separate the currency symbol from the amount.
You can also choose to build a custom format as follows:
NumberFormat nf = NumberFormat.getCurrencyInstance();
DecimalFormatSymbols dfs = new DecimalFormatSymbols();
dfs.setCurrencySymbol("€");
dfs.setGroupingSeparator('.');
dfs.setMonetaryDecimalSeparator(',');
((DecimalFormat) nf).setDecimalFormatSymbols(dfs);
System.out.println(nf.format(rawNumber.floatValue() / 100));
This also gives the same output:
€1.207.987,00
Thanks guys for all the answers but according with http://www.bubblefoundry.com/blog/2013/11/formatting-dutch-currency-amounts/ the default format for Netherlands currency have the space between the symbol and number, so i'll keep with the space.
So I'm getting a crash java.lang.NumberFormatException: Invalid float: "٠" because for some reason on Egyptian devices the decimal delimiter is ٠ instead of .
How do I solve this? It can handle users who have , (comma) as the decimal symbol, but this weird dot causes a crash. Here's the code that's the problem:
DecimalFormat oneDigit = new DecimalFormat("#.#");
DecimalFormatSymbols dfs = new DecimalFormatSymbols();
dfs.setDecimalSeparator('.');
oneDigit.setDecimalFormatSymbols(dfs);
sevenDaysAverage = Float.valueOf(oneDigit.format(sevenDaysAverage)); // exception here
My goal is to have a number formatted with a single decimal delimited by a dot, because the app is in English and that's how the number should be displayed.
String f = "20.0"; // suppose . is weird symbol
String f1 = f.replace(".","."); // replace weird dot with decimal point
Then convert f1 to String.
sevenDaysAverage = Float.valueOf(f1);
That's an Arabic Zero not a decimal point,
You ought to use NumberFormat class.It allows you to parse Strings into a locale aware number. This would prevent you from facing situations where the decimal separator character is , For example in case of German, it would be:
NumberFormat nf_ge = NumberFormat.getInstance(Locale.GERMAN);
String number_ge = nf_ge.format(1000000);
If you want the grouping separator to be a point, you can use an european locale:
NumberFormat nf = NumberFormat.getNumberInstance(Locale.GERMAN);
DecimalFormat df = (DecimalFormat)nf;
Alternatively you can use the DecimalFormatSymbols class to change the symbols that appear in the formatted numbers produced by the format method. These symbols include the decimal separator, the grouping separator, the minus sign, and the percent sign, among others:
DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(currentLocale);
otherSymbols.setDecimalSeparator(',');
otherSymbols.setGroupingSeparator('.');
DecimalFormat df = new DecimalFormat(formatString, otherSymbols);
I am trying to convert an input string to euro/Bulgarian currency,I am having two scenario's.
First,
When input is 10,000 the Bulgarian format should be like 10 000 and euro format should be 10.000
Second,
if the input is 10.23 then both European and Bulgarian format should be 10,23.
I am trying to do using Big Decimal,Something like,
String s = "+000000055511.00";
BigDecimal b = new BigDecimal(s.replace(",", "."));
b.setScale(2, RoundingMode.HALF_UP);
System.out.println(b.toPlainString());
But I am not able to do it as an common utility which takes and converts into euro or bulgarian currency.Is there any utility for the same?Can somebody help me?
You may use java.text.NumberFormat.getCurrencyInstance(Locale) with appropriate locals. If there are no such locals which match your requirements then construct your own decimal formatter java.text.DecimalFormat with pattern ##' '##0.00 resp. ##,##0.00 plus appropriate currency sign. The formatter can be applied to BigDecimal:
String s = "+000000027511.00";
BigDecimal B = new BigDecimal(s);
// don't replace "." by ",": english number format expected here
b.setScale(2, RoundingMode.HALF_UP);
NumberFormat f = new DecimalFormat(...); //initialize as requested see docs
System.out.println(f.format(b));
Another question is why you don't want fraction digits if your number is 10,000? If this is really the case you must define two formatters more and must select them according to your creteria.
Hope this helps.
I have a monetary amount that is entered by a user and want to validate it. I have written a JSF validator but am having trouble getting this to work in all circumstances. Here is my scenario:
I have users in different Locales and therefore I need to cope with the various methods of input and want to allow the following
English
1234
1,234
1234.56
1,234.5
German & Spanish
1234
1.234
1234,56
1.234,5
French
1234
1 234
1234,56
1 234,5
My problem is with French as options 2 & 4 are seen as invalid using this code as the parsing stops at the space.
public void validate(final FacesContext pContext,
final UIComponent pComponent,
final Object pValue) {
boolean isValid = true;
final Locale locale = (Locale)pComponent.getAttributes().get(USERS_LOCALE);
final Currency currency = (Currency)pComponent.getAttributes().get(CURRENCY);
final NumberFormat formatter = NumberFormat.getNumberInstance(locale);
formatter.setGroupingUsed(true);
formatter.setMinimumFractionDigits(currency.getDefaultFractionDigits());
formatter.setMaximumFractionDigits(currency.getDefaultFractionDigits());
final ParsePosition pos = new ParsePosition(0);
final String stringValue = (String)pValue;
if (pos.getIndex() != stringValue.length() || pos.getErrorIndex() != -1) {
isValid = false;
}
...
I also want to ensure that the following are treated as invalid but they all parse successfully (except for French of course)
1,234,9.56 (Invalid grouping)
1,234.567 (too many decimal places for the currency)
Any help will be much appreciated
Ian
The French thousands' separator is actually a non-breaking space, \u00a0. If your input uses a regular space you can change the input:
input = input.replace(' ', '\u00a0');
Another thing you can do is change the grouping symbol to a regular space:
DecimalFormat decimalFormatter = (DecimalFormat) formatter;
DecimalFormatSymbols symbols = decimalFormatter.getDecimalFormatSymbols();
symbols.setGroupingSeparator(' ');
decimalFormatter.setDecimalFormatSymbols(symbols);
Can't recommend this, though. The new formatter won't accept numbers that use a non-breaking space as the grouping character.
I have this code:
public String formatDouble(double d) {
return String.format("???", d); //Should I use NumberFormat here?
}
For the sample input:
1.00
1,00
1,23
1.234567
I would like to get this output:
1
1
1.23
1.234
How can I configure the pattern (or maybe NumberFormat instance) for producing the correct output?
This would do roughly what you need:
Decimalformat df = new DecimalFormat("0.###");
df.setRoundingMode(RoundingMode.FLOOR);
df.format(1.2345);
However, the decimal separator (the dot) will be dependent on current locale. To override it, you may want to provide your own format symbols (see DecimalFormat.setDecimalFormatSymbols()).
Simple way: String.format("%.2f", 1234.23)
If you need thousand separator, add a comma before the dot: String.format("%,.2f", 1234.23) -> the result: 1,234.23.
DecimalFormat should do what you need.