ICU-6420 Fixed calendar/date symbols mismatch problem for C.

X-SVN-Rev: 25309
This commit is contained in:
Yoshito Umaoka 2009-01-23 23:15:42 +00:00
parent c6efa90f2d
commit f682a39582
4 changed files with 206 additions and 28 deletions

View File

@ -1,6 +1,6 @@
/* /*
******************************************************************************* *******************************************************************************
* Copyright (C) 1997-2008, International Business Machines Corporation and * * Copyright (C) 1997-2009, International Business Machines Corporation and *
* others. All Rights Reserved. * * others. All Rights Reserved. *
******************************************************************************* *******************************************************************************
* *
@ -514,6 +514,19 @@ SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition&
pos.setBeginIndex(0); pos.setBeginIndex(0);
pos.setEndIndex(0); pos.setEndIndex(0);
Calendar *workCal = &cal;
TimeZone *backupTZ = NULL;
if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) {
// Different calendar type
// We use the time and time zone from the input calendar, but
// do not use the input calendar for field calculation.
UDate t = cal.getTime(status);
fCalendar->setTime(t, status);
backupTZ = fCalendar->getTimeZone().clone();
fCalendar->setTimeZone(cal.getTimeZone());
workCal = fCalendar;
}
UBool inQuote = FALSE; UBool inQuote = FALSE;
UChar prevCh = 0; UChar prevCh = 0;
int32_t count = 0; int32_t count = 0;
@ -525,7 +538,7 @@ SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition&
// Use subFormat() to format a repeated pattern character // Use subFormat() to format a repeated pattern character
// when a different pattern or non-pattern character is seen // when a different pattern or non-pattern character is seen
if (ch != prevCh && count > 0) { if (ch != prevCh && count > 0) {
subFormat(appendTo, prevCh, count, pos, cal, status); subFormat(appendTo, prevCh, count, pos, *workCal, status);
count = 0; count = 0;
} }
if (ch == QUOTE) { if (ch == QUOTE) {
@ -553,7 +566,12 @@ SimpleDateFormat::format(Calendar& cal, UnicodeString& appendTo, FieldPosition&
// Format the last item in the pattern, if any // Format the last item in the pattern, if any
if (count > 0) { if (count > 0) {
subFormat(appendTo, prevCh, count, pos, cal, status); subFormat(appendTo, prevCh, count, pos, *workCal, status);
}
if (backupTZ != NULL) {
// Restore the original time zone
fCalendar->adoptTimeZone(backupTZ);
} }
// and if something failed (e.g., an invalid format character), reset our FieldPosition // and if something failed (e.g., an invalid format character), reset our FieldPosition
@ -1337,8 +1355,10 @@ UBool SimpleDateFormat::isNumeric(UChar formatChar, int32_t count) {
void void
SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& parsePos) const SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition& parsePos) const
{ {
UErrorCode status = U_ZERO_ERROR;
int32_t pos = parsePos.getIndex(); int32_t pos = parsePos.getIndex();
int32_t start = pos; int32_t start = pos;
UBool ambiguousYear[] = { FALSE }; UBool ambiguousYear[] = { FALSE };
int32_t count = 0; int32_t count = 0;
@ -1357,6 +1377,21 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
const UnicodeString numericFormatChars(NUMERIC_FORMAT_CHARS); const UnicodeString numericFormatChars(NUMERIC_FORMAT_CHARS);
TimeZone *backupTZ = NULL;
Calendar *workCal = &cal;
if (&cal != fCalendar && uprv_strcmp(cal.getType(), fCalendar->getType()) != 0) {
// Different calendar type
// We use the time/zone from the input calendar, but
// do not use the input calendar for field calculation.
fCalendar->setTime(cal.getTime(status),status);
if (U_FAILURE(status)) {
goto ExitParse;
}
backupTZ = fCalendar->getTimeZone().clone();
fCalendar->setTimeZone(cal.getTimeZone());
workCal = fCalendar;
}
for (int32_t i=0; i<fPattern.length(); ++i) { for (int32_t i=0; i<fPattern.length(); ++i) {
UChar ch = fPattern.charAt(i); UChar ch = fPattern.charAt(i);
@ -1422,14 +1457,13 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
if (fieldPat == abutPat) { if (fieldPat == abutPat) {
count -= abutPass++; count -= abutPass++;
if (count == 0) { if (count == 0) {
parsePos.setIndex(start); status = U_PARSE_ERROR;
parsePos.setErrorIndex(pos); goto ExitParse;
return;
} }
} }
pos = subParse(text, pos, ch, count, pos = subParse(text, pos, ch, count,
TRUE, FALSE, ambiguousYear, cal); TRUE, FALSE, ambiguousYear, *workCal);
// If the parse fails anywhere in the run, back up to the // If the parse fails anywhere in the run, back up to the
// start of the run and retry. // start of the run and retry.
@ -1443,15 +1477,14 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
// Handle non-numeric fields and non-abutting numeric // Handle non-numeric fields and non-abutting numeric
// fields. // fields.
else { else {
int32_t s = pos; int32_t s = subParse(text, pos, ch, count,
pos = subParse(text, pos, ch, count, FALSE, TRUE, ambiguousYear, *workCal);
FALSE, TRUE, ambiguousYear, cal);
if (pos < 0) { if (s < 0) {
parsePos.setErrorIndex(s); status = U_PARSE_ERROR;
parsePos.setIndex(start); goto ExitParse;
return;
} }
pos = s;
} }
} }
@ -1504,9 +1537,8 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
} }
// We fall through to this point if the match fails // We fall through to this point if the match fails
parsePos.setIndex(start); status = U_PARSE_ERROR;
parsePos.setErrorIndex(pos); goto ExitParse;
return;
} }
} }
@ -1538,7 +1570,6 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
// when the two-digit year is equal to the start year, and thus might fall at the // when the two-digit year is equal to the start year, and thus might fall at the
// front or the back of the default century. This only works because we adjust // front or the back of the default century. This only works because we adjust
// the year correctly to start with in other cases -- see subParse(). // the year correctly to start with in other cases -- see subParse().
UErrorCode status = U_ZERO_ERROR;
if (ambiguousYear[0] || tztype != TZTYPE_UNK) // If this is true then the two-digit year == the default start year if (ambiguousYear[0] || tztype != TZTYPE_UNK) // If this is true then the two-digit year == the default start year
{ {
// We need a copy of the fields, and we need to avoid triggering a call to // We need a copy of the fields, and we need to avoid triggering a call to
@ -1671,13 +1702,25 @@ SimpleDateFormat::parse(const UnicodeString& text, Calendar& cal, ParsePosition&
delete copy; delete copy;
} }
} }
ExitParse: ExitParse:
// Set the parsed result if local calendar is used
// instead of the input calendar
if (U_SUCCESS(status) && workCal != &cal) {
cal.setTimeZone(workCal->getTimeZone());
cal.setTime(workCal->getTime(status), status);
}
// Restore the original time zone if required
if (backupTZ != NULL) {
fCalendar->adoptTimeZone(backupTZ);
}
// If any Calendar calls failed, we pretend that we // If any Calendar calls failed, we pretend that we
// couldn't parse the string, when in reality this isn't quite accurate-- // couldn't parse the string, when in reality this isn't quite accurate--
// we did parse it; the Calendar calls just failed. // we did parse it; the Calendar calls just failed.
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
parsePos.setErrorIndex(pos); parsePos.setErrorIndex(pos);
parsePos.setIndex(start); parsePos.setIndex(start);
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
******************************************************************************** ********************************************************************************
* Copyright (C) 1997-2008, International Business Machines * Copyright (C) 1997-2009, International Business Machines
* Corporation and others. All Rights Reserved. * Corporation and others. All Rights Reserved.
******************************************************************************** ********************************************************************************
* *
@ -245,7 +245,11 @@ public:
* occurence of the timezone pattern character 'z'. * occurence of the timezone pattern character 'z'.
* *
* @param cal Calendar set to the date and time to be formatted * @param cal Calendar set to the date and time to be formatted
* into a date/time string. * into a date/time string. When the calendar type is
* different from the internal calendar held by this
* DateFormat instance, the date and the time zone will
* be inherited from the input calendar, but other calendar
* field values will be calculated by the internal calendar.
* @param appendTo Output parameter to receive result. * @param appendTo Output parameter to receive result.
* Result is appended to existing contents. * Result is appended to existing contents.
* @param fieldPosition On input: an alignment field, if desired (see examples above) * @param fieldPosition On input: an alignment field, if desired (see examples above)
@ -342,7 +346,12 @@ public:
* *
* @param text The date/time string to be parsed * @param text The date/time string to be parsed
* @param cal a Calendar set to the date and time to be formatted * @param cal a Calendar set to the date and time to be formatted
* into a date/time string. * into a date/time string. When the calendar type
* is different from the internal calendar held by this
* DateFormat instance, calendar field values will be
* parsed based on the internal calendar, then the result
* (time in milliseconds and time zone) will be set in
* this calendar.
* @param pos On input, the position at which to start parsing; on * @param pos On input, the position at which to start parsing; on
* output, the position at which parsing terminated, or the * output, the position at which parsing terminated, or the
* start position if the parse failed. * start position if the parse failed.

View File

@ -1,6 +1,6 @@
/******************************************************************** /********************************************************************
* COPYRIGHT: * COPYRIGHT:
* Copyright (c) 1997-2008, International Business Machines Corporation and * Copyright (c) 1997-2009, International Business Machines Corporation and
* others. All Rights Reserved. * others. All Rights Reserved.
********************************************************************/ ********************************************************************/
@ -74,9 +74,10 @@ void DateFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &nam
TESTCASE(34,TestRelativeClone); TESTCASE(34,TestRelativeClone);
TESTCASE(35,TestHostClone); TESTCASE(35,TestHostClone);
TESTCASE(36,TestTimeZoneDisplayName); TESTCASE(36,TestTimeZoneDisplayName);
TESTCASE(37,TestRoundtripWithCalendar);
/* /*
TESTCASE(37,TestRelativeError); TESTCASE(38,TestRelativeError);
TESTCASE(38,TestRelativeOther); TESTCASE(39,TestRelativeOther);
*/ */
default: name = ""; break; default: name = ""; break;
} }
@ -2898,6 +2899,128 @@ void DateFormatTest::TestTimeZoneDisplayName()
delete cal; delete cal;
} }
void DateFormatTest::TestRoundtripWithCalendar(void) {
UErrorCode status = U_ZERO_ERROR;
TimeZone *tz = TimeZone::createTimeZone("Europe/Paris");
TimeZone *gmt = TimeZone::createTimeZone("Etc/GMT");
Calendar *calendars[] = {
Calendar::createInstance(*tz, Locale("und@calendar=gregorian"), status),
Calendar::createInstance(*tz, Locale("und@calendar=buddhist"), status),
// Calendar::createInstance(*tz, Locale("und@calendar=hebrew"), status),
Calendar::createInstance(*tz, Locale("und@calendar=islamic"), status),
Calendar::createInstance(*tz, Locale("und@calendar=japanese"), status),
NULL
};
if (U_FAILURE(status)) {
errln("Failed to initialize calendars");
for (int i = 0; calendars[i] != NULL; i++) {
delete calendars[i];
}
return;
}
//FIXME The formatters commented out below are currently failing because of
// the calendar calculation problem reported by #6691
// The order of test formatters must match the order of calendars above.
DateFormat *formatters[] = {
DateFormat::createDateTimeInstance(DateFormat::kFull, DateFormat::kFull, Locale("en_US")), //calendar=gregorian
DateFormat::createDateTimeInstance(DateFormat::kFull, DateFormat::kFull, Locale("th_TH")), //calendar=buddhist
// DateFormat::createDateTimeInstance(DateFormat::kFull, DateFormat::kFull, Locale("he_IL@calendar=hebrew")),
DateFormat::createDateTimeInstance(DateFormat::kFull, DateFormat::kFull, Locale("ar_EG@calendar=islamic")),
// DateFormat::createDateTimeInstance(DateFormat::kFull, DateFormat::kFull, Locale("ja_JP@calendar=japanese")),
NULL
};
UDate d = Calendar::getNow();
UnicodeString buf;
FieldPosition fpos;
ParsePosition ppos;
for (int i = 0; formatters[i] != NULL; i++) {
buf.remove();
fpos.setBeginIndex(0);
fpos.setEndIndex(0);
calendars[i]->setTime(d, status);
// Normal case output - the given calendar matches the calendar
// used by the formatter
formatters[i]->format(*calendars[i], buf, fpos);
UnicodeString refStr(buf);
for (int j = 0; calendars[j] != NULL; j++) {
if (j == i) {
continue;
}
buf.remove();
fpos.setBeginIndex(0);
fpos.setEndIndex(0);
calendars[j]->setTime(d, status);
// Even the different calendar type is specified,
// we should get the same result.
formatters[i]->format(*calendars[j], buf, fpos);
if (refStr != buf) {
errln((UnicodeString)"FAIL: Different format result with a different calendar for the same time -"
+ "\n Reference calendar type=" + calendars[i]->getType()
+ "\n Another calendar type=" + calendars[j]->getType()
+ "\n Expected result=" + refStr
+ "\n Actual result=" + buf);
}
}
calendars[i]->setTimeZone(*gmt);
calendars[i]->clear();
ppos.setErrorIndex(-1);
ppos.setIndex(0);
// Normal case parse result - the given calendar matches the calendar
// used by the formatter
formatters[i]->parse(refStr, *calendars[i], ppos);
for (int j = 0; calendars[j] != NULL; j++) {
if (j == i) {
continue;
}
calendars[j]->setTimeZone(*gmt);
calendars[j]->clear();
ppos.setErrorIndex(-1);
ppos.setIndex(0);
// Even the different calendar type is specified,
// we should get the same time and time zone.
formatters[i]->parse(refStr, *calendars[j], ppos);
if (calendars[i]->getTime(status) != calendars[j]->getTime(status)
|| calendars[i]->getTimeZone() != calendars[j]->getTimeZone()) {
UnicodeString tzid;
errln((UnicodeString)"FAIL: Different parse result with a different calendar for the same string -"
+ "\n Reference calendar type=" + calendars[i]->getType()
+ "\n Another calendar type=" + calendars[j]->getType()
+ "\n Date string=" + refStr
+ "\n Expected time=" + calendars[i]->getTime(status)
+ "\n Expected time zone=" + calendars[i]->getTimeZone().getID(tzid)
+ "\n Actual time=" + calendars[j]->getTime(status)
+ "\n Actual time zone=" + calendars[j]->getTimeZone().getID(tzid));
}
}
if (U_FAILURE(status)) {
errln((UnicodeString)"FAIL: " + u_errorName(status));
break;
}
}
delete tz;
delete gmt;
for (int i = 0; calendars[i] != NULL; i++) {
delete calendars[i];
}
for (int i = 0; formatters[i] != NULL; i++) {
delete formatters[i];
}
}
/* /*
void DateFormatTest::TestRelativeError(void) void DateFormatTest::TestRelativeError(void)
{ {

View File

@ -1,6 +1,6 @@
/******************************************************************** /********************************************************************
* COPYRIGHT: * COPYRIGHT:
* Copyright (c) 1997-2007, International Business Machines Corporation and * Copyright (c) 1997-2009, International Business Machines Corporation and
* others. All Rights Reserved. * others. All Rights Reserved.
********************************************************************/ ********************************************************************/
@ -185,12 +185,15 @@ public:
void TestHostClone(void); void TestHostClone(void);
void TestTimeZoneDisplayName(void);
void TestRoundtripWithCalendar(void);
public: public:
/*** /***
* Test Relative Dates * Test Relative Dates
*/ */
void TestRelative(void); void TestRelative(void);
void TestTimeZoneDisplayName(void);
/* void TestRelativeError(void); /* void TestRelativeError(void);
void TestRelativeOther(void); void TestRelativeOther(void);
*/ */