mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-13 08:53:20 +00:00
ICU-2656 make HHmmss not parse negative numbers; clean up handling of 'u' (EXTENDED_YEAR)
X-SVN-Rev: 11255
This commit is contained in:
parent
e7be1a69e2
commit
8c8a6bbe08
3 changed files with 155 additions and 81 deletions
|
@ -4,8 +4,8 @@
|
|||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/dev/test/format/DateFormatTest.java,v $
|
||||
* $Date: 2003/01/24 19:56:07 $
|
||||
* $Revision: 1.11 $
|
||||
* $Date: 2003/03/07 01:05:51 $
|
||||
* $Revision: 1.12 $
|
||||
*
|
||||
*****************************************************************************************
|
||||
*/
|
||||
|
@ -841,42 +841,38 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
|||
*/
|
||||
public void TestSpaceParsing() {
|
||||
|
||||
String PARSE_FAILURE = "parse failure";
|
||||
String DATA[] = {
|
||||
"yyyy MM dd",
|
||||
|
||||
// pattern, input, expexted output (in quotes)
|
||||
"MMMM d yy", " 04 05 06", PARSE_FAILURE, // MMMM wants Apr/April
|
||||
"MMMM d yy", "04 05 06", PARSE_FAILURE,
|
||||
"MM d yy", " 04 05 06", "\"2006 04 05\"",
|
||||
"MM d yy", "04 05 06", "\"2006 04 05\"",
|
||||
"MMMM d yy", " Apr 05 06", "\"2006 04 05\"",
|
||||
"MMMM d yy", "Apr 05 06", "\"2006 04 05\"",
|
||||
"MMMM d yy", " 04 05 06", null, // MMMM wants Apr/April
|
||||
null, "04 05 06", null,
|
||||
"MM d yy", " 04 05 06", "2006 04 05",
|
||||
null, "04 05 06", "2006 04 05",
|
||||
"MMMM d yy", " Apr 05 06", "2006 04 05",
|
||||
null, "Apr 05 06", "2006 04 05",
|
||||
};
|
||||
|
||||
Locale en = new Locale("en", "", "");
|
||||
SimpleDateFormat sdfObj = new SimpleDateFormat("", en);
|
||||
expectParse(DATA, new Locale("en"));
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i=0; i<DATA.length; i+=3) {
|
||||
sdfObj.applyPattern(DATA[i]);
|
||||
ParsePosition pp = new ParsePosition(0);
|
||||
Date udDate = sdfObj.parse(DATA[i+1], pp);
|
||||
String output;
|
||||
if (pp.getErrorIndex() == -1) {
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy MM dd", en);
|
||||
output = "\"" + formatter.format(udDate) + "\"";
|
||||
} else {
|
||||
output = PARSE_FAILURE;
|
||||
}
|
||||
String exp = DATA[i+2];
|
||||
if (output.equals(exp)) {
|
||||
logln("Ok: Parse of \"" + DATA[i+1] + "\" with \"" +
|
||||
DATA[i] + "\" => " + output);
|
||||
} else {
|
||||
errln("FAIL: Parse of \"" + DATA[i+1] + "\" with \"" +
|
||||
DATA[i] + "\" => " +
|
||||
output + ", expected " + exp);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Test handling of "HHmmss" pattern.
|
||||
*/
|
||||
public void TestExactCountFormat() {
|
||||
String DATA[] = {
|
||||
"yyyy MM dd HH:mm:ss",
|
||||
|
||||
// pattern, input, expected parse or null if expect parse failure
|
||||
"HHmmss", "123456", "1970 01 01 12:34:56",
|
||||
null, "12345", "1970 01 01 12:34:05",
|
||||
null, "1234", null,
|
||||
null, "00-05", null,
|
||||
null, "12-34", null,
|
||||
null, "00+05", null,
|
||||
};
|
||||
|
||||
expectParse(DATA, new Locale("en"));
|
||||
}
|
||||
|
||||
public void TestCoverage() {
|
||||
|
@ -903,4 +899,68 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
|||
errln("fail, date format symbols not equal");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test parsing. Input is an array that starts with the following
|
||||
* header:
|
||||
*
|
||||
* [0] = pattern string to parse [i+2] with
|
||||
*
|
||||
* followed by test cases, each of which is 3 array elements:
|
||||
*
|
||||
* [i] = pattern, or null to reuse prior pattern
|
||||
* [i+1] = input string
|
||||
* [i+2] = expected parse result (parsed with pattern [0])
|
||||
*
|
||||
* If expect parse failure, then [i+2] should be null.
|
||||
*/
|
||||
void expectParse(String[] data, Locale loc) {
|
||||
Date FAIL = null;
|
||||
String FAIL_STR = "parse failure";
|
||||
int i = 0;
|
||||
|
||||
SimpleDateFormat fmt = new SimpleDateFormat("", loc);
|
||||
SimpleDateFormat ref = new SimpleDateFormat(data[i++], loc);
|
||||
SimpleDateFormat gotfmt = new SimpleDateFormat("G yyyy MM dd HH:mm:ss z", loc);
|
||||
|
||||
String currentPat = null;
|
||||
while (i<data.length) {
|
||||
String pattern = data[i++];
|
||||
String input = data[i++];
|
||||
String expected = data[i++];
|
||||
|
||||
if (pattern != null) {
|
||||
fmt.applyPattern(pattern);
|
||||
currentPat = pattern;
|
||||
}
|
||||
String gotstr = FAIL_STR;
|
||||
Date got;
|
||||
try {
|
||||
got = fmt.parse(input);
|
||||
gotstr = gotfmt.format(got);
|
||||
} catch (ParseException e1) {
|
||||
got = FAIL;
|
||||
}
|
||||
|
||||
Date exp = FAIL;
|
||||
String expstr = FAIL_STR;
|
||||
if (expected != null) {
|
||||
expstr = expected;
|
||||
try {
|
||||
exp = ref.parse(expstr);
|
||||
} catch (ParseException e2) {
|
||||
errln("FAIL: Internal test error");
|
||||
}
|
||||
}
|
||||
|
||||
if (got == exp || (got != null && got.equals(exp))) {
|
||||
logln("Ok: " + input + " x " +
|
||||
currentPat + " => " + gotstr);
|
||||
} else {
|
||||
errln("FAIL: " + input + " x " +
|
||||
currentPat + " => " + gotstr + ", expected " +
|
||||
expstr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
*******************************************************************************
|
||||
*
|
||||
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/text/DateFormatSymbols.java,v $
|
||||
* $Date: 2002/12/18 21:20:52 $
|
||||
* $Revision: 1.15 $
|
||||
* $Date: 2003/03/07 01:05:48 $
|
||||
* $Revision: 1.16 $
|
||||
*
|
||||
*****************************************************************************************
|
||||
*/
|
||||
|
@ -76,6 +76,11 @@ import java.util.Vector;
|
|||
*/
|
||||
public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
|
||||
// TODO make sure local pattern char string is 18 characters long,
|
||||
// that is, that it encompasses the new 'u' char for
|
||||
// EXTENDED_YEAR. Two options: 1. Make sure resource data is
|
||||
// correct; 2. Make code add in 'u' at end if len == 17.
|
||||
|
||||
/**
|
||||
* Construct a DateFormatSymbols object by loading format data from
|
||||
* resources for the default locale.
|
||||
|
@ -185,7 +190,7 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
|||
* Unlocalized date-time pattern characters. For example: 'y', 'd', etc.
|
||||
* All locales use the same these unlocalized pattern characters.
|
||||
*/
|
||||
static final String patternChars = "GyMdkHmsSEDFwWahKz";
|
||||
static final String patternChars = "GyMdkHmsSEDFwWahKzu";
|
||||
|
||||
/**
|
||||
* Localized date-time pattern characters. For example, a locale may
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
*******************************************************************************
|
||||
*
|
||||
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/text/SimpleDateFormat.java,v $
|
||||
* $Date: 2002/12/05 01:22:28 $
|
||||
* $Revision: 1.17 $
|
||||
* $Date: 2003/03/07 01:05:50 $
|
||||
* $Revision: 1.18 $
|
||||
*
|
||||
*****************************************************************************************
|
||||
*/
|
||||
|
@ -246,6 +246,11 @@ public class SimpleDateFormat extends DateFormat {
|
|||
private static final String GMT_MINUS = "GMT-";
|
||||
private static final String GMT = "GMT";
|
||||
|
||||
// This prefix is designed to NEVER MATCH real text, in order to
|
||||
// suppress the parsing of negative numbers. Adjust as needed (if
|
||||
// this becomes valid Unicode).
|
||||
private static final String SUPPRESS_NEGATIVE_PREFIX = "\uAB00";
|
||||
|
||||
/**
|
||||
* Cache to hold the DateTimePatterns of a Locale.
|
||||
*/
|
||||
|
@ -455,7 +460,8 @@ public class SimpleDateFormat extends DateFormat {
|
|||
Calendar.SECOND, Calendar.MILLISECOND, Calendar.DAY_OF_WEEK,
|
||||
Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK_IN_MONTH,
|
||||
Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH,
|
||||
Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR, Calendar.ZONE_OFFSET
|
||||
Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR, Calendar.ZONE_OFFSET,
|
||||
Calendar.EXTENDED_YEAR
|
||||
};
|
||||
|
||||
// Map index into pattern character string to DateFormat field number
|
||||
|
@ -492,15 +498,10 @@ public class SimpleDateFormat extends DateFormat {
|
|||
int maxIntCount = Integer.MAX_VALUE;
|
||||
String current = "";
|
||||
|
||||
// TEMPORARY HACK TODO fix this
|
||||
if (ch == 'u') { // 'u' - EXTENDED_YEAR
|
||||
return zeroPaddingNumber(cal.get(Calendar.EXTENDED_YEAR),
|
||||
1, maxIntCount);
|
||||
}
|
||||
|
||||
if ((patternCharIndex=formatData.patternChars.indexOf(ch)) == -1)
|
||||
if ((patternCharIndex=formatData.patternChars.indexOf(ch)) == -1) {
|
||||
throw new IllegalArgumentException("Illegal pattern character " +
|
||||
"'" + ch + "'");
|
||||
}
|
||||
|
||||
int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
|
||||
int value = cal.get(field);
|
||||
|
@ -609,6 +610,7 @@ public class SimpleDateFormat extends DateFormat {
|
|||
// case 12: // 'w' - WEEK_OF_YEAR
|
||||
// case 13: // 'W' - WEEK_OF_MONTH
|
||||
// case 16: // 'K' - HOUR: 0-based. eg, 11PM + 1 hour =>> 0 AM
|
||||
// case 18: // 'u' - EXTENDED_YEAR
|
||||
current = zeroPaddingNumber(value, count, maxIntCount);
|
||||
break;
|
||||
} // switch (patternCharIndex)
|
||||
|
@ -651,6 +653,7 @@ public class SimpleDateFormat extends DateFormat {
|
|||
char prevCh = 0;
|
||||
int count = 0;
|
||||
int interQuoteCount = 1; // Number of chars between quotes
|
||||
boolean allowNegative = true;
|
||||
|
||||
for (int i=0; i<pattern.length(); ++i)
|
||||
{
|
||||
|
@ -701,7 +704,8 @@ public class SimpleDateFormat extends DateFormat {
|
|||
{
|
||||
int startOffset = start;
|
||||
start=subParse(text, start, prevCh, count,
|
||||
false, ambiguousYear, cal);
|
||||
false, allowNegative, ambiguousYear, cal);
|
||||
allowNegative = true;
|
||||
if ( start<0 ) {
|
||||
pos.setErrorIndex(startOffset);
|
||||
pos.setIndex(oldStart);
|
||||
|
@ -736,8 +740,12 @@ public class SimpleDateFormat extends DateFormat {
|
|||
// obeyCount. That's because the next field directly
|
||||
// abuts this one, so we have to use the count to know when
|
||||
// to stop parsing. [LIU]
|
||||
// Don't allow negatives in this field or in the next.
|
||||
// This prevents anomalies like HHmmss matching 12-34
|
||||
// as 12:-3:4, or 11:57:04.
|
||||
start = subParse(text, start, prevCh, count, true,
|
||||
ambiguousYear, cal);
|
||||
allowNegative, ambiguousYear, cal);
|
||||
allowNegative = false;
|
||||
if (start < 0) {
|
||||
pos.setErrorIndex(startOffset);
|
||||
pos.setIndex(oldStart);
|
||||
|
@ -759,7 +767,8 @@ public class SimpleDateFormat extends DateFormat {
|
|||
// where ch = '-', ':', or ' ', repectively.
|
||||
int startOffset = start;
|
||||
start=subParse(text, start, prevCh, count,
|
||||
false, ambiguousYear, cal);
|
||||
false, allowNegative, ambiguousYear, cal);
|
||||
allowNegative = true;
|
||||
if ( start < 0 ) {
|
||||
pos.setErrorIndex(startOffset);
|
||||
pos.setIndex(oldStart);
|
||||
|
@ -797,7 +806,7 @@ public class SimpleDateFormat extends DateFormat {
|
|||
{
|
||||
int startOffset = start;
|
||||
start=subParse(text, start, prevCh, count,
|
||||
false, ambiguousYear, cal);
|
||||
false, allowNegative, ambiguousYear, cal);
|
||||
if ( start < 0 ) {
|
||||
pos.setIndex(oldStart);
|
||||
pos.setErrorIndex(startOffset);
|
||||
|
@ -983,7 +992,8 @@ public class SimpleDateFormat extends DateFormat {
|
|||
* @stable ICU 2.0
|
||||
*/
|
||||
protected int subParse(String text, int start, char ch, int count,
|
||||
boolean obeyCount, boolean[] ambiguousYear, Calendar cal)
|
||||
boolean obeyCount, boolean allowNegative,
|
||||
boolean[] ambiguousYear, Calendar cal)
|
||||
{
|
||||
Number number = null;
|
||||
int value = 0;
|
||||
|
@ -991,33 +1001,9 @@ public class SimpleDateFormat extends DateFormat {
|
|||
ParsePosition pos = new ParsePosition(0);
|
||||
int patternCharIndex = -1;
|
||||
|
||||
// TEMPORARY HACK TODO fix this
|
||||
if (ch == 'u') { // 'u' - EXTENDED_YEAR
|
||||
pos.setIndex(start);
|
||||
for (;;) {
|
||||
if (pos.getIndex() >= text.length()) return -start;
|
||||
char c = text.charAt(pos.getIndex());
|
||||
if (c != ' ' && c != '\t') break;
|
||||
pos.setIndex(pos.getIndex()+1);
|
||||
}
|
||||
if (obeyCount) {
|
||||
if ((start+count) > text.length()) {
|
||||
return -start;
|
||||
}
|
||||
number = numberFormat.parse(text.substring(0, start+count), pos);
|
||||
} else {
|
||||
number = numberFormat.parse(text, pos);
|
||||
}
|
||||
if (number == null) {
|
||||
return -start;
|
||||
}
|
||||
value = number.intValue();
|
||||
cal.set(Calendar.EXTENDED_YEAR, value);
|
||||
return pos.getIndex();
|
||||
}
|
||||
|
||||
if ((patternCharIndex=formatData.patternChars.indexOf(ch)) == -1)
|
||||
if ((patternCharIndex=formatData.patternChars.indexOf(ch)) == -1) {
|
||||
return -start;
|
||||
}
|
||||
|
||||
int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
|
||||
|
||||
|
@ -1049,9 +1035,9 @@ public class SimpleDateFormat extends DateFormat {
|
|||
if (obeyCount)
|
||||
{
|
||||
if ((start+count) > text.length()) return -start;
|
||||
number = numberFormat.parse(text.substring(0, start+count), pos);
|
||||
number = parseInt(text.substring(0, start+count), pos, allowNegative);
|
||||
}
|
||||
else number = numberFormat.parse(text, pos);
|
||||
else number = parseInt(text, pos, allowNegative);
|
||||
if (number == null)
|
||||
return -start;
|
||||
value = number.intValue();
|
||||
|
@ -1257,14 +1243,15 @@ public class SimpleDateFormat extends DateFormat {
|
|||
// case 12: // 'w' - WEEK_OF_YEAR
|
||||
// case 13: // 'W' - WEEK_OF_MONTH
|
||||
// case 16: // 'K' - HOUR: 0-based. eg, 11PM + 1 hour =>> 0 AM
|
||||
// case 18: // 'u' - EXTENDED_YEAR
|
||||
|
||||
// Handle "generic" fields
|
||||
if (obeyCount)
|
||||
{
|
||||
if ((start+count) > text.length()) return -start;
|
||||
number = numberFormat.parse(text.substring(0, start+count), pos);
|
||||
number = parseInt(text.substring(0, start+count), pos, allowNegative);
|
||||
}
|
||||
else number = numberFormat.parse(text, pos);
|
||||
else number = parseInt(text, pos, allowNegative);
|
||||
if (number != null) {
|
||||
cal.set(field, number.intValue());
|
||||
return pos.getIndex();
|
||||
|
@ -1273,6 +1260,28 @@ public class SimpleDateFormat extends DateFormat {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an integer using fNumberFormat. This method is semantically
|
||||
* const, but actually may modify fNumberFormat.
|
||||
*/
|
||||
private Number parseInt(String text,
|
||||
ParsePosition pos,
|
||||
boolean allowNegative) {
|
||||
String oldPrefix = null;
|
||||
DecimalFormat df = null;
|
||||
if (!allowNegative) {
|
||||
try {
|
||||
df = (DecimalFormat)numberFormat;
|
||||
oldPrefix = df.getNegativePrefix();
|
||||
df.setNegativePrefix(SUPPRESS_NEGATIVE_PREFIX);
|
||||
} catch (ClassCastException e1) {}
|
||||
}
|
||||
Number number = numberFormat.parse(text, pos);
|
||||
if (!allowNegative) {
|
||||
df.setNegativePrefix(oldPrefix);
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate a pattern, mapping each character in the from string to the
|
||||
|
|
Loading…
Add table
Reference in a new issue