From feeda8cd901c549759391933603ba2431d89730f Mon Sep 17 00:00:00 2001 From: Alan Liu Date: Sat, 16 Oct 1999 01:35:16 +0000 Subject: [PATCH] [ICU-32] DateFormat does not parse Feb 29 2000. This is a bug in GregorianCalendar era handling. Fixed algorithm, added test. X-Commit-URL: https://ssl.icu-project.org/trac/changeset/70 --- icu4c/source/i18n/gregocal.cpp | 17 +++++++-- icu4c/source/i18n/gregocal.h | 8 ++++ icu4c/source/test/intltest/dtfmrgts.cpp | 51 +++++++++++++++++++++++++ icu4c/source/test/intltest/dtfmrgts.h | 1 + 4 files changed, 74 insertions(+), 3 deletions(-) diff --git a/icu4c/source/i18n/gregocal.cpp b/icu4c/source/i18n/gregocal.cpp index 1e761236f56..970303d73b0 100644 --- a/icu4c/source/i18n/gregocal.cpp +++ b/icu4c/source/i18n/gregocal.cpp @@ -34,6 +34,8 @@ * 09/14/98 stephen Changed type of kOneDay, kOneWeek to double. * Fixed bug in roll() * 10/15/99 aliu Fixed j31, incorrect WEEK_OF_YEAR computation. +* 10/15/99 aliu Fixed j32, cannot set date to Feb 29 2000 AD. +* {JDK bug 4210209 4209272} ******************************************************************************** */ @@ -540,7 +542,7 @@ int32_t GregorianCalendar::monthLength(int32_t month) const { int32_t year = internalGet(YEAR); - if(internalGet(ERA) == BC) { + if(internalGetEra() == BC) { year = 1 - year; } @@ -614,7 +616,7 @@ GregorianCalendar::computeFields(UErrorCode& status) // Time to fields takes the wall millis (Standard or DST). timeToFields(localMillis, FALSE, status); - uint8_t era = (uint8_t) internalGet(ERA); + uint8_t era = (uint8_t) internalGetEra(); int32_t year = internalGet(YEAR); int32_t month = internalGet(MONTH); int32_t date = internalGet(DATE); @@ -1198,7 +1200,7 @@ GregorianCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) if (field == YEAR) { int32_t year = internalGet(YEAR); - if (internalGet(ERA) == AD) { + if (internalGetEra() == AD) { year += amount; if (year > 0) set(YEAR, year); @@ -1795,4 +1797,13 @@ GregorianCalendar::getISOYear(UErrorCode& status) return isoYear; } +/** + * Return the ERA. We need a special method for this because the + * default ERA is AD, but a zero (unset) ERA is BC. + */ +int32_t +GregorianCalendar::internalGetEra() const { + return isSet(ERA) ? internalGet(ERA) : AD; +} + //eof diff --git a/icu4c/source/i18n/gregocal.h b/icu4c/source/i18n/gregocal.h index 5c450997b36..0207342c20c 100644 --- a/icu4c/source/i18n/gregocal.h +++ b/icu4c/source/i18n/gregocal.h @@ -23,6 +23,8 @@ * Fixed bug in roll() * 10/15/99 aliu Fixed j31, incorrect WEEK_OF_YEAR computation. * Added documentation of WEEK_OF_YEAR computation. +* 10/15/99 aliu Fixed j32, cannot set date to Feb 29 2000 AD. +* {JDK bug 4210209 4209272} ******************************************************************************** */ @@ -460,6 +462,12 @@ private: */ int32_t getISOYear(UErrorCode& status); + /** + * Return the ERA. We need a special method for this because the + * default ERA is AD, but a zero (unset) ERA is BC. + */ + int32_t internalGetEra() const; + // this is 2^52 - 1, the largest allowable mantissa with a 0 exponent in a 64-bit double static const UDate EARLIEST_SUPPORTED_MILLIS; static const UDate LATEST_SUPPORTED_MILLIS; diff --git a/icu4c/source/test/intltest/dtfmrgts.cpp b/icu4c/source/test/intltest/dtfmrgts.cpp index e4431000527..87ccc9cfc34 100644 --- a/icu4c/source/test/intltest/dtfmrgts.cpp +++ b/icu4c/source/test/intltest/dtfmrgts.cpp @@ -54,6 +54,7 @@ DateFormatRegressionTest::runIndexedTest( int32_t index, bool_t exec, char* &nam CASE(20,Test4151706) CASE(21,Test4162071) CASE(22,Test4182066) + CASE(23,Test4210209) default: name = ""; break; } @@ -1086,5 +1087,55 @@ void DateFormatRegressionTest::Test4182066() { } } +/** + * j32 {JDK Bug 4210209 4209272} + * DateFormat cannot parse Feb 29 2000 when setLenient(false) + */ +void +DateFormatRegressionTest::Test4210209() { + UErrorCode status = U_ZERO_ERROR; + UnicodeString pattern("MMM d, yyyy"); + SimpleDateFormat sfmt(pattern, Locale::US, status); + SimpleDateFormat sdisp("MMM dd yyyy GG", Locale::US, status); + DateFormat& fmt = *(DateFormat*)&sfmt; // Yuck: See j25 + DateFormat& disp = *(DateFormat*)&sdisp; // Yuck: See j25 + if (FAILURE(status)) { + errln("Couldn't create SimpleDateFormat"); + return; + } + Calendar* calx = (Calendar*)fmt.getCalendar(); // cast away const! + calx->setLenient(FALSE); + UDate d = date(2000-1900, Calendar::FEBRUARY, 29); + UnicodeString s, ss; + fmt.format(d, s); + logln(disp.format(d, ss.remove()) + " f> " + pattern + + " => \"" + s + "\""); + ParsePosition pos(0); + d = fmt.parse(s, pos); + logln(UnicodeString("\"") + s + "\" p> " + pattern + + " => " + disp.format(d, ss.remove())); + logln(UnicodeString("Parse pos = ") + pos.getIndex() + + ", error pos = " + pos.getErrorIndex()); + if (pos.getErrorIndex() != -1) { + errln(UnicodeString("FAIL: Error index should be -1")); + } + + // The underlying bug is in GregorianCalendar. If the following lines + // succeed, the bug is fixed. If the bug isn't fixed, they will throw + // an exception. + GregorianCalendar cal(status); + if (FAILURE(status)) { + errln("FAIL: Unable to create Calendar"); + return; + } + cal.clear(); + cal.setLenient(FALSE); + cal.set(2000, Calendar::FEBRUARY, 29); // This should work! + logln(UnicodeString("Attempt to set Calendar to Feb 29 2000: ") + + disp.format(cal.getTime(status), ss.remove())); + if (FAILURE(status)) { + errln("FAIL: Unable to set Calendar to Feb 29 2000"); + } +} //eof diff --git a/icu4c/source/test/intltest/dtfmrgts.h b/icu4c/source/test/intltest/dtfmrgts.h index a40c1e15b29..d5a5e70f80b 100644 --- a/icu4c/source/test/intltest/dtfmrgts.h +++ b/icu4c/source/test/intltest/dtfmrgts.h @@ -51,6 +51,7 @@ public: void Test4151706(void); void Test4162071(void); void Test4182066(); + void Test4210209(); }; #endif // _DATEFORMATREGRESSIONTEST_