[NEW BUG] NumberFormat.parse fails in some scenarios
naoto.sato at oracle.com
naoto.sato at oracle.com
Wed Aug 26 17:09:19 UTC 2020
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 (filipe.silvestrim at 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
>
>
More information about the core-libs-dev
mailing list