Changing the pattern for CHF currency in Java using icu package? - java

I wrote a java function that shows the locale pattern for each currency. See the function below. What I am interested to know is that why when the currency is CHF, the 2nd decimal is hardcoded to 5?
Note that I am using icu package and this issue doesn't exist with java.util.Currency package. I am using the default locale of en_US.
Here is the output of the function which is related to USD and CHF currencies:
Analyzing currency: [USD] localePattern: [¤#,##0.00;(¤#,##0.00)] Currency symbol [$]
Analyzing currency: [CHF] localePattern: [¤#,##0.05;(¤#,##0.05)] Currency symbol [SwF]
Here is the java function I wrote:
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.util.Currency;
public static void main(String[] args)
{
Currency configuredCurrency = null;
NumberFormat formatter = NumberFormat.getCurrencyInstance();
DecimalFormat localeCurrencyFormatter = (DecimalFormat)formatter;
String localePattern = "";
String symbol = "";
String currencies = "AED,AFN,ALL,AMD,ARS,AUD,BGN,BHD,BND,BOB,BRL,BWP,BYR,CAD,CHF,CLP,CNY,COP,CRC,CZK,DJF,DKK,DOP,DZD,EEK,EGP,ERN,ETB,EUR,GBP,GTQ,HKD,HNL,HRK,HUF,IDR,ILS,INR,IQD,IRR,ISK,JOD,JPY,KES,KPW,KRW,KWD,KZT,LBP,LTL,LVL,LYD,MAD,MKD,MTL,MXN,MYR,NIO,NOK,NZD,OMR,PAB,PEN,PHP,PKR,PLN,PYG,QAR,RON,RUB,SAR,SDD,SEK,SGD,SKK,SOS,SVC,SYP,SwF,THB,TND,TRY,TZS,UAH,USD,UYU,VEB,VND,YER,ZAR,ZWD";
String[] currenciesArray = currencies.split(",");
for (int i = 0; i < currenciesArray.length; i++)
{
String currency = currenciesArray[i];
configuredCurrency = Currency.getInstance(currency);
localeCurrencyFormatter.setCurrency(configuredCurrency);
localePattern = localeCurrencyFormatter.toPattern();
symbol = localeCurrencyFormatter.getCurrency().getSymbol();
System.out.println("Analyzing currency: [" + currency + "] localePattern: [" + localePattern + "] Currency symbol [" + symbol + "]");
}
}

The 5 there is the rounding increment (there is no 0.01 of Swiss franc, 0.05 is the least valuable coin (Swiss franc wikipedia)).
Also from the icu4j DecimalFormat javadoc:
"In place of '0', the digits '1' through '9' may be used to indicate a rounding increment."

The '5' tells the ICU package that there are special rules about how to round the number to the nearest 5/100ths when converting to a string form.
"In Switzerland, five centimes are the smallest currency unit for payment transactions. For Swiss company codes and the currency Swiss franc, you therefore enter 5 ."
SAP help web site

Thank you all for the help. I was finally able to find an answer. I wrote this piece of code and did the trick:
localeCurrencyFormatter.setRoundingIncrement(new BigDecimal("0"));

Related

How to prevent rounding up decimal values using NumberFormat?

I have this function:
public void printCurrency(double currencyAmount, String lang, String country) {
Locale locale = new Locale(lang, country);
Currency currency = Currency.getInstance(locale);
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
numberFormat.setMaximumFractionDigits(2);
Log.d(TAG, currency.getDisplayName() + ": " + numberFormat.format(currencyAmount));
}
Everytime I call this with the following:
double amount = 123456789012345.67d;
printCurrency(amount, "jp", "JP");
printCurrency(amount, "en", "GB");
printCurrency(amount, "en", "US");
My amount is always rounded like so:
> Japanese Yen: ¥123,456,789,012,346
> British Pound Sterling: £123,456,789,012,346.00
> US Dollar: $123,456,789,012,346.00
I need a solution that always give a 2-decimal-place amount (if applicable) such as
> Japanese Yen: ¥123,456,789,012,346
> British Pound Sterling: £123,456,789,012,345.67
> US Dollar: $123,456,789,012,345.67
I tried the following but same issue:
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
numberFormat.setMaximumFractionDigits(2);
numberFormat.setRoundingMode(RoundingMode.UNNECESSARY);
I want to post my test result here because I don't think there is something wrong with my code.
Given the following values:
amount = 1234567890123.67;
amount = 12345678901234.67;
amount = 123456789012345.67;
my function prints the following amounts:
British Pound Sterling: £1,234,567,890,123.67
British Pound Sterling: £12,345,678,901,234.70
British Pound Sterling: £123,456,789,012,346.00
As you can see, there is no rounding of amount until length is more than 15 digits.
EDIT: Tried the amount = 12345678901234567890d, result is
British Pound Sterling: £12,345,678,901,234,600,000.00
So I guess, this approach is limited to amount length <= 15.

Formatting number based on the different currency code for different locale where output to be in the currency symbol formatted based on the locale

Language: Java
Problem: I need to set the currency code manually in java. Let's say "USD" and locale can be either "fr-CA" or "en_US" based on the user logged in . I am unable to find the solution where we can do the number format by setting the manual currency and displaying the symbol with number in the output. Please note currency code will not be the same as the locale and vice versa.
For example, if my currency is USD then based on the different locale, the number should be formatted and the output should be as below.
$1,300,000.00 - english
1.300.000,00 $ - Deutch
1 300 000,00 US$ - Potuguese
1 300 000,00 $ US - France canada
Tried below but it does not give the expected output:
Currency currencyInstance1 = Currency.getInstance("USD"); // This can change based on the user input on the UI.
NumberFormat numberFormat4 = NumberFormat.getCurrencyInstance(Locale.CANADA_FRENCH);
numberFormat4.setCurrency(currencyInstance1);
System.out.println(numberFormat4.format(amount4));
Actual output : 123 456,79 USD
**Expected output:**
For french canada: 1 300 000,00 $ US
For Portuguese : 1 300 000,00 US$
For Deutch : 1.300.000,00 $
Any help is appreciated.
Cross currency for cross locale is not supported in Java.
Country codes are an important locale component because of java. text.Format objects for dates, time, numbers, and currency are particularly sensitive to this element. Country codes add precision to the language component of a locale. For example, French is used in both France and Canada. However, precise usage and idiomatic expressions vary in the two countries.
These differences can be captured with different locale designators in which only the country code is different. For example, the code fr_CA (French-speaking Canada) is different from fr_FR (French-speaking France).
So if we need to fetch the symbol then we would need to create a map with the locale and currency. Pass the currency to fetch the symbol and then use replace to add it.
public static Map<Currency, Locale> currencyLocaleMap;
static {
currencyLocaleMap = new HashMap<>();
List<Locale> availableLocales =
Arrays.asList(Locale.getAvailableLocales());
List<Locale> supportedLocales = new ArrayList<>();
supportedLocales.add(Locale.forLanguageTag("en-US"));
List<Locale> filteredLocales = supportedLocales.stream().filter
(eachLocale -> availableLocales.contains(eachLocale)).collect(Collectors.toList());
System.out.println("UtilTemp : Locales supported : " + filteredLocales);
for (Locale locale : filteredLocales) {
try {
if(!locale.getCountry().isEmpty()){
Currency currency = Currency.getInstance(locale);
currencyLocaleMap.put(currency, locale);
}
}
catch (Exception e) {
}
}
}
public static String getCurrencySymbol(String currencyCode) {
Currency currency = Currency.getInstance(currencyCode);
System.out.println("UtilTemp :" + currencyLocaleMap);
return currency.getSymbol(currencyLocaleMap.get("USD"));
}

Checking if a String has a fraction part in Java : Locale involved

I have a String value which can either hold a Long or a Double. The String may be Locale based.
So it may hold the following values:
11
11.00
15,25 (for Locale like Denmark where the decimal part is denoted by a comma instead of dot)
I want to do something only when it is a Double; in sense that it contains a fraction value. A fraction value of "00" is also a valid case.
if(string contains fraction){
// do something
}
Given above three examples, control should go inside if for 11.00 and 15,25 but not for 11.
How can I check this?
Please keep in mind that Locale is involved. So dot and comma may have different meaning for different Locale. So simple regex to find their occurrence won't work. For e.g. 11,00 is 1100 if Locale is Australia and thus is not a double. But 11,00 is a double if Locale is a European country like Denmark or Germany.
I need to find some solution using NumberFormat but not able to work it out.
I have Locale info. So I know if the String is of which Locale. Given that, how can I find if String has a fraction or not?
EDIT: Since you've edited your question stating you know the Locale, you can use it with NumberFormat.getNumberInstance(locale).parse(strValue) in combination with a regex for the comma and thousand separator. Here a test code:
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
class Main{
private static final Locale DUTCH = new Locale("nl","NL");
public static void main(String[] a){
test("11", Locale.ENGLISH);
test("11", DUTCH);
System.out.println();
test("11.00", Locale.ENGLISH);
test("11.00", DUTCH);
System.out.println();
test("11,00", Locale.ENGLISH);
test("11,00", DUTCH);
System.out.println();
test("15.123", Locale.ENGLISH);
test("15.123", DUTCH);
System.out.println();
test("15,123", Locale.ENGLISH);
test("15,123", DUTCH);
System.out.println();
test("something", Locale.ENGLISH);
test("something", DUTCH);
}
static void test(String val, Locale locale){
try{
DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);
char decimalSep = symbols.getDecimalSeparator();
char thousandSep = symbols.getGroupingSeparator();
String escapedDecimalSep = decimalSep == '.' ? "\\." : decimalSep+"";
String escapedThousandSep = thousandSep == '.' ? "\\." : thousandSep+"";
String intRegex = "\\d+(" + escapedThousandSep + "\\d{3})*"; // Example ENGLISH: "\\d+(,\\d{3})*"
String doubleRegex = intRegex + escapedDecimalSep + "\\d+"; // Example ENGLISH: "\\d+(,\\d{3})*\\.\\d+"
NumberFormat format = NumberFormat.getInstance(locale);
Number number = format.parse(val);
if(val.matches(doubleRegex)){
double d = number.doubleValue();
System.out.println(val + " (in locale " + locale + ") is a double: " + d);
} else if(val.matches(intRegex)){
int i = number.intValue();
System.out.println(val + " (in locale " + locale + ") is an integer: " + i);
} else{
System.out.println("Unable to determine whether value " + val + " is an integer or double for locale " + locale);
}
} catch(ParseException ex){
System.out.println("Error occurred for value \"" + val + "\". Are you sure it's an integer or decimal?");
}
}
}
Try it online.
Here is the output:
11 (in locale en) is an integer: 11
11 (in locale nl_NL) is an integer: 11
11.00 (in locale en) is a double: 11.0
Unable to determine whether value 11.00 is an integer or double for locale nl_NL
Unable to determine whether value 11,00 is an integer or double for locale en
11,00 (in locale nl_NL) is a double: 11.0
15.123 (in locale en) is a double: 15.123
15.123 (in locale nl_NL) is an integer: 15123
15,123 (in locale en) is an integer: 15123
15,123 (in locale nl_NL) is a double: 15.123
Error occurred for value "something". Are you sure it's an integer or decimal?
Error occurred for value "something". Are you sure it's an integer or decimal?
With a regex you could do
Pattern decimalPattern = Pattern.compile("\\d+(,|\\.)\\d+{2}");
and then have
boolean isDecimal = decimalPattern.matcher(input).matches();
Regex:
\d+ one or more digits
(,|\\.) a decimal point or a comma
\d+ one or more digits again
Or you could do the splitting thing
String[] split = input.split("(,|\\.)");
boolean isDecimal = split.length > 1 && split[1].length() == 2;
You could use a loop to check if it have a comma or dot and then check with the if?
boolean IsADouble = false;
for (int i = 0; i < String.length(); i++) {
if (String.charAt(i) == ','|| String.charAt(i) == '.') {
IsADouble = true
}
}
And then you create the If to do something if its a double.
Hope it helped you :)

getSymbol() in java.util.Currency returns different values depending on API level

The following code is being used to format a number in the proper currency:
public static String getFormattedCurrency(String currencyCode, String currencyName, Number value) {
//....
NumberFormat format = NumberFormat.getCurrencyInstance(Locale.getDefault());
Currency currency = Currency.getInstance(currencyCode);
format.setCurrency(currency);
if (format instanceof DecimalFormat) {
format.setMinimumFractionDigits(CURRENCY_MIN_FRACTION_DIGITS);
}
Log.d("Currency", "Symbol: " + currency.getSymbol() + ", Currency: " + currency + ", Locale: " + local);
return format.format(value);
}
The value of currencyCode is THB, the Thai baht. On Lollipop, currency.getSymbol() returns ฿, the sign for the Thai baht. However, on Oreo, the same method returns THB.
Why are different values being returned between these two API levels?
Based on this issue, it would appear to be a Unicode decision.

Unexpected currency symbol in currency NumberFormat

While testing on an Android emulator, I came across an inconsistency between the formatted strings produced by a NumberFormat instance and it's reported currency symbol.
I start by setting up a localized currency formatter like so:
// displayLocale is af_NA
DecimalFormat df = (DecimalFormat)
NumberFormat.getCurrencyInstance(displayLocale);
// currency is USD
df.setCurrency(currency); // USD localized for af_NA
Then I use it to format a value. The result of invoking:
df.format(1)
is "$1". This might not seem obviously wrong, but when I check the details of the formatter, things dont add up. Invoking:
df.getDecimalFormatSymbols().getCurrencySymbol()
yields "USD" and not the expected "$"...what!? Even stranger, I've yet to find a physical device that I can replicate the issue on. Tests running under Robolectric don't exhibit this behavior either.
Has anybody else come across this behavior before and if so, any idea what's causing it? And finally, is this behavior that might be encountered on physical devices or is it purely an AVD thing?
UPDATE:
Here's a method you can call from any Activity to check for and print out any offending formatters:
void checkFormatters() {
Currency USD = Currency.getInstance(Locale.US);
Money oneDollar = new Money(BigDecimal.ONE, USD);
for(Locale locale : Locale.getAvailableLocales()) {
DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance(locale);
df.setCurrency(USD);
String currencySymbol = df.getDecimalFormatSymbols().getCurrencySymbol();
String localizedDollarString = CurrencyUtils.getPrettyCurrencyString(locale, oneDollar);
if(!localizedDollarString.contains(currencySymbol)) {
Log.w(TAG, locale.getDisplayName() + ": Expected localized dollar string [" + localizedDollarString +
"] to contain currency symbol [" + currencySymbol +
"]");
}
}
}
If the device you're testing on has the issue then you'll see results like these:
Uzbek (LATN): Expected localized dollar string [$ 1] to contain currency symbol: [US$]
Uzbek (Uzbekistan,UZ): Expected localized dollar string [$ 1] to contain currency symbol: [US$]

Categories

Resources