]> granicus.if.org Git - icu/commitdiff
ICU-10334 C vs. J differences when in lenient mode
authorScott Russell <DTownSMR@gmail.com>
Wed, 18 Dec 2013 19:47:13 +0000 (19:47 +0000)
committerScott Russell <DTownSMR@gmail.com>
Wed, 18 Dec 2013 19:47:13 +0000 (19:47 +0000)
X-SVN-Rev: 34791

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/DateFormatRegressionTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatTest.java

index f83fc3c419706c447152dd707ae547e35ef82ec2..d8eda16b4e7032e913c34d6d8a2f847f83a86e17 100644 (file)
@@ -452,6 +452,8 @@ public abstract class DateFormat extends UFormat {
      * PARSE_ALLOW_WHITESPACE - indicates whitespace tolerance. Also included is trailing dot tolerance.
      * <br/>
      * PARSE_ALLOW_NUMERIC - indicates tolerance of numeric data when String data may be assumed. eg: YEAR_NAME_FIELD
+     * <br/>
+     * PRASE_PARTIAL_MATCH - indicates tolerance of partial matches against pattern literals
      * 
      * @internal ICU technology preview
      */
@@ -465,7 +467,12 @@ public abstract class DateFormat extends UFormat {
          * indicates tolerance of numeric data when String data may be assumed. eg: YEAR_NAME_FIELD 
          * @internal ICU technology preview
          */
-        PARSE_ALLOW_NUMERIC 
+        PARSE_ALLOW_NUMERIC, 
+        /**
+         * indicates tolerance of a partial literal match
+         * @draft ICU 53
+         */
+        PARSE_PARTIAL_MATCH
     };
     
     /**
@@ -1472,7 +1479,7 @@ public abstract class DateFormat extends UFormat {
         return calendar.isLenient();
     }
 
-    /**
+    /** 
      * set a boolean attribute for this instance. Aspects of DateFormat leniency are controlled by
      * boolean attributes. 
      * 
index be8470858e2ee411831aff1dfd5f93198c1d302e..f3513e43add736e38d86f6b8eb7a416ed9bcc0cd 100644 (file)
@@ -2438,6 +2438,9 @@ public class SimpleDateFormat extends DateFormat {
                 } else if ((pch == ' ' || pch == '.') && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_WHITESPACE)) {
                     ++idx;
                     continue;
+                } else if (pos != originalPos && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_PARTIAL_MATCH)) {
+                    ++idx;
+                    continue;
                 }
                 break;
             }
@@ -2731,11 +2734,14 @@ public class SimpleDateFormat extends DateFormat {
         if (patternCharIndex == 4 /*'k' HOUR_OF_DAY1_FIELD*/ ||
             patternCharIndex == 15 /*'h' HOUR1_FIELD*/ ||
             (patternCharIndex == 2 /*'M' MONTH_FIELD*/ && count <= 2) ||
-            (patternCharIndex == 26 /*'L' STAND_ALONE_MONTH*/ && count <= 2) ||
-            (patternCharIndex == 19 /*'e' DOW_LOCAL*/ && count <= 2) ||
+            patternCharIndex == 26 /*'L' STAND_ALONE_MONTH*/ ||
+            patternCharIndex == 19 /*'e' DOW_LOCAL*/ ||
+            patternCharIndex == 25 /*'c' STAND_ALONE_DAY_OF_WEEK*/ ||
             patternCharIndex == 1 /*'y' YEAR */ || patternCharIndex == 18 /*'Y' YEAR_WOY */ ||
             patternCharIndex == 30 /*'U' YEAR_NAME_FIELD, falls back to numeric */ ||
             (patternCharIndex == 0 /*'G' ERA */ && isChineseCalendar) ||
+            patternCharIndex == 27 /* 'Q' - QUARTER*/ ||
+            patternCharIndex == 28 /* 'q' - STANDALONE QUARTER*/ ||
             patternCharIndex == 8 /*'S' FRACTIONAL_SECOND */ )
             {
                 // It would be good to unify this with the obeyCount logic below,
@@ -2764,7 +2770,8 @@ public class SimpleDateFormat extends DateFormat {
                     } else {
                         number = parseInt(text, pos, allowNegative,currentNumberFormat);
                     }
-                    if (number == null && patternCharIndex != 30) {
+                    if (number == null && !allowNumericFallback(patternCharIndex)) {
+                        // only return if pattern is NOT one that allows numeric fallback
                         return ~start;
                     }
                 }
@@ -2853,7 +2860,8 @@ public class SimpleDateFormat extends DateFormat {
                 return ~start;
             case 2: // 'M' - MONTH
             case 26: // 'L' - STAND_ALONE_MONTH
-                if (count <= 2) { // i.e., M/MM, L/LL
+                if (count <= 2 || (number != null && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_NUMERIC))) {
+                    // i.e., M/MM, L/LL or lenient & have a number
                     // Don't want to parse the month if it is a string
                     // while pattern uses numeric style: M/MM, L/LL.
                     // [We computed 'value' above.]
@@ -2918,7 +2926,8 @@ public class SimpleDateFormat extends DateFormat {
                 cal.set(Calendar.MILLISECOND, value);
                 return pos.getIndex();
             case 19: // 'e' - DOW_LOCAL
-                if(count <= 2) { // i.e. e/ee
+                if(count <= 2 || (number != null && (getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_NUMERIC))) ) { 
+                    // i.e. e/ee or lenient and have a number
                     cal.set(field, value);
                     return pos.getIndex();
                 }
@@ -2946,6 +2955,11 @@ public class SimpleDateFormat extends DateFormat {
                 return newStart;
             }
             case 25: { // 'c' - STAND_ALONE_DAY_OF_WEEK
+                if(count == 1 || (number != null && (getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_NUMERIC))) ) { 
+                    // i.e. c or lenient and have a number
+                    cal.set(field, value);
+                    return pos.getIndex();
+                }
                 // Want to be able to parse at least wide, abbrev, short forms.
                 int newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.standaloneWeekdays, null, cal); // try cccc wide
                 if (newStart > 0) {
@@ -3100,7 +3114,8 @@ public class SimpleDateFormat extends DateFormat {
                 return ~start;
             }
             case 27: // 'Q' - QUARTER
-                if (count <= 2) { // i.e., Q or QQ.
+                if (count <= 2 || (number != null && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_NUMERIC))) { 
+                    // i.e., Q or QQ. or lenient & have number
                     // Don't want to parse the quarter if it is a string
                     // while pattern uses numeric style: Q or QQ.
                     // [We computed 'value' above.]
@@ -3121,7 +3136,8 @@ public class SimpleDateFormat extends DateFormat {
                 }
 
             case 28: // 'q' - STANDALONE QUARTER
-                if (count <= 2) { // i.e., q or qq.
+                if (count <= 2 || (number != null && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_NUMERIC))) { 
+                    // i.e., q or qq. or lenient & have number
                     // Don't want to parse the quarter if it is a string
                     // while pattern uses numeric style: q or qq.
                     // [We computed 'value' above.]
@@ -3170,6 +3186,22 @@ public class SimpleDateFormat extends DateFormat {
             }
     }
 
+    /**
+     * return true if the pattern specified by patternCharIndex is one that allows
+     * numeric fallback regardless of actual pattern size.
+     */
+    private boolean allowNumericFallback(int patternCharIndex) {
+        if (patternCharIndex == 26 /*'L' STAND_ALONE_MONTH*/ ||
+            patternCharIndex == 19 /*'e' DOW_LOCAL*/ ||
+            patternCharIndex == 25 /*'c' STAND_ALONE_DAY_OF_WEEK*/ ||
+            patternCharIndex == 30 /*'U' YEAR_NAME_FIELD*/ ||
+            patternCharIndex == 27 /* 'Q' - QUARTER*/ ||
+            patternCharIndex == 28 /* 'q' - STANDALONE QUARTER*/) {
+            return true;
+        }
+        return false;
+    }
+    
     /**
      * Parse an integer using numberFormat.  This method is semantically
      * const, but actually may modify fNumberFormat.
index 68f6bbcb76a6b96f4ac5946d000f0bde1f3d5881..69c8dfde94ccd4deee6860751e5e8b8569c50c12 100644 (file)
@@ -1275,5 +1275,80 @@ public class DateFormatRegressionTest extends com.ibm.icu.dev.test.TestFmwk {
             }
         }
   }
-    
+  
+
+    public void TestT10334() {
+        String pattern = new String("'--: 'EEE-WW-MMMM-yyyy");
+        String text = new String("--mon-02-march-2011");
+        SimpleDateFormat format = new SimpleDateFormat(pattern);
+
+        format.setBooleanAttribute(DateFormat.BooleanAttribute.PARSE_PARTIAL_MATCH, false);      
+        try {
+            format.parse(text);
+            errln("parse partial match did NOT fail in strict mode!");
+        } catch (ParseException pe) {        
+            // expected
+        }
+
+        format.setBooleanAttribute(DateFormat.BooleanAttribute.PARSE_PARTIAL_MATCH, true);
+        try {
+            format.parse(text);
+        } catch (ParseException pe) {
+            errln("parse partial match failure in lenient mode: " + pe.getLocalizedMessage());
+        }
+
+        pattern = new String("YYYY MM dd");
+        text =    new String("2013 12 10");
+        format.applyPattern(pattern);
+        Date referenceDate = null;
+        try {
+            referenceDate = format.parse(text);            
+        } catch (ParseException pe) {
+            errln("unable to instantiate reference date: " + pe.getLocalizedMessage());
+        }
+
+        FieldPosition fp = new FieldPosition(0);
+        pattern = new String("YYYY LL dd ee cc qq QQ");
+        format.applyPattern(pattern);
+        StringBuffer formattedString = new StringBuffer(); 
+        formattedString = format.format(referenceDate, formattedString, fp);
+        logln("ref date: " + formattedString);
+
+
+        pattern = new String("YYYY LLL dd eee ccc qqq QQQ");
+        text = new String("2013 12 10 03 3 04 04");
+        format.applyPattern(pattern);
+        logln(format.format(referenceDate));
+        
+        format.setBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_NUMERIC, true);
+        ParsePosition pp = new ParsePosition(0);
+        format.parse(text, pp);
+        int errorIdx = pp.getErrorIndex();
+        if (errorIdx != -1) {
+            
+            errln("numeric parse error at["+errorIdx+"] on char["+pattern.substring(errorIdx, errorIdx+1)+"] in pattern["+pattern+"]");
+        }
+
+        format.setBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_NUMERIC, false);
+        try {
+        format.parse(text);
+        errln("numeric parse did NOT fail in strict mode!");
+        } catch (ParseException pe) {
+            // expected
+        }
+        
+        /*
+         * test to verify new code (and improve code coverage) for normal quarter processing
+         */
+        text = new String("2013 Dec 10 Thu Thu Q4 Q4");
+        try {
+            format.parse(text);
+        } catch (ParseException pe) {
+            errln("normal quarter processing failed");
+        }
+
+
+
+    }
+
 }
index e04d978a3b03cede7a4f162df9f1a89ef7d48498..86bd4e2bd803800bd2766ddb6f33e8fc7cbeb566 100644 (file)
@@ -4320,7 +4320,7 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
                 if(p.getErrorIndex() != -1)
                     continue;
                 else
-                    errln("error: unexpected parse success..."+item.parseString + " w/ lenient="+item.leniency+" should have faile");
+                    errln("error: unexpected parse success..."+item.parseString + " w/ lenient="+item.leniency+" should have failed");
             }
             if(p.getErrorIndex() != -1) {
                 errln("error: parse error for string " +item.parseString + " -- idx["+p.getIndex()+"] errIdx["+p.getErrorIndex()+"]");
@@ -4335,5 +4335,5 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
             }
         }
     }
-
+    
 }