From: Scott Russell Date: Fri, 4 Jan 2013 17:16:07 +0000 (+0000) Subject: ICU-9836 C/Java mismatch parsing years with pattern "yyyy" X-Git-Tag: milestone-59-0-1~3239 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c54b40f7dc5971a77f03ffb06647bde79108f123;p=icu ICU-9836 C/Java mismatch parsing years with pattern "yyyy" X-SVN-Rev: 33011 --- diff --git a/icu4c/source/i18n/smpdtfmt.cpp b/icu4c/source/i18n/smpdtfmt.cpp index 622a2f311a6..3b319af65d7 100644 --- a/icu4c/source/i18n/smpdtfmt.cpp +++ b/icu4c/source/i18n/smpdtfmt.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 1997-2012, International Business Machines Corporation and * +* Copyright (C) 1997-2013, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* * @@ -2541,19 +2541,22 @@ int32_t SimpleDateFormat::subParse(const UnicodeString& text, int32_t& start, UC && u_isdigit(text.charAt(start)) && u_isdigit(text.charAt(start+1))) { - // Assume for example that the defaultCenturyStart is 6/18/1903. - // This means that two-digit years will be forced into the range - // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02 - // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond - // to 1904, 1905, etc. If the year is 03, then it is 2003 if the - // other fields specify a date before 6/18, or 1903 if they specify a - // date afterwards. As a result, 03 is an ambiguous year. All other - // two-digit years are unambiguous. - if(fHaveDefaultCentury) { // check if this formatter even has a pivot year - int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100; - ambiguousYear[0] = (value == ambiguousTwoDigitYear); - value += (fDefaultCenturyStartYear/100)*100 + - (value < ambiguousTwoDigitYear ? 100 : 0); + // only adjust year for patterns less than 3. + if(count < 3) { + // Assume for example that the defaultCenturyStart is 6/18/1903. + // This means that two-digit years will be forced into the range + // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02 + // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond + // to 1904, 1905, etc. If the year is 03, then it is 2003 if the + // other fields specify a date before 6/18, or 1903 if they specify a + // date afterwards. As a result, 03 is an ambiguous year. All other + // two-digit years are unambiguous. + if(fHaveDefaultCentury) { // check if this formatter even has a pivot year + int32_t ambiguousTwoDigitYear = fDefaultCenturyStartYear % 100; + ambiguousYear[0] = (value == ambiguousTwoDigitYear); + value += (fDefaultCenturyStartYear/100)*100 + + (value < ambiguousTwoDigitYear ? 100 : 0); + } } } cal.set(UCAL_YEAR, value); diff --git a/icu4c/source/test/cintltst/cdattst.c b/icu4c/source/test/cintltst/cdattst.c index 0d2ab2521d3..18cc874efda 100644 --- a/icu4c/source/test/cintltst/cdattst.c +++ b/icu4c/source/test/cintltst/cdattst.c @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2012, International Business Machines Corporation and + * Copyright (c) 1997-2013, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ /******************************************************************************** @@ -36,6 +36,7 @@ static void TestExtremeDates(void); static void TestAllLocales(void); static void TestRelativeCrash(void); static void TestContext(void); +static void TestCalendarDateParse(void); #define LEN(a) (sizeof(a)/sizeof(a[0])) @@ -53,6 +54,7 @@ void addDateForTest(TestNode** root) TESTCASE(TestAllLocales); TESTCASE(TestRelativeCrash); TESTCASE(TestContext); + TESTCASE(TestCalendarDateParse); } /* Testing the DateFormat API */ static void TestDateFormat() @@ -880,7 +882,101 @@ static void TestDateFormatCalendar() { ctest_resetTimeZone(); } + + +/** + * Test parsing two digit year against "YY" vs. "YYYY" patterns + */ +static void TestCalendarDateParse() { + + int32_t result; + UErrorCode ec = U_ZERO_ERROR; + UDateFormat* simpleDateFormat = 0; + int parsePos = 0; + int twoDigitCenturyStart = 75; + int currentTwoDigitYear = 0; + int startCentury = 0; + UCalendar* tempCal = 0; + UCalendar* calendar = 0; + + U_STRING_DECL(pattern, "yyyy", 4); + U_STRING_INIT(pattern, "yyyy", 4); + U_STRING_DECL(pattern2, "yy", 2); + U_STRING_INIT(pattern2, "yy", 2); + U_STRING_DECL(text, "75", 2); + U_STRING_INIT(text, "75", 2); + + simpleDateFormat = udat_open(UDAT_FULL, UDAT_FULL, "en-GB", 0, 0, 0, 0, &ec); + udat_applyPattern(simpleDateFormat, 0, pattern, u_strlen(pattern)); + udat_setLenient(simpleDateFormat, 0); + + currentTwoDigitYear = getCurrentYear() % 100; + startCentury = getCurrentYear() - currentTwoDigitYear; + if (twoDigitCenturyStart > currentTwoDigitYear) { + startCentury -= 100; + } + tempCal = ucal_open(NULL, -1, NULL, UCAL_GREGORIAN, &ec); + ucal_setMillis(tempCal, 0, &ec); + ucal_setDateTime(tempCal, startCentury + twoDigitCenturyStart, UCAL_JANUARY, 1, 0, 0, 0, &ec); + udat_set2DigitYearStart(simpleDateFormat, ucal_getMillis(tempCal, &ec), &ec); + + calendar = ucal_open(NULL, -1, NULL, UCAL_GREGORIAN, &ec); + ucal_setMillis(calendar, 0, &ec); + ucal_setDateTime(calendar, twoDigitCenturyStart, UCAL_JANUARY, 1, 0, 0, 0, &ec); + + udat_parseCalendar(simpleDateFormat, calendar, text, u_strlen(text), &parsePos, &ec); + + /* Check result */ + result = ucal_get(calendar, UCAL_YEAR, &ec); + if (U_FAILURE(ec)) { + log_err("FAIL: ucal_get(UCAL_YEAR) failed with %s\n", u_errorName(ec)); + goto FAIL; + } + + if (result != 75) { + log_err("FAIL: parsed incorrect year: %d\n", result); + goto FAIL; + } + + parsePos = 0; + udat_applyPattern(simpleDateFormat, 0, pattern2, u_strlen(pattern2)); + udat_parseCalendar(simpleDateFormat, calendar, text, u_strlen(text), &parsePos, &ec); + + /* Check result */ + result = ucal_get(calendar, UCAL_YEAR, &ec); + if (U_FAILURE(ec)) { + log_err("FAIL: ucal_get(UCAL_YEAR) failed with %s\n", u_errorName(ec)); + goto FAIL; + } + + if (result != 1975) { + log_err("FAIL: parsed incorrect year: %d\n", result); + goto FAIL; + } + + FAIL: + udat_close(simpleDateFormat); + udat_close(tempCal); + udat_close(calendar); +} + + /*INTERNAL FUNCTIONS USED*/ +static int getCurrentYear() { + static int currentYear = 0; + if (currentYear == 0) { + UErrorCode status = U_ZERO_ERROR; + UCalendar *cal = ucal_open(NULL, -1, NULL, UCAL_GREGORIAN, &status); + if (!U_FAILURE(status)) { + /* Get the current year from the default UCalendar */ + currentYear = ucal_get(cal, UCAL_YEAR, &status); + ucal_close(cal); + } + } + + return currentYear; +} + /* N.B.: use idx instead of index to avoid 'shadow' warnings in strict mode. */ static void VerifygetSymbols(UDateFormat* datfor, UDateFormatSymbolType type, int32_t idx, const char* expected) { diff --git a/icu4c/source/test/cintltst/cdattst.h b/icu4c/source/test/cintltst/cdattst.h index 7159f88c55c..7fc03251aa8 100644 --- a/icu4c/source/test/cintltst/cdattst.h +++ b/icu4c/source/test/cintltst/cdattst.h @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2009, International Business Machines Corporation and + * Copyright (c) 1997-2013, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ /******************************************************************************** @@ -49,6 +49,7 @@ * test subroutine used by the testing functions **/ static UChar* myNumformat(const UNumberFormat* numfor, double d); + static int getCurrentYear(void); #endif /* #if !UCONFIG_NO_FORMATTING */