Android/Java - Convert double price to String, with a currency parameter given - java

I know it has been asked a lot of times, and I did manage to get a lot of Google & stackoverflow results on this matter, but the one I tried doesn't work and gives an IllegalArgumentExceptions.
How do I convert a double price to a String, with a Euro[€]/Dollar[$] currency given?
This is my code:
// Convert Price-Double to String
public static String doubleToPrice(double price, String currency){
// Create DecimalFormat based on Currency
// Default (Euro)
// Use "€##.###,##"
DecimalFormat formatter = new DecimalFormat("€##.###,##");
// currency parameter given
if(currency != null && !currency.trim().equals(""))
// Dollar
// Use "$##,###.##"
if(currency.trim().equals("$"))
formatter = new DecimalFormat("$##,###.##");
// no currency parameter given: use Config.CURRENCY instead
else
// Dollar
// Use "$##,###.##"
if(compare(currency, Config.CURRENCY))
formatter = new DecimalFormat("$##,###.##");
// Format the string
String priceString = formatter.format(price);
// Add a space between the currency and the price value
priceString = priceString.substring(0, 1) + " " + priceString.substring(1, priceString.length());
// Replace last two "00" digits for "-"
if(priceString.endsWith("00")){
int i = priceString.lastIndexOf("00");
priceString = new StringBuilder(priceString).replace(i, i+2, "-").toString();
}
return priceString;
}
When I use the € as currency parameter, I'm getting an IllegalArgumentException at DecimalFormatter formatter = new DecimalFormatter("€##.###,##");. Does anyone know what is wrong with my DecimalFormatter prefix?
I'm also getting an IllegalArgumentException when I remove the € or $ from my Formatter prefix.
Thanks in advance for the responses.
EDIT 1 (Some examples):
doubleToPrice(4.23, "€"); should return "€ 4,23"
doubleToPrice(7.3, "€"); should return "€ 7,30"
doubleToPrice(9, "€"); should return "€ 9,-"
doubleToPrice(12345678.9, "€"); should return "€ 12.345.678,90"
doubleToPrice(0.39, "€"); should return "€ 0,39"
doubleToPrice(0.06, "€"); should return "€ 0,06"
doubleToPrice(4.23, "$"); should return "$ 4.23"
doubleToPrice(7.3, "$"); should return "$ 7.30"
doubleToPrice(9, "$"); should return "$ 9.-"
doubleToPrice(12345678.9, "$"); should return "$ 12,345,678.90"
doubleToPrice(0.39, "$"); should return "$ 0.39"
doubleToPrice(0.06, "$"); should return "$ 0.06"
EDIT 2 / Solution:
Accepted Hirak's answer, since he covered most of the things I wanted after his edit. Still, here is the finished code with the "hacks" for converting 00 to -.
// Convert Price-Double to String
public static String doubleToPrice(double price, char currency){
// Define currency to be used
char curr = Config.CURRENCY;
if(currency == '€' || currency == '$')
curr = currency;
// Format the string using a DecimalFormat
Locale locale = new Locale("de", "DE");
if(curr == '$')
locale = new Locale("en", "US");
DecimalFormatSymbols sym = new DecimalFormatSymbols(locale);
sym.setGroupingSeparator('.');
if(curr == '$')
sym.setGroupingSeparator(',');
DecimalFormat formatter = (DecimalFormat)NumberFormat.getNumberInstance(locale);
formatter.applyPattern(curr + "##,##0.00");
formatter.setDecimalFormatSymbols(sym);
String returnString = formatter.format(price);
// Replace "00" after the comma with "-"
if(returnString.endsWith("00")){
int i = returnString.lastIndexOf("00");
returnString = new StringBuilder(returnString).replace(i, i+2, "-").toString();
}
// Add space between currency-symbol and price
returnString = returnString.substring(0, 1) + " " + returnString.substring(1, returnString.length());
Log.i("PRICE FORMATTING", "double that goes in [" + price + "]; string that comes out [" + returnString + "]");
return returnString;
}

Is this what you want? (note that, even though I have put DOT as decimal separator, in the output, you will get COMMA as decimal deparator, because Java decides at runtime based on the locale.)
static String doubleToPrice(double dbl,char currency) {
Locale locale =null;
if(currency=='€') {
locale = new Locale("fr", "FR");
}else {
locale = new Locale("en", "EN");
}//Add locales as per need.
DecimalFormatSymbols sym = new DecimalFormatSymbols(locale);
sym.setGroupingSeparator('.');
DecimalFormat decimalFormat = (DecimalFormat)
NumberFormat.getNumberInstance(locale);
decimalFormat.applyPattern(currency+"##,###.00");
decimalFormat.setDecimalFormatSymbols(sym);
return decimalFormat.format(dbl);
}

Related

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.

How can I format int numbers in java?

I am trying to apply this format to a given int number #´###,### I tried with DecimalFormat class but it only allows to have one grouping separator symbol when I need to have two the accute accent for millions and commas for thousands.
So at the end I can format values like 1,000 or millions in this way 1´000,000
I always prefer to use String.format, but I am not sure if there is a Locale that would format numbers like that either. Here is some code that will do the job though.
// Not sure if you wanted to start with a number or a string. Adjust accordingly
String stringValue = "1000000";
float floatValue = Float.valueOf(stringValue);
// Format the string to a known format
String formattedValue = String.format(Locale.US, "%,.2f", floatValue);
// Split the string on the separator
String[] parts = formattedValue.split(",");
// Put the parts back together with the special separators
String specialFormattedString = "";
int partsRemaining = parts.length;
for(int i=0;i<parts.length;i++)
{
specialFormattedString += parts[i];
partsRemaining--;
if(partsRemaining > 1)
specialFormattedString += "`";
else if(partsRemaining == 1)
specialFormattedString += ",";
}
I found useful the link #Roshan provide in comments, this solution is using regex expression and replaceFirst method
public static String audienceFormat(int number) {
String value = String.valueOf(number);
if (value.length() > 6) {
value = value.replaceFirst("(\\d{1,3})(\\d{3})(\\d{3})", "$1\u00B4$2,$3");
} else if (value.length() >=5 && value.length() <= 6) {
value = value.replaceFirst("(\\d{2,3})(\\d{3})", "$1,$2");
} else {
value = value.replaceFirst("(\\d{1})(\\d+)", "$1,$2");
}
return value;
}
I don't know if this solution has a performance impact, also I am rockie with regex, so this code might be shorted.
Try this, these Locale formats in your required format.
List<Locale> locales = Arrays.asList(new Locale("it", "CH"), new Locale("fr", "CH"), new Locale("de", "CH"));
for (Locale locale : locales) {
DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance(locale);
DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
dfs.setCurrencySymbol("");
df.setDecimalFormatSymbols(dfs);
System.out.println(String.format("%5s %15s %15s", locale, format(df.format(1000)), format(df.format(1_000_000))));
}
util method
private static String format(String str) {
int index = str.lastIndexOf('\'');
if (index > 0) {
return new StringBuilder(str).replace(index, index + 1, ",").toString();
}
return str;
}
output
it_CH 1,000.00 1'000,000.00
fr_CH 1,000.00 1'000,000.00
de_CH 1,000.00 1'000,000.00
set df.setMaximumFractionDigits(0); to remove the fractions
output
it_CH 1,000 1'000,000
fr_CH 1,000 1'000,000
de_CH 1,000 1'000,000
Maybe try using this, the "#" in place with the units you want before the space or comma.
String num = "1000500000.574";
String newnew = new DecimalFormat("#,###.##").format(Double.parseDouble(number));

How can I find out the currency sub-unit (aka minor unit) symbol in Java?

Currency.getSymbol will give me the major symbol (e.g. "$" for USD) but I'd like to get the minor unit (e.g. "p" for GBP or the cents symbol for USD), without writing my own look up table.
Is there a standard, i.e. built in way to do this?
I would like to suggest for this situation to use custom custom currency format. Use DecimalFormat or NumberFormat of java.text.* package.
There are a lot of example for that.
Example
public class CurrencyFormatExample {
public void currencyFormat(Locale currentLocale) {
Double currency = new Double(9843.21);
NumberFormat currencyFormatter;
String currencyOut;
currencyFormatter = NumberFormat.getCurrencyInstance(currentLocale);
currencyOut = currencyFormatter.format(currency);
System.out.println(currencyOut + " " + currentLocale.toString());
}
public static void main(String args[]) {
Locale[] locales = new Locale[]{new Locale("fr", "FR"),
new Locale("de", "DE"), new Locale("ca", "CA"),
new Locale("rs", "RS"),new Locale("en", "IN")
};
CurrencyFormatExample[] formate = new CurrencyFormatExample[locales.length];
for (int i = 0; i < locales.length; i++) {
formate[i].currencyFormat(locales[i]);
}
}
}
Out put:
9Â 843,21 â?¬ fr_FR
9.843,21 â?¬ de_DE
CAD 9.843,21 ca_CA
RSD 9,843.21 rs_RS
Rs.9,843.21 en_IN
Reference here:
Update for minor currency
Locale locale = Locale.UK;
Currency curr = Currency.getInstance(locale);
// get and print the symbol of the currency
String symbol = curr.getSymbol(locale);
System.out.println("Symbol is = " + symbol);
Output :
Symbol is = £

How to convert formatted strings to float?

I have a list of strings and I'd like to convert them to float if a pattern is matched.
Here are some values and the expected result:
1000 -> 1000.0
1.000 -> 1000.0
1.000,000 -> 1000.0
-1.000,000 -> -1000.0
9,132 -> 9.132
1,000.00 -> invalid
30.10.2010 -> invalid
1,000.000,00 -> invalid
I tried this code for checking if a number is valid, but the pattern is never matched:
Pattern pattern = Pattern.compile("#.###,###");
for(String s : list){
Matcher m = pattern.matcher(s);
if(m.matches()){
//convert
}
}
Beside that I've tried to use this code:
DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance();
for(String s : list){
try {
Number num = df.parse(s);
//..
} catch (ParseException e) {
}
}
The problem with this code is, that no pattern-based validation is performed. E.g. a date like 2012/05/30 is converted to 2012.
So how can I either define a valid pattern or configure DecimalFormat for my needs?
The Pattern class works with regular expressions. You probably want this:
Pattern pattern = Pattern.compile("-?\d\.\d{1,3}(,\d{1,3})?");
You probably want to tune this regex depending on exactly what formats you want or don't want to match.
I think this is what you want. The comments should explain it.
#Test
public void testAllValues() {
testValue("1000", "1000");
testValue("1.000,000", "1000");
testValue("-1.000,000", "-1000");
testValue("9,132", "9.132");
testValue("1,000.00", null);
testValue("30.10.2010", null);
testValue("1,000.000,00", null);
}
private void testValue(String germanString, String usString) {
BigDecimal germanDecimal = (BigDecimal) parse(germanString);
if (usString != null) {
BigDecimal usDecimal = new BigDecimal(usString);
assertEquals("German " + germanString + " did not equal US " + usString, 0, germanDecimal.compareTo(usDecimal));
} else {
assertEquals("German " + germanString + " should not have been pareseable", null, germanDecimal);
}
}
public BigDecimal parse(String s) {
// Patch because parse doesn't enforce the number of digits between the
// grouping character (dot).
if (!Pattern.matches("[^.]*(\\.\\d{3})*[^.]*", s)) {
return null;
}
DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance(Locale.GERMANY);
df.setParseBigDecimal(true);
// Have to use the ParsePosition API or else it will silently stop
// parsing even though some of the characters weren't part of the parsed
// number.
ParsePosition position = new ParsePosition(0);
BigDecimal parsed = (BigDecimal) df.parse(s, position);
// getErrorIndex() doesn't seem to accurately reflect errors, but
// getIndex() does reflect how far we successfully parsed.
if (position.getIndex() == s.length()) {
return parsed;
} else {
return null;
}
}
Try
System.out.println("1,000.000,00".matches("^[+-]?\\d+(\\.\\d{3})*(,\\d+)?"));
I am not sure if your number can start with + so added it just in case. Also don't know if 0100000.000.000,1234 should be valid. If not tell why and regex will be corrected.
If the pattern is the comma try:
String[] splitted = string.split(",")
If size of splitted > 2 --> invalid.
If splitted.size == 2 && splitted[1].split(".") > 0 --> invalid also.
If the format is fine --> remove all points, replace comma with point, parse string after comma into int and connect the pieces.
A very simple approach but it works...

Categories

Resources