I have a java string variable in my groovy app. The variable contains a user input of price in possibly different currency formats:
val = "1,250.50"
val = "1.250,50"
val = "1250,50"
val = "1250.50"
(etc.. I don't know if there are anymore funny way other countries write this)
Is there a way to parse this to the appropriate double value regardless of the format? Looking at this at the moment but not sure if it'll help. My current method only works for the US format:
total = Double.parseDouble(val.replace('$','').replaceAll(",","").trim())
You cannot parse it without knowing what the user will use as decimal separator, grouping separator, ... . For example if I type 1,250 you do not know whether I mean one thousand two hundred fifty (1,250.00), or one point two hundred fifty (1.250) .
That's why the NumberFormat/DecimalFormat class of Java allows you to specify the grouping and decimal separator.
What you could do is hoping that the user inputs his values using the conventions corresponding to his Locale settings, and use the
NumberFormat.getInstance( Locale )
with the current Locale of the JVM.
Note: with the NumberFormat you can also parse a currency. See NumberFormat#getCurrencyInstance
Related
if I want to use different number formats across Europe to one format (double), it doesn't seem to work.
Locale locale = new Locale("nl", "NL");
NumberFormat nf= NumberFormat.getNumberInstance(locale);
returns nf.parse("4,000.00").doubleValue();
it returns 4.000 instead of 4000.0, but when enter nf.parse("900,00") it works (returns 900.0)
Another time I enter 4000 and it converts to 4000.0 (expected).
So now I am left with inconsistent types.
I want to convert each number to the same double format. can you guide me?
now I am left with inconsistent types
This is incorrect. The behaviour is entirely consistent and according to spec.
In dutch, the comma is the wholes/fractions separator: There can be only one, and everything to the left is the wholes, and to the right of it, the fractional part. The dot is the thousands separator.
900,00
This is parsed as nine hundred, whole. 900 is to the left of the comma - so those are the wholes. 00 is the fractional part, which is nothing, so, you end up with 900. As expected - a dutch person reading 900,00 would assume that said 'nine hundred'.
4000
Obviously, that's four thousand. No problems there.
4,000.00
That's 4,000 - i.e. four, with 000 as fractional part, and that is how this is parsed. The .00 isn't parsed at all.
Wait, what?
NumberFormat is designed to parse multiple numbers from a stream of text. Even the .parse(string) version of it. Here, try it:
Locale locale = new Locale("nl", "NL");
NumberFormat nf= NumberFormat.getNumberInstance(locale);
System.out.println(nf.parse("4,000hey now this is strange").doubleValue();
works and runs fine, and prints '4'.
Fixing it
If you really want to fix it, you have a few strategies. One of them, is to first verify that the entire input is valid (e.g. with a regular expression) and only then parsing it.
Another option is to explicitly check that the whole input is consumed. You can do that:
String input = "4,000.00";
ParsePosition ps = new ParsePosition(0);
Locale locale = new Locale("nl", "NL");
NumberFormat nf = NumberFormat.getNumberInstance(locale);
double v = nf.parse(input, ps).doubleValue();
if (ps.getIndex() != input.length()) throw new IllegalArgumentException("Not a number: " + input);
The above code parses 900,00 as nine hundred, parser 4000 as four thousand, same for 4.000, and throws an exception if you attempt to toss 4,000.00 at it. Which is, indeed, not a valid anything in dutch locale.
I want something that parses both 4,000.00 as 4000, but also 900,00 as 900.
That is highly inconsistent and implies you want 4,000 to be parsed as 4 and yet 4,000.00 as 4000. If you want this, you're on your own and have to write it from scratch, no built in library (or, as far as I know, any external one) would do such utter befuddled inconsistent craziness.
NB: Note that the snippet would parse 4.000.00 as 400000 and works fine; inconsistent application of thousands separators is leniently parsed by NumberFormat and you can't tell it to be strict. In fact, 4.1.23.4567 is parsed as 41234567 - the only reason 4,000.00 is not parsed in the first place is because dots are not allowed in the fractional part at all. If you don't want that, you're again stuck, you can't use NumberFormat then. Regexes maybe, but you're now on the hook for writing one for each locale you care to support.
I am trying to format a NumberValue as a String in the current locale format.
For example, for Locale.Germany these would be the results I am trying to get:
1 -> 1,00
1.1 -> 1,10
0.1 -> 0,10
What I have tried:
String test1 = NumberFormat.getInstance().format(1.1);
// -> 1,1
String test2 = NumberFormat.getCurrencyInstance().format(1.1);
// -> 1,10 €
How could I get the same format as the second example, without the currency symbol? Or alternativly, how could I make sure the result from the first example always has the amount of decimals needed for the current locale?
Edit: Since there are some currencies that have 0,2 or 3 decimals, I don't think setting the decimals to a fixed amount will have the correct results.
Also, some currencies have their symbol in the front, and some in the back, so cutting off the last characters will probably not work either.
http://apps.cybersource.com/library/documentation/sbc/quickref/currencies.pdf
java.util.Currency provides the necessary information. A reasonable approach to print currency values without the currency sign in pure Java code is to get a general-purpose NumberFormat instance and configure it to use the number of digits defined by the appropriate Currency object:
NumberFormat numberFormat = NumberFormat.getInstance(myLocale);
Currency currency = Currency.getInstance(myLocale);
numberFormat.setMinimumFractionDigits(currency.getDefaultFractionDigits());
If you need a specific currency as opposed to the default currency of the locale you use, use the String override of Currency.getInstance() instead and provide the appropriate currency code.
I'm trying to get the number format according to current locale but I have a problem with the currency symbol.
This is my method:
import java.util.Locale;
import java.text.NumberFormat;
public void i18nCurrency(Locale currentLocale) {
Double price = 9876543.21;
NumberFormat currencyFormatter =
NumberFormat.getCurrencyInstance(currentLocale);
System.out.println(currencyFormatter.format(price));
}
It prints: ¤ 9 876 543,21 for uk and ¤9.876.543,21 for german. The number format is correct, but I need to get the currency symbol as well. Why I can't get the symbol?
The symbol you're getting is a universal currency placeholder. It is displayed when currency is unknown.
You probably wonder why it is unknown. Well, from your description you simply called the method passing something like Locale.GERMAN. If you did, there is no way of knowing what currency to use, because:
Euro is a currency of Germany and Austria
Swiss Frank (SFr.) is a currency of Switzerland
Each of these countries has German as at least one of their official languages. In order to resolve the problem, you always need to pass a country, i.e. call the method with Locale.GERMANY as a parameter.
Now, the harder part. It is all fairly easy when you are working with desktop application. All you have to do is to detect current OS locale like this:
Locale currentLocale = Locale.getDefault(LocaleCategory.FORMAT);
However, this method won't work with web applications. I suspect this is the case. Well, the Locale that web browsers give you might be not suitable for formatting currencies, as they may lack information about the country.
The recommended way to solve this issue is to create user profile and ask users to select the Locale (separately for UI translations and formatting purposes).
I still have to point out one important thing, because I don't want you to run into problems. When you have some monetary value in your application (usually it should be an instance of BigDecimal class, as double is not suitable for this purpose), it represents some value in a specific currency. Be it Euro, British Pound, or a Dollar, but the value is specific. It doesn't really make sense to format this value for specific country currency, as you should first change the amount (I believe you understand why).
What you probably need instead, is overriding the currency symbol or currency code to match your currency. The format and the symbol placement should obviously stay intact.
Please consider this example:
Currency dollar = Currency.getInstance("USD");
NumberFormat fmt = NumberFormat.getCurrencyInstance(Locale.GERMANY); //this gets € as currency symbol
BigDecimal monetaryAmount = BigDecimal.valueOf(12.34d);
String originalEuros = fmt.format(monetaryAmount);
System.out.println(originalEuros);
fmt.setCurrency(dollar); // change the currency symbol to $
String modifiedDollars = fmt.format(monetaryAmount);
System.out.println(modifiedDollars);
This prints:
12,34 €
12,34 USD
Wait, why? The answer to your question lies in this subtle code snippet:
System.out.println(currency.getSymbol(Locale.GERMANY));
System.out.println(currency.getSymbol(Locale.US));
The result:
USD
$
What gets printed depends on a Locale. It is probably better this way, I cannot tell...
I believe, unless you are creating Internet currency exchange application, you should stick to my example.
On iOS there's a handy function on NSNumber
localizedStringWithStyle:
Which will format a numeric value into a string using digits appropriate to that region (possibly Western, possible Arabic, etc).
I assume in Java on Android a NumberFormat object (or similar) could be configured to do this but I'm not sure how.
So my question. In Android how to you format a number in locale specific digits?
When you instantiate the NumberFormat object, you call it like this:
NumberFormat formatter = NumberFormat.getInstance(Locale.US);
You can pick from the list of static Locales or call NumberFormat.getAvailableLocales() to search for a locale which matches your desired output.
int n;
String.format("%d", n);
That's all. String.format uses the user's locale by default. You can request a particular locale by passing it as the first argument (i.e. before the format string).
I'm doing some javascript work inside a ColdFusion shopping cart, and I need to be able to format some numbers in js which will mimic LScurrencyFormat() in CF.
Currently we are taking the first (left,1) character of a formatted string but that doesn't work for currencies like Yen or Euro which come after the number, not to mention any multiple character currency symbols.
What I need to find, based on the current CF locale, is
currency symbol
decimal delimiter (, or .)
leading or trailing (before or after the number)
From there i can run my own js formatting to make the formatted numbers come out as expected on the page.In php we can use localeconv() to get these values... how can I find them in CF?
I am not aware of any built in functions. However, you can obtain the first two items from java. As far as the third, the closest suggestion I have seen is to parse the localized number pattern and detect the position of the currency sign ie \u00A4. Note: It is just a mask placeholder. It is not the same as the actual currency symbols like "$" or "£".
Edit:
As discussed in the comments, getLocale() returns some user friendly name which unfortunately does not quite line up with java's. The easiest way to get the java locale object for the current request is using getPageContext().getResponse().getLocale().
<cfscript>
// Get the current locale as a java object
javaLocale = getPageContext().getResponse().getLocale();
// get numeric settings for that locale
currency = createObject("java", "java.text.DecimalFormat").getCurrencyInstance(javaLocale);
symbols = currency.getDecimalFormatSymbols();
// 164 => decimal code point for currency sign
currencyPattern = currency.toLocalizedPattern();
result.hasTrailingCurrencySymbol = currencyPattern.indexOf(javacast("int", 164)) > 0;
result.currencySymbol = symbols.getCurrencySymbol();
result.decimalSeparator= symbols.getDecimalSeparator();
WriteDump(result);
</cfscript>
getLocale() returns the old cf5 style locale "names" but only for those locales supported by cf5. if you dump out the supported locales (Server.Coldfusion.SupportedLocales) you'll see the goofy old cf5 style locale names as well as the core java locale IDs (ie both "Chinese(China)" and "zh_CN"). if your locale wasn't one of the cf5 supported locales you should see the core java locale ID (ie th_TH for thai, thailand). see
http://cfbugs.adobe.com/cfbugreport/flexbugui/cfbugtracker/main.html#bugId=82474
as a small tweak to leigh's answer, you should also be concerned with the currency/locale's fraction digits. for instance in normal practice, you can't have part of a yen (ie 1.1 isn't quite kosher). you can get that info from the Currency class's getDefaultFractionDigits() method:
result.fractionDigits=currency.getDefaultFractionDigits();