Hi, A colleague of mine (filipe.silvest...@innogames.com) approached me today that his code wasn’t working that converted a currency String into cents. Apparently, the code worked with Java 8 while it didn’t with 11+.
public class Main { public static void main(String[] args) throws IOException { // System.setProperty("java.locale.providers", "JRE"); System.out.println(getPriceInCents(Locale.GERMANY, "9,99 €")); } static int getPriceInCents(Locale locale, String price) { try { DecimalFormat format = (DecimalFormat) NumberFormat.getCurrencyInstance(locale); Number number = format.parse(price); return (int) (number.doubleValue() * 100); } catch (ParseException e) { // This should be thrown on JDK 9+ System.out.println(e); } return 0; } } After some digging I think this is caused by the changes done for JDK-8008577[1]. When I change the java.locale.providers property to "JRE" for example, it works again. My investigations so far revealed that apparently the CLDR number pattern for the currency slightly differs. I created breakpoints in sun.util.locale.provider.NumberFormatProviderImpl::getInstance() to display some things: LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type); String[] numberPatterns = adapter.getLocaleResources(override).getNumberPatterns(); DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(override); int entry = (choice == INTEGERSTYLE) ? NUMBERSTYLE : choice; DecimalFormat format = new DecimalFormat(numberPatterns[entry], symbols); // CLDR (type) // #,##0.00 ¤ (numberPatterns[entry]) // [35,44,35,35,48,46,48,48,-62,-96,-62,-92] (numberPatterns[entry] in bytes) // // JRE type // #,##0.00 ¤;-#,##0.00 ¤ (numberPatterns[entry]) // [35,44,35,35,48,46,48,48,32,-62,-92,59,45,35,44,35,35,48,46,48,48,32,-62,-92] (numberPatterns[entry] in bytes) The JRE one includes the negative pattern, but the more interesting bit is that apparently the spacing differs here. For JRE it seems to be a normal space (the 32), but for CLDR it's showing [-62, -96] which seems to be a non breaking space aka nbsp. Ultimately this leads to a check failing in DecimalFormat when parsing the string "9,99 €" that obviously includes a normal space. if (gotPositive) { // the regionMatches will return false because nbsp != space gotPositive = text.regionMatches(position,positiveSuffix,0, positiveSuffix.length()); } Which itself leads to the following in our case: // fail if neither or both if (gotPositive == gotNegative) { parsePosition.errorIndex = position; // We hit this part here which causes the parsing to fail return false; } There are workarounds - e.g. by setting java.locale.providers as already mentioned or setting format.setPositiveSuffix(" €"); to fix this particular case. Is this a bug or a feature or are we missing something? In case this is an actual bug we would appreciate a "reported-by" mentioning in an eventual fix. Thanks in advance. I do hope you can follow my thoughts in this email. [1] https://bugs.openjdk.java.net/browse/JDK-8008577 Cheers, Christoph