Hi Christoph,

The behavior you are observing is what is expected with the CLDR provider, as the currency format is exactly the one that CLDR has for the de locale:

https://unicode-org.github.io/cldr-staging/charts/37/by_type/numbers.number_formatting_patterns.html#53687a25c19b6481

I'd recommend that either the code uses the "COMPAT" (same as "JRE") provider which keeps compatibility with JDK8 (which you have mentioned already), or replace the normal space with non-breaking space in the input string.

Naoto

On 8/26/20 7:18 AM, Christoph Dreis wrote:
Hi,

A colleague of mine ([email protected]) 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


Reply via email to