]> granicus.if.org Git - icu/commitdiff
ICU-13366 Fixed ICU4J number parsing problems with supplimental characters in SimpleD...
authorYoshito Umaoka <y.umaoka@gmail.com>
Wed, 4 Oct 2017 14:34:54 +0000 (14:34 +0000)
committerYoshito Umaoka <y.umaoka@gmail.com>
Wed, 4 Oct 2017 14:34:54 +0000 (14:34 +0000)
X-SVN-Rev: 40544

icu4j/main/classes/core/src/com/ibm/icu/impl/DateNumberFormat.java
icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java
icu4j/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/IntlTestDateFormat.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/TimeZoneFormatTest.java

index af077d5e899233ba14863d77ba99bc1ef645e003..2baa01816cc572a3f7a37650cef49a2fb71f8c47 100644 (file)
@@ -45,6 +45,9 @@ public final class DateNumberFormat extends NumberFormat {
     private int minIntDigits;
 
     public DateNumberFormat(ULocale loc, String digitString, String nsName) {
+        if (digitString.length() > 10) {
+            throw new UnsupportedOperationException("DateNumberFormat does not support digits out of BMP.");
+        }
         initialize(loc,digitString,nsName);
     }
 
index 8e87423c6d38190e3b202fce57e4c3dbd11787d9..47ffa5de9f73e68c7f5cd2d03d503f1473e98135 100644 (file)
@@ -1571,12 +1571,20 @@ public abstract class DateFormat extends UFormat {
      */
     public void setNumberFormat(NumberFormat newNumberFormat)
     {
-        this.numberFormat = (NumberFormat)newNumberFormat.clone();
-        /*In order to parse String like "11.10.2001" to DateTime correctly
-          in Locale("fr","CH") [Richard/GCL]
-        */
-        this.numberFormat.setParseIntegerOnly(true);
-        this.numberFormat.setGroupingUsed(false);
+        numberFormat = (NumberFormat)newNumberFormat.clone();
+        fixNumberFormatForDates(numberFormat);
+    }
+
+    // no matter what the locale's default number format looked like, we want
+    // to modify it so that it doesn't use thousands separators, doesn't always
+    // show the decimal point, and recognizes integers only when parsing
+    static void fixNumberFormatForDates(NumberFormat nf) {
+        nf.setGroupingUsed(false);
+        if (nf instanceof DecimalFormat) {
+            ((DecimalFormat)nf).setDecimalSeparatorAlwaysShown(false);
+        }
+        nf.setParseIntegerOnly(true);
+        nf.setMinimumFractionDigits(0);
     }
 
     /**
index b5d63896d183c9df32b6df83b3b402b9972a44b1..5c81eba1e1fde3971707ab27b96e729026a0151e 100644 (file)
@@ -1115,15 +1115,20 @@ public class SimpleDateFormat extends DateFormat {
         }
         if (numberFormat == null) {
             NumberingSystem ns = NumberingSystem.getInstance(locale);
-            if (ns.isAlgorithmic()) {
+            String digitString = ns.getDescription();
+            // DateNumberFormat does not support non-BMP digits at this moment.
+            if (ns.isAlgorithmic() || digitString.length() != 10) {
                 numberFormat = NumberFormat.getInstance(locale);
             } else {
-                String digitString = ns.getDescription();
                 String nsName = ns.getName();
                 // Use a NumberFormat optimized for date formatting
                 numberFormat = new DateNumberFormat(locale, digitString, nsName);
             }
         }
+        if (numberFormat instanceof DecimalFormat) {
+            fixNumberFormatForDates(numberFormat);
+        }
+
         // Note: deferring calendar calculation until when we really need it.
         // Instead, we just record time of construction for backward compatibility.
         defaultCenturyBase = System.currentTimeMillis();
@@ -1151,7 +1156,14 @@ public class SimpleDateFormat extends DateFormat {
             String digits = null;
             if (numberFormat instanceof DecimalFormat) {
                 DecimalFormatSymbols decsym = ((DecimalFormat) numberFormat).getDecimalFormatSymbols();
-                digits = new String(decsym.getDigits());
+                String[] strDigits = decsym.getDigitStrings();
+                // Note: TimeZoneFormat#setGMTOffsetDigits() does not support string array,
+                // so we need to concatenate digits to make a single string.
+                StringBuilder digitsBuf = new StringBuilder();
+                for (String digit : strDigits) {
+                    digitsBuf.append(digit);
+                }
+                digits = digitsBuf.toString();
             } else if (numberFormat instanceof DateNumberFormat) {
                 digits = new String(((DateNumberFormat)numberFormat).getDigits());
             }
@@ -2236,8 +2248,17 @@ public class SimpleDateFormat extends DateFormat {
      */
     private void initLocalZeroPaddingNumberFormat() {
         if (numberFormat instanceof DecimalFormat) {
-            decDigits = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getDigits();
+            DecimalFormatSymbols tmpDecfs = ((DecimalFormat)numberFormat).getDecimalFormatSymbols();
+            String[] tmpDigits = tmpDecfs.getDigitStringsLocal();
             useLocalZeroPaddingNumberFormat = true;
+            decDigits = new char[10];
+            for (int i = 0; i < 10; i++) {
+                if (tmpDigits[i].length() > 1) {
+                    useLocalZeroPaddingNumberFormat = false;
+                    break;
+                }
+                decDigits[i] = tmpDigits[i].charAt(0);
+            }
         } else if (numberFormat instanceof DateNumberFormat) {
             decDigits = ((DateNumberFormat)numberFormat).getDigits();
             useLocalZeroPaddingNumberFormat = true;
@@ -3226,10 +3247,8 @@ public class SimpleDateFormat extends DateFormat {
                 /* Skip this for Chinese calendar, moved from ChineseDateFormat */
                 if ( override != null && (override.compareTo("hebr") == 0 || override.indexOf("y=hebr") >= 0) && value < 1000 ) {
                     value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
-                } else if (count == 2 && (pos.getIndex() - start) == 2 && cal.haveDefaultCentury()
-                    && UCharacter.isDigit(text.charAt(start))
-                    && UCharacter.isDigit(text.charAt(start+1)))
-                    {
+                } else if (count == 2 && text.codePointCount(start, pos.getIndex()) == 2 && cal.haveDefaultCentury()
+                    && countDigits(text, start, pos.getIndex()) == 2) {
                         // Assume for example that the defaultCenturyStart is 6/18/1903.
                         // This means that two-digit years will be forced into the range
                         // 6/18/1903 to 6/17/2003.  As a result, years 00, 01, and 02
@@ -3242,7 +3261,7 @@ public class SimpleDateFormat extends DateFormat {
                         ambiguousYear[0] = value == ambiguousTwoDigitYear;
                         value += (getDefaultCenturyStartYear()/100)*100 +
                             (value < ambiguousTwoDigitYear ? 100 : 0);
-                    }
+                }
                 cal.set(field, value);
 
                 // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
@@ -3322,7 +3341,7 @@ public class SimpleDateFormat extends DateFormat {
                 return pos.getIndex();
             case 8: // 'S' - FRACTIONAL_SECOND
                 // Fractional seconds left-justify
-                i = pos.getIndex() - start;
+                i = countDigits(text, start, pos.getIndex());
                 if (i < 3) {
                     while (i < 3) {
                         value *= 10;
@@ -3788,6 +3807,26 @@ public class SimpleDateFormat extends DateFormat {
         return number;
     }
 
+    /**
+     * Counts number of digit code points in the specified text.
+     *
+     * @param text  input text
+     * @param start start index, inclusive
+     * @param end   end index, exclusive
+     * @return  number of digits found in the text in the specified range.
+     */
+    private static int countDigits(String text, int start, int end) {
+        int numDigits = 0;
+        int idx = start;
+        while (idx < end) {
+            int cp = text.codePointAt(idx);
+            if (UCharacter.isDigit(cp)) {
+                numDigits++;
+            }
+            idx += UCharacter.charCount(cp);
+        }
+        return numDigits;
+    }
 
     /**
      * Translate a pattern, mapping each character in the from string to the
index ec36fc1e45408badfb8f0f3db75761de9f5e81f9..8dc9c321933320e83a80ecba05e3780640ab8ca2 100644 (file)
@@ -77,7 +77,7 @@ public class IntlTestDateFormat extends TestFmwk {
         fLimit = 3;
 
         for(timeStyle = 0; timeStyle < 4; timeStyle++) {
-            fTestName = new String("Time test " + timeStyle + " (" + localeName + ")");
+            fTestName = "Time test " + timeStyle + " (" + localeName + ")";
             try {
                 fFormat = DateFormat.getTimeInstance(timeStyle, locale);
             }
@@ -85,13 +85,13 @@ public class IntlTestDateFormat extends TestFmwk {
                 errln("FAIL: localeTest time getTimeInstance exception");
                 throw e;
             }
-            TestFormat();
+            testDates();
         }
 
         fLimit = 2;
 
         for(dateStyle = 0; dateStyle < 4; dateStyle++) {
-            fTestName = new String("Date test " + dateStyle + " (" + localeName + ")");
+            fTestName = "Date test " + dateStyle + " (" + localeName + ")";
             try {
                 fFormat = DateFormat.getDateInstance(dateStyle, locale);
             }
@@ -99,12 +99,12 @@ public class IntlTestDateFormat extends TestFmwk {
                 errln("FAIL: localeTest date getTimeInstance exception");
                 throw e;
             }
-            TestFormat();
+            testDates();
         }
 
         for(dateStyle = 0; dateStyle < 4; dateStyle++) {
             for(timeStyle = 0; timeStyle < 4; timeStyle++) {
-                fTestName = new String("DateTime test " + dateStyle + "/" + timeStyle + " (" + localeName + ")");
+                fTestName = "DateTime test " + dateStyle + "/" + timeStyle + " (" + localeName + ")";
                 try {
                     fFormat = DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
                 }
@@ -112,13 +112,12 @@ public class IntlTestDateFormat extends TestFmwk {
                     errln("FAIL: localeTest date/time getDateTimeInstance exception");
                     throw e;
                 }
-                TestFormat();
+                testDates();
             }
         }
     }
 
-    @Test
-    public void TestFormat() {
+    private void testDates() {
         if (fFormat == null) {
             errln("FAIL: DateFormat creation failed");
             return;
@@ -259,6 +258,7 @@ public class IntlTestDateFormat extends TestFmwk {
                     new ULocale("bg_BG"),
                     new ULocale("fr_CA"),
                     new ULocale("zh_TW"),
+                    new ULocale("ccp"),     // decimal digits are not in BMP
             };
         } else {
             locales = DateFormat.getAvailableULocales();
index e3719cfd9712677d76949e9e4ec24140b70eefaf..82b221853cf1ed7a872f5ca26b598f0bb5a275d8 100644 (file)
@@ -128,7 +128,8 @@ public class TimeZoneFormatTest extends TestFmwk {
         if (TEST_ALL || TestFmwk.getExhaustiveness() > 5) {
             LOCALES = ULocale.getAvailableLocales();
         } else {
-            LOCALES = new ULocale[] {new ULocale("en"), new ULocale("en_CA"), new ULocale("fr"), new ULocale("zh_Hant"), new ULocale("fa")};
+            LOCALES = new ULocale[] {new ULocale("en"), new ULocale("en_CA"), new ULocale("fr"),
+                    new ULocale("zh_Hant"), new ULocale("fa"), new ULocale("ccp")};
         }
 
         String[] tzids;
@@ -245,10 +246,13 @@ public class TimeZoneFormatTest extends TestFmwk {
                             if (!isOffsetFormat) {
                                 // Check if localized GMT format is used as a fallback of name styles
                                 int numDigits = 0;
-                                for (int n = 0; n < tzstr.length(); n++) {
-                                    if (UCharacter.isDigit(tzstr.charAt(n))) {
+                                int idx = 0;
+                                while (idx < tzstr.length()) {
+                                    int cp = tzstr.codePointAt(idx);
+                                    if (UCharacter.isDigit(cp)) {
                                         numDigits++;
                                     }
+                                    idx += UCharacter.charCount(cp);
                                 }
                                 isOffsetFormat = (numDigits > 0);
                             }