]> granicus.if.org Git - icu/commitdiff
ICU-10378 (J) Support skeleton meta-character 'J' in DateTimePatternGenerator
authorPeter Edberg <pedberg@unicode.org>
Thu, 12 Sep 2013 23:50:38 +0000 (23:50 +0000)
committerPeter Edberg <pedberg@unicode.org>
Thu, 12 Sep 2013 23:50:38 +0000 (23:50 +0000)
X-SVN-Rev: 34294

icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateTimeGeneratorTest.java

index 5a145dd2bb25216ccea8106902c9709fa620af6c..43fcb6a696202d77bf609f31e803d40c2767da7d 100644 (file)
@@ -381,24 +381,39 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
      * getBestPattern which takes optional skip matcher
      */
     private String getBestPattern(String skeleton, DateTimeMatcher skipMatcher, int options) {
-        //if (!isComplete) complete();
-        // if skeleton contains meta hour field 'j', then
-        // replace it with the default hour format char
-        skeleton = skeleton.replaceAll("j", String.valueOf(defaultHourFormatChar));
+        int flags = DTPG_FLAGS_NONE;
+        // Replace hour metacharacters 'j' and 'J', set flags as necessary
+        StringBuilder skeletonCopy = new StringBuilder(skeleton);
+        boolean inQuoted = false;
+        for (int patPos = 0; patPos < skeletonCopy.length(); patPos++) {
+            char patChr = skeletonCopy.charAt(patPos);
+            if (patChr == '\'') {
+                inQuoted = !inQuoted;
+            } else if (!inQuoted) {
+                if (patChr == 'j') {
+                    skeletonCopy.setCharAt(patPos, defaultHourFormatChar);
+                } else if (patChr == 'J') {
+                       // Get pattern for skeleton with H, then (in adjustFieldTypes)
+                       // replace H or k with defaultHourFormatChar
+                       skeletonCopy.setCharAt(patPos, 'H');
+                       flags |= DTPG_FLAGS_SKELETON_USES_CAP_J;
+                }
+            }
+        }
 
         String datePattern, timePattern;
         synchronized(this) {
-            current.set(skeleton, fp, false);
+            current.set(skeletonCopy.toString(), fp, false);
             PatternWithMatcher bestWithMatcher = getBestRaw(current, -1, _distanceInfo, skipMatcher);
             if (_distanceInfo.missingFieldMask == 0 && _distanceInfo.extraFieldMask == 0) {
                 // we have a good item. Adjust the field types
-                return adjustFieldTypes(bestWithMatcher, current, false, options);
+                return adjustFieldTypes(bestWithMatcher, current, flags, options);
             }
             int neededFields = current.getFieldMask();
 
             // otherwise break up by date and time.
-            datePattern = getBestAppending(current, neededFields & DATE_MASK, _distanceInfo, skipMatcher, options);
-            timePattern = getBestAppending(current, neededFields & TIME_MASK, _distanceInfo, skipMatcher, options);
+            datePattern = getBestAppending(current, neededFields & DATE_MASK, _distanceInfo, skipMatcher, flags, options);
+            timePattern = getBestAppending(current, neededFields & TIME_MASK, _distanceInfo, skipMatcher, flags, options);
         }
 
         if (datePattern == null) return timePattern == null ? "" : timePattern;
@@ -665,7 +680,7 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
     public String replaceFieldTypes(String pattern, String skeleton, int options) {
         synchronized (this) { // synchronized since a getter must be thread-safe
             PatternWithMatcher patternNoMatcher = new PatternWithMatcher(pattern, null);
-            return adjustFieldTypes(patternNoMatcher, current.set(skeleton, fp, false), false, options);
+            return adjustFieldTypes(patternNoMatcher, current.set(skeleton, fp, false), DTPG_FLAGS_NONE, options);
         }
     }
 
@@ -1568,11 +1583,11 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
      * We only get called here if we failed to find an exact skeleton. We have broken it into date + time, and look for the pieces.
      * If we fail to find a complete skeleton, we compose in a loop until we have all the fields.
      */
-    private String getBestAppending(DateTimeMatcher source, int missingFields, DistanceInfo distInfo, DateTimeMatcher skipMatcher, int options) {
+    private String getBestAppending(DateTimeMatcher source, int missingFields, DistanceInfo distInfo, DateTimeMatcher skipMatcher, int flags, int options) {
         String resultPattern = null;
         if (missingFields != 0) {
             PatternWithMatcher resultPatternWithMatcher = getBestRaw(source, missingFields, distInfo, skipMatcher);
-            resultPattern = adjustFieldTypes(resultPatternWithMatcher, source, false, options);
+            resultPattern = adjustFieldTypes(resultPatternWithMatcher, source, flags, options);
 
             while (distInfo.missingFieldMask != 0) { // precondition: EVERY single field must work!
 
@@ -1581,14 +1596,14 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
                 if ((distInfo.missingFieldMask & SECOND_AND_FRACTIONAL_MASK) == FRACTIONAL_MASK
                         && (missingFields & SECOND_AND_FRACTIONAL_MASK) == SECOND_AND_FRACTIONAL_MASK) {
                     resultPatternWithMatcher.pattern = resultPattern;
-                    resultPattern = adjustFieldTypes(resultPatternWithMatcher, source, true, options);
+                    resultPattern = adjustFieldTypes(resultPatternWithMatcher, source, flags | DTPG_FLAGS_FIX_FRACTIONAL_SECONDS, options);
                     distInfo.missingFieldMask &= ~FRACTIONAL_MASK; // remove bit
                     continue;
                 }
 
                 int startingMask = distInfo.missingFieldMask;
                 PatternWithMatcher tempWithMatcher = getBestRaw(source, distInfo.missingFieldMask, distInfo, skipMatcher);
-                String temp = adjustFieldTypes(tempWithMatcher, source, false, options);
+                String temp = adjustFieldTypes(tempWithMatcher, source, flags, options);
                 int foundMask = startingMask & ~distInfo.missingFieldMask;
                 int topField = getTopBitNumber(foundMask);
                 resultPattern = MessageFormat.format(getAppendFormat(topField), new Object[]{resultPattern, temp, getAppendName(topField)});
@@ -1681,7 +1696,12 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
      * @param fixFractionalSeconds TODO
      * 
      */
-    private String adjustFieldTypes(PatternWithMatcher patternWithMatcher, DateTimeMatcher inputRequest, boolean fixFractionalSeconds, int options) {
+    // flags values
+    private static final int DTPG_FLAGS_NONE = 0;
+    private static final int DTPG_FLAGS_FIX_FRACTIONAL_SECONDS = 1;
+    private static final int DTPG_FLAGS_SKELETON_USES_CAP_J = 2;
+
+    private String adjustFieldTypes(PatternWithMatcher patternWithMatcher, DateTimeMatcher inputRequest, int flags, int options) {
         fp.set(patternWithMatcher.pattern);
         StringBuilder newPattern = new StringBuilder();
         for (Object item : fp.getItems()) {
@@ -1697,7 +1717,7 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
                 //                int type = types[canonicalIndex][1];
                 int type = variableField.getType();
 
-                if (fixFractionalSeconds && type == SECOND) {
+                if ((flags & DTPG_FLAGS_FIX_FRACTIONAL_SECONDS) != 0 && type == SECOND) {
                     String newField = inputRequest.original[FRACTIONAL_SECOND];
                     fieldBuilder.append(decimal);
                     fieldBuilder.append(newField);
@@ -1749,6 +1769,9 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
                     }
                     char c = (type != HOUR && type != MONTH && type != WEEKDAY && (type != YEAR || reqField.charAt(0)=='Y'))?
                                 reqField.charAt(0): fieldBuilder.charAt(0);
+                    if (type == HOUR && (flags & DTPG_FLAGS_SKELETON_USES_CAP_J) != 0) {
+                        c = defaultHourFormatChar;
+                    }
                     fieldBuilder = new StringBuilder();
                     for (int i = adjFieldLen; i > 0; --i) fieldBuilder.append(c);
                 }
index 299446ebdc2a58811dee751df081eec18a19d447..39da5d0e034f79b9838cd523c13e646080c5ab6a 100644 (file)
@@ -268,6 +268,7 @@ public class DateTimeGeneratorTest extends TestFmwk {
     // can be generated by using GENERATE_TEST_DATA. Must be reviewed before adding
     static final Object[] dateTestData = {
         new Date(916300739123L), // 1999-01-13T23:58:59.123,0-0800
+
         new ULocale("en_US"),
         new String[] {"yM", "1/1999"},
         new String[] {"yMMM", "Jan 1999"},
@@ -285,6 +286,8 @@ public class DateTimeGeneratorTest extends TestFmwk {
         new String[] {"MMMEd", "Wed, Jan 13"},
         new String[] {"Ed", "13 Wed"},
         new String[] {"jmmssSSS", "11:58:59.123 PM"},
+        new String[] {"JJmm", "11:58"},
+
         new ULocale("en_US@calendar=japanese"), // (new locale for testing ticket 6872<-5702)
         new String[] {"yM", "1/11 H"},
         new String[] {"yMMM", "Jan 11 Heisei"},
@@ -302,6 +305,8 @@ public class DateTimeGeneratorTest extends TestFmwk {
         new String[] {"MMMEd", "Wed, Jan 13"},
         new String[] {"Ed", "13 Wed"},
         new String[] {"jmmssSSS", "11:58:59.123 PM"},
+        new String[] {"JJmm", "11:58"},
+
         new ULocale("de_DE"),
         new String[] {"yM", "1.1999"},
         new String[] {"yMMM", "Jan. 1999"},
@@ -319,6 +324,8 @@ public class DateTimeGeneratorTest extends TestFmwk {
         new String[] {"MMMEd", "Mi., 13. Jan."},
         new String[] {"Ed", "Mi., 13."},
         new String[] {"jmmssSSS", "23:58:59,123"},
+        new String[] {"JJmm", "23:58"},
+
         new ULocale("fi"),
         new String[] {"yM", "1.1999"}, // (fixed expected result per ticket 6872<-6626)
         new String[] {"yMMM", "tammi 1999"}, // (fixed expected result per ticket 6872<-7007)
@@ -336,6 +343,8 @@ public class DateTimeGeneratorTest extends TestFmwk {
         new String[] {"MMMEd", "ke 13. tammikuuta"},
         new String[] {"Ed", "ke 13."},
         new String[] {"jmmssSSS", "23.58.59,123"},
+        new String[] {"JJmm", "23.58"},
+
         new ULocale("es"),
         new String[] {"yM", "1/1999"},
         new String[] {"yMMM", "ene. de 1999"},
@@ -353,6 +362,8 @@ public class DateTimeGeneratorTest extends TestFmwk {
         new String[] {"MMMEd", "mi\u00E9. 13 de ene."},
         new String[] {"Ed", "mi\u00E9. 13"},
         new String[] {"jmmssSSS", "23:58:59,123"},
+        new String[] {"JJmm", "23:58"},
+
         new ULocale("ja"), // (new locale for testing ticket 6872<-6626)
         new String[] {"yM", "1999/1"},
         new String[] {"yMMM", "1999\u5E741\u6708"},
@@ -370,6 +381,8 @@ public class DateTimeGeneratorTest extends TestFmwk {
         new String[] {"MMMEd", "1\u670813\u65E5(\u6C34)"},
         new String[] {"Ed", "13\u65E5(\u6C34)"},
         new String[] {"jmmssSSS", "23:58:59.123"},
+        new String[] {"JJmm", "23:58"},
+
         new ULocale("ja@calendar=japanese"), // (new locale for testing ticket 6872<-5702)
         new String[] {"yM", "\u5E73\u621011/1"},
         new String[] {"yMMM", "\u5E73\u621011\u5E741\u6708"},
@@ -387,6 +400,8 @@ public class DateTimeGeneratorTest extends TestFmwk {
         new String[] {"MMMEd", "1\u670813\u65E5(\u6C34)"},
         new String[] {"Ed", "13\u65E5(\u6C34)"},
         new String[] {"jmmssSSS", "23:58:59.123"},
+        new String[] {"JJmm", "23:58"},
+
         new ULocale("zh_Hans_CN"),
         new String[] {"yM", "1999/1"},
         new String[] {"yMMM", "1999\u5E741\u6708"}, // (fixed expected result per ticket 6872<-6626)
@@ -404,6 +419,8 @@ public class DateTimeGeneratorTest extends TestFmwk {
         new String[] {"MMMEd", "1\u670813\u65E5\u5468\u4E09"},
         new String[] {"Ed", "13\u65E5\u5468\u4E09"},
         new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
+        new String[] {"JJmm", "11:58"},
+
         new ULocale("zh_TW@calendar=roc"), // (new locale for testing ticket 6872<-5702)
         new String[] {"yM", "\u6C11\u570B88/1"},
         new String[] {"yMMM", "\u6C11\u570B88\u5E741\u6708"},
@@ -421,6 +438,8 @@ public class DateTimeGeneratorTest extends TestFmwk {
         new String[] {"MMMEd", "1\u670813\u65E5\u9031\u4E09"},
         new String[] {"Ed", "13\u65E5\uFF08\u9031\u4E09\uFF09"},
         new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
+        new String[] {"JJmm", "11:58"},
+
         new ULocale("ru"),
         new String[] {"yM", "01.1999"},
         new String[] {"yMMM", "\u042F\u043D\u0432. 1999"},
@@ -438,6 +457,7 @@ public class DateTimeGeneratorTest extends TestFmwk {
         new String[] {"MMMEd", "\u0421\u0440, 13 \u044F\u043D\u0432."},
         new String[] {"Ed", "\u0421\u0440, 13"},
         new String[] {"jmmssSSS", "23:58:59,123"},
+        new String[] {"JJmm", "23:58"},
 
         new ULocale("zh@calendar=chinese"),
         new String[] {"yM", "\u620A\u5BC5\u5E7411\u6708"},
@@ -456,6 +476,7 @@ public class DateTimeGeneratorTest extends TestFmwk {
         new String[] {"MMMEd", "\u5341\u4E00\u670826\u65E5\u5468\u4E09"},
         new String[] {"Ed", "26\u65E5\u5468\u4E09"},
         new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
+        new String[] {"JJmm", "11:58"},
     };
     
     public void DayMonthTest() {