From: Scott Russell Date: Tue, 4 Mar 2014 14:39:12 +0000 (+0000) Subject: ICU-10336 merge branch into trunk X-Git-Tag: milestone-59-0-1~2093 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b0c6d643dbf2fb524e40e990248364f9887034b2;p=icu ICU-10336 merge branch into trunk X-SVN-Rev: 35318 --- diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java index cf555985e6f..fe00e31de78 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DateFormat.java @@ -456,14 +456,21 @@ public abstract class DateFormat extends UFormat { public enum BooleanAttribute { /** * indicates whitespace tolerance. Also included is trailing dot tolerance. - * @internal ICU technology preview + * @draft ICU 53 */ PARSE_ALLOW_WHITESPACE, /** - * indicates tolerance of numeric data when String data may be assumed. eg: YEAR_NAME_FIELD - * @internal ICU technology preview + * indicates tolerance of numeric data when String data may be assumed. + * e.g. YEAR_NAME_FIELD + * @draft ICU 53 */ PARSE_ALLOW_NUMERIC, + /** + * indicates tolerance of pattern mismatch between input data and specified format pattern. + * e.g. accepting "September" for a month pattern of MMM ("Sep") + * @draft ICU 53 + */ + PARSE_MULTIPLE_PATTERNS_FOR_MATCH, /** * indicates tolerance of a partial literal match * @draft ICU 53 diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java index 0597a4321ba..4fe544f0cfd 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java @@ -28,7 +28,6 @@ import com.ibm.icu.impl.ICUCache; import com.ibm.icu.impl.PatternProps; import com.ibm.icu.impl.SimpleCache; import com.ibm.icu.lang.UCharacter; -import com.ibm.icu.text.BreakIterator; import com.ibm.icu.text.TimeZoneFormat.Style; import com.ibm.icu.text.TimeZoneFormat.TimeType; import com.ibm.icu.util.BasicTimeZone; @@ -2918,21 +2917,27 @@ public class SimpleDateFormat extends DateFormat { // count >= 3 // i.e., MMM/MMMM or LLL/LLLL // Want to be able to parse both short and long forms. boolean haveMonthPat = (formatData.leapMonthPatterns != null && formatData.leapMonthPatterns.length >= DateFormatSymbols.DT_MONTH_PATTERN_COUNT); - // Try count == 4 first: - int newStart = (patternCharIndex == 2)? + // Try count == 4 first:, unless we're strict + int newStart = 0; + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 4) { + newStart = (patternCharIndex == 2)? matchString(text, start, Calendar.MONTH, formatData.months, (haveMonthPat)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_FORMAT_WIDE]: null, cal): matchString(text, start, Calendar.MONTH, formatData.standaloneMonths, (haveMonthPat)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_STANDALONE_WIDE]: null, cal); if (newStart > 0) { return newStart; - } else { // count == 4 failed, now try count == 3 + } + } + // count == 4 failed, now try count == 3 + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 3) { return (patternCharIndex == 2)? matchString(text, start, Calendar.MONTH, formatData.shortMonths, (haveMonthPat)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV]: null, cal): matchString(text, start, Calendar.MONTH, formatData.standaloneShortMonths, (haveMonthPat)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_STANDALONE_ABBREV]: null, cal); } + return newStart; } case 4: // 'k' - HOUR_OF_DAY (1..24) // [We computed 'value' above.] @@ -2970,20 +2975,28 @@ public class SimpleDateFormat extends DateFormat { case 9: { // 'E' - DAY_OF_WEEK // Want to be able to parse at least wide, abbrev, short, and narrow forms. int newStart = 0; - if ((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.weekdays, null, cal)) > 0) { // try EEEE wide - return newStart; - } - if ((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.shortWeekdays, null, cal)) > 0) { // try EEE abbrev - return newStart; - } - if (formatData.shorterWeekdays != null) { - if((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.shorterWeekdays, null, cal)) > 0) { // try EEEEEE short + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 4) { + if ((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.weekdays, null, cal)) > 0) { // try EEEE wide return newStart; } } - if (formatData.narrowWeekdays != null) { - if((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.narrowWeekdays, null, cal)) > 0) { // try EEEEE narrow + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 3) { + if ((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.shortWeekdays, null, cal)) > 0) { // try EEE abbrev return newStart; + } + } + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 6) { + if (formatData.shorterWeekdays != null) { + if((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.shorterWeekdays, null, cal)) > 0) { // try EEEEEE short + return newStart; + } + } + } + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 5) { + if (formatData.narrowWeekdays != null) { + if((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.narrowWeekdays, null, cal)) > 0) { // try EEEEE narrow + return newStart; + } } } return newStart; @@ -2995,13 +3008,21 @@ public class SimpleDateFormat extends DateFormat { 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) { - return newStart; - } else if ((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.standaloneShortWeekdays, null, cal)) > 0) { // try ccc abbrev - return newStart; - } else if (formatData.standaloneShorterWeekdays != null) { - return matchString(text, start, Calendar.DAY_OF_WEEK, formatData.standaloneShorterWeekdays, null, cal); // try cccccc short + int newStart = 0; + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 4) { + if ((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.standaloneWeekdays, null, cal)) > 0) { // try cccc wide + return newStart; + } + } + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 3) { + if ((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.standaloneShortWeekdays, null, cal)) > 0) { // try ccc abbrev + return newStart; + } + } + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 6) { + if (formatData.standaloneShorterWeekdays != null) { + return matchString(text, start, Calendar.DAY_OF_WEEK, formatData.standaloneShorterWeekdays, null, cal); // try cccccc short + } } return newStart; } @@ -3145,14 +3166,18 @@ public class SimpleDateFormat extends DateFormat { // count >= 3 // i.e., QQQ or QQQQ // Want to be able to parse both short and long forms. // Try count == 4 first: - int newStart = matchQuarterString(text, start, Calendar.MONTH, - formatData.quarters, cal); - if (newStart > 0) { - return newStart; - } else { // count == 4 failed, now try count == 3 + int newStart = 0; + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 4) { + if((newStart = matchQuarterString(text, start, Calendar.MONTH, formatData.quarters, cal)) > 0) { + return newStart; + } + } + // count == 4 failed, now try count == 3 + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 3) { return matchQuarterString(text, start, Calendar.MONTH, formatData.shortQuarters, cal); } + return newStart; } case 28: // 'q' - STANDALONE QUARTER @@ -3167,14 +3192,18 @@ public class SimpleDateFormat extends DateFormat { // count >= 3 // i.e., qqq or qqqq // Want to be able to parse both short and long forms. // Try count == 4 first: - int newStart = matchQuarterString(text, start, Calendar.MONTH, - formatData.standaloneQuarters, cal); - if (newStart > 0) { - return newStart; - } else { // count == 4 failed, now try count == 3 + int newStart = 0; + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 4) { + if((newStart = matchQuarterString(text, start, Calendar.MONTH, formatData.standaloneQuarters, cal)) > 0) { + return newStart; + } + } + // count == 4 failed, now try count == 3 + if(getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH) || count == 3) { return matchQuarterString(text, start, Calendar.MONTH, formatData.standaloneShortQuarters, cal); } + return newStart; } default: diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatTest.java index 8753902d12c..944712ca8e9 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateFormatTest.java @@ -4499,4 +4499,91 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk { } } } + + public void TestParseMultiPatternMatch() { + // For details see http://bugs.icu-project.org/trac/ticket/10336 + + class TestMultiPatternMatchItem { + public boolean leniency; + public String parseString; + public String pattern; + public String expectedResult; // null indicates expected error + // Simple constructor + public TestMultiPatternMatchItem(boolean len, String parString, String patt, String expResult) { + leniency = len; + pattern = patt; + parseString = parString; + expectedResult = expResult; + } + }; + + final TestMultiPatternMatchItem[] items = { + // leniency parse String pattern expected result + new TestMultiPatternMatchItem(true, "2013-Sep 13", "yyyy-MMM dd", "2013-Sep 13"), + new TestMultiPatternMatchItem(true, "2013-September 14", "yyyy-MMM dd", "2013-Sep 14"), + new TestMultiPatternMatchItem(false, "2013-September 15", "yyyy-MMM dd", null), + new TestMultiPatternMatchItem(false, "2013-September 16", "yyyy-MMMM dd", "2013-September 16"), + new TestMultiPatternMatchItem(true, "2013-Sep 17", "yyyy-LLL dd", "2013-Sep 17"), + new TestMultiPatternMatchItem(true, "2013-September 18", "yyyy-LLL dd", "2013-Sep 18"), + new TestMultiPatternMatchItem(false, "2013-September 19", "yyyy-LLL dd", null), + new TestMultiPatternMatchItem(false, "2013-September 20", "yyyy-LLLL dd", "2013-September 20"), + new TestMultiPatternMatchItem(true, "2013 Sat Sep 21", "yyyy EEE MMM dd", "2013 Sat Sep 21"), + new TestMultiPatternMatchItem(true, "2013 Sunday Sep 22", "yyyy EEE MMM dd", "2013 Sun Sep 22"), + new TestMultiPatternMatchItem(false, "2013 Monday Sep 23", "yyyy EEE MMM dd", null), + new TestMultiPatternMatchItem(false, "2013 Tuesday Sep 24", "yyyy EEEE MMM dd", "2013 Tuesday Sep 24"), + new TestMultiPatternMatchItem(true, "2013 Wed Sep 25", "yyyy eee MMM dd", "2013 Wed Sep 25"), + new TestMultiPatternMatchItem(true, "2013 Thu Sep 26", "yyyy eee MMM dd", "2013 Thu Sep 26"), + new TestMultiPatternMatchItem(false, "2013 Friday Sep 27", "yyyy eee MMM dd", null), + new TestMultiPatternMatchItem(false, "2013 Saturday Sep 28", "yyyy eeee MMM dd", "2013 Saturday Sep 28"), + new TestMultiPatternMatchItem(true, "2013 Sun Sep 29", "yyyy ccc MMM dd", "2013 Sun Sep 29"), + new TestMultiPatternMatchItem(true, "2013 Monday Sep 30", "yyyy ccc MMM dd", "2013 Mon Sep 30"), + new TestMultiPatternMatchItem(false, "2013 Sunday Oct 13", "yyyy ccc MMM dd", null), + new TestMultiPatternMatchItem(false, "2013 Monday Oct 14", "yyyy cccc MMM dd", "2013 Monday Oct 14"), + new TestMultiPatternMatchItem(true, "2013 Oct 15 Q4", "yyyy MMM dd QQQ", "2013 Oct 15 Q4"), + new TestMultiPatternMatchItem(true, "2013 Oct 16 4th quarter", "yyyy MMM dd QQQ", "2013 Oct 16 Q4"), + new TestMultiPatternMatchItem(false, "2013 Oct 17 4th quarter", "yyyy MMM dd QQQ", null), + new TestMultiPatternMatchItem(false, "2013 Oct 18 Q4", "yyyy MMM dd QQQ", "2013 Oct 18 Q4"), + new TestMultiPatternMatchItem(true, "2013 Oct 19 Q4", "yyyy MMM dd qqqq", "2013 Oct 19 4th quarter"), + new TestMultiPatternMatchItem(true, "2013 Oct 20 4th quarter", "yyyy MMM dd qqqq", "2013 Oct 20 4th quarter"), + new TestMultiPatternMatchItem(false, "2013 Oct 21 Q4", "yyyy MMM dd qqqq", null), + new TestMultiPatternMatchItem(false, "2013 Oct 22 4th quarter", "yyyy MMM dd qqqq", "2013 Oct 22 4th quarter"), + }; + + StringBuffer result = new StringBuffer(); + Date d = new Date(); + GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"), Locale.US); + SimpleDateFormat sdfmt = new SimpleDateFormat(); + ParsePosition p = new ParsePosition(0); + for (TestMultiPatternMatchItem item: items) { + cal.clear(); + sdfmt.setCalendar(cal); + sdfmt.applyPattern(item.pattern); + sdfmt.setLenient(item.leniency); + sdfmt.setBooleanAttribute(DateFormat.BooleanAttribute.PARSE_MULTIPLE_PATTERNS_FOR_MATCH, item.leniency); + result.setLength(0); + p.setIndex(0); + p.setErrorIndex(-1); + d = sdfmt.parse(item.parseString, p); + if(item.expectedResult == null) { + if(p.getErrorIndex() != -1) + continue; + else + 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()+"]"); + continue; + } + cal.setTime(d); + result = sdfmt.format(cal, result, new FieldPosition(0)); + if(!result.toString().equalsIgnoreCase(item.expectedResult)) { + errln("error: unexpected format result. expected - " + item.expectedResult + " but result was - " + result); + } else { + logln("formatted results match! - " + result.toString()); + } + } + + } + + }