From 52415073127492d658571a2a8da88a11458462f1 Mon Sep 17 00:00:00 2001 From: Peter Edberg Date: Thu, 6 Mar 2014 09:47:25 +0000 Subject: [PATCH] ICU-10637 Format/parse using 'r', C part 2 (add tests, clean up islamic delta calculation) X-SVN-Rev: 35355 --- icu4c/source/i18n/calendar.cpp | 87 +++++++++++++++---------- icu4c/source/test/intltest/dtfmrgts.cpp | 40 ++++++------ icu4c/source/test/intltest/dtfmttst.cpp | 73 +++++++++++++++++---- 3 files changed, 132 insertions(+), 68 deletions(-) diff --git a/icu4c/source/i18n/calendar.cpp b/icu4c/source/i18n/calendar.cpp index 0a240e504b4..3931570e14b 100644 --- a/icu4c/source/i18n/calendar.cpp +++ b/icu4c/source/i18n/calendar.cpp @@ -106,7 +106,7 @@ U_CDECL_END */ const char* fldName(UCalendarDateFields f) { - return udbg_enumName(UDBG_UCalendarDateFields, (int32_t)f); + return udbg_enumName(UDBG_UCalendarDateFields, (int32_t)f); } #if UCAL_DEBUG_DUMP @@ -740,7 +740,7 @@ fSkippedWallTime(UCAL_WALLTIME_LAST) clear(); fZone = zone.clone(); if (fZone == NULL) { - success = U_MEMORY_ALLOCATION_ERROR; + success = U_MEMORY_ALLOCATION_ERROR; } setWeekData(aLocale, NULL, success); } @@ -1168,18 +1168,35 @@ Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t m // per #10752 move the non-default implementation to subclasses // (default implementation will do no year adjustment) +static int32_t gregoYearFromIslamicStart(int32_t year) { + // ad hoc conversion, improve under #10752 + // rough est for now, ok for grego 1846-2138, + // otherwise occasionally wrong (for 3% of years) + int cycle, offset, shift = 0; + if (year >= 1397) { + cycle = (year - 1397) / 67; + offset = (year - 1397) % 67; + shift = 2*cycle + ((offset >= 33)? 1: 0); + } else { + cycle = (year - 1396) / 67 - 1; + offset = -(year - 1396) % 67; + shift = 2*cycle + ((offset <= 33)? 1: 0); + } + return year + 579 - shift; +} + int32_t Calendar::getRelatedYear(UErrorCode &status) const { if (U_FAILURE(status)) { return 0; } - int32_t year = get(UCAL_EXTENDED_YEAR, status); + int32_t year = get(UCAL_EXTENDED_YEAR, status); if (U_FAILURE(status)) { return 0; } // modify for calendar type - ECalType type = getCalendarType(getType()); - switch (type) { + ECalType type = getCalendarType(getType()); + switch (type) { case CALTYPE_PERSIAN: year += 622; break; case CALTYPE_HEBREW: @@ -1201,21 +1218,7 @@ int32_t Calendar::getRelatedYear(UErrorCode &status) const case CALTYPE_ISLAMIC_UMALQURA: case CALTYPE_ISLAMIC_TBLA: case CALTYPE_ISLAMIC_RGSA: - // ad hoc conversion, improve under #10752 - { - int cycle, offset, shift = 0; - if (year >= 1397) { - cycle = (year - 1397) / 67; - offset = (year - 1397) % 67; - shift = 2*cycle + ((offset >= 33)? 1: 0); - } else { - cycle = (year - 1396) / 67 - 1; - offset = -(year - 1396) % 67; - shift = 2*cycle + ((offset <= 33)? 1: 0); - } - year += 579 - shift; - } - break; + year = gregoYearFromIslamicStart(year); break; default: // CALTYPE_GREGORIAN // CALTYPE_JAPANESE @@ -1224,8 +1227,8 @@ int32_t Calendar::getRelatedYear(UErrorCode &status) const // CALTYPE_ISO8601 // do nothing, EXTENDED_YEAR same as Gregorian break; - } - return year; + } + return year; } // ------------------------------------- @@ -1233,11 +1236,27 @@ int32_t Calendar::getRelatedYear(UErrorCode &status) const // per #10752 move the non-default implementation to subclasses // (default implementation will do no year adjustment) +static int32_t firstIslamicStartYearFromGrego(int32_t year) { + // ad hoc conversion, improve under #10752 + // rough est for now, ok for grego 1846-2138, + // otherwise occasionally wrong (for 3% of years) + int cycle, offset, shift = 0; + if (year >= 1977) { + cycle = (year - 1977) / 65; + offset = (year - 1977) % 65; + shift = 2*cycle + ((offset >= 32)? 1: 0); + } else { + cycle = (year - 1976) / 65 - 1; + offset = -(year - 1976) % 65; + shift = 2*cycle + ((offset <= 32)? 1: 0); + } + return year - 579 + shift; +} void Calendar::setRelatedYear(int32_t year) { // modify for calendar type - ECalType type = getCalendarType(getType()); - switch (type) { + ECalType type = getCalendarType(getType()); + switch (type) { case CALTYPE_PERSIAN: year -= 622; break; case CALTYPE_HEBREW: @@ -1259,9 +1278,7 @@ void Calendar::setRelatedYear(int32_t year) case CALTYPE_ISLAMIC_UMALQURA: case CALTYPE_ISLAMIC_TBLA: case CALTYPE_ISLAMIC_RGSA: - // needs adjustment, will do under #10752 - year -= 578; // handles current year +/- a few - break; + year = firstIslamicStartYearFromGrego(year); break; default: // CALTYPE_GREGORIAN // CALTYPE_JAPANESE @@ -1270,9 +1287,9 @@ void Calendar::setRelatedYear(int32_t year) // CALTYPE_ISO8601 // do nothing, EXTENDED_YEAR same as Gregorian break; - } - // set extended year - set(UCAL_EXTENDED_YEAR, year); + } + // set extended year + set(UCAL_EXTENDED_YEAR, year); } // ------------------------------------- @@ -2438,11 +2455,11 @@ Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) co status = U_ILLEGAL_ARGUMENT_ERROR; return UCAL_WEEKDAY; } - if (fWeekendOnset == fWeekendCease) { - if (dayOfWeek != fWeekendOnset) - return UCAL_WEEKDAY; - return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET; - } + if (fWeekendOnset == fWeekendCease) { + if (dayOfWeek != fWeekendOnset) + return UCAL_WEEKDAY; + return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET; + } if (fWeekendOnset < fWeekendCease) { if (dayOfWeek < fWeekendOnset || dayOfWeek > fWeekendCease) { return UCAL_WEEKDAY; diff --git a/icu4c/source/test/intltest/dtfmrgts.cpp b/icu4c/source/test/intltest/dtfmrgts.cpp index 2f482e2000d..992fd3d8ab8 100644 --- a/icu4c/source/test/intltest/dtfmrgts.cpp +++ b/icu4c/source/test/intltest/dtfmrgts.cpp @@ -1630,26 +1630,28 @@ void DateFormatRegressionTest::TestT10619(void) { const TestDateFormatLeniencyItem * itemPtr; for (itemPtr = items; itemPtr->locale != NULL; itemPtr++ ) { - Locale locale = Locale::createFromName(itemPtr->locale); - status = U_ZERO_ERROR; - ParsePosition pos(0); - SimpleDateFormat * sdmft = new SimpleDateFormat(itemPtr->pattern, locale, status); - if (U_FAILURE(status)) { - dataerrln("Unable to create SimpleDateFormat - %s", u_errorName(status)); - continue; - } - sdmft->setLenient(itemPtr->leniency); - sdmft->setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, itemPtr->leniency, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, itemPtr->leniency, status); - /*UDate d = */sdmft->parse(itemPtr->parseString, pos); + Locale locale = Locale::createFromName(itemPtr->locale); + status = U_ZERO_ERROR; + ParsePosition pos(0); + SimpleDateFormat * sdmft = new SimpleDateFormat(itemPtr->pattern, locale, status); + if (U_FAILURE(status)) { + dataerrln("Unable to create SimpleDateFormat - %s", u_errorName(status)); + continue; + } + sdmft->setLenient(itemPtr->leniency); + sdmft->setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, itemPtr->leniency, status).setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, itemPtr->leniency, status); + /*UDate d = */sdmft->parse(itemPtr->parseString, pos); - delete sdmft; - if(pos.getErrorIndex() > -1) - if(itemPtr->expectedResult.length() != 0) { - errln("error: unexpected error - " + itemPtr->parseString + " - error index " + pos.getErrorIndex() + " - leniency " + itemPtr->leniency); - continue; - } else { - continue; - } + delete sdmft; + if(pos.getErrorIndex() > -1) { + if(itemPtr->expectedResult.length() != 0) { + errln("error: unexpected error - " + itemPtr->parseString + " - error index " + pos.getErrorIndex() + + " - leniency " + itemPtr->leniency); + continue; + } else { + continue; + } + } } } delete cal; diff --git a/icu4c/source/test/intltest/dtfmttst.cpp b/icu4c/source/test/intltest/dtfmttst.cpp index c38e8a766cb..b04087b9797 100644 --- a/icu4c/source/test/intltest/dtfmttst.cpp +++ b/icu4c/source/test/intltest/dtfmttst.cpp @@ -4052,6 +4052,7 @@ void DateFormatTest::TestContext() // test item for a particular locale + calendar and date format typedef struct { + int32_t era; int32_t year; int32_t month; int32_t day; @@ -4064,6 +4065,7 @@ typedef struct { typedef struct { const char * locale; // with calendar DateFormat::EStyle style; + UnicodeString pattern; // ignored unless style == DateFormat::kNone const CalAndFmtTestItem *caftItems; } TestNonGregoItem; @@ -4071,23 +4073,62 @@ void DateFormatTest::TestNonGregoFmtParse() { // test items for he@calendar=hebrew, long date format const CalAndFmtTestItem cafti_he_hebrew_long[] = { - { 4999, 12, 29, 12, 0, CharsToUnicodeString("\\u05DB\\u05F4\\u05D8 \\u05D1\\u05D0\\u05DC\\u05D5\\u05DC \\u05D3\\u05F3\\u05EA\\u05EA\\u05E7\\u05E6\\u05F4\\u05D8") }, - { 5100, 0, 1, 12, 0, CharsToUnicodeString("\\u05D0\\u05F3 \\u05D1\\u05EA\\u05E9\\u05E8\\u05D9 \\u05E7\\u05F3") }, - { 5774, 5, 1, 12, 0, CharsToUnicodeString("\\u05D0\\u05F3 \\u05D1\\u05D0\\u05D3\\u05E8 \\u05D0\\u05F3 \\u05EA\\u05E9\\u05E2\\u05F4\\u05D3") }, - { 5999, 12, 29, 12, 0, CharsToUnicodeString("\\u05DB\\u05F4\\u05D8 \\u05D1\\u05D0\\u05DC\\u05D5\\u05DC \\u05EA\\u05EA\\u05E7\\u05E6\\u05F4\\u05D8") }, - { 6100, 0, 1, 12, 0, CharsToUnicodeString("\\u05D0\\u05F3 \\u05D1\\u05EA\\u05E9\\u05E8\\u05D9 \\u05D5\\u05F3\\u05E7\\u05F3") }, - { 0, 0, 0, 0, 0, UnicodeString("") } // terminator + { 0, 4999, 12, 29, 12, 0, CharsToUnicodeString("\\u05DB\\u05F4\\u05D8 \\u05D1\\u05D0\\u05DC\\u05D5\\u05DC \\u05D3\\u05F3\\u05EA\\u05EA\\u05E7\\u05E6\\u05F4\\u05D8") }, + { 0, 5100, 0, 1, 12, 0, CharsToUnicodeString("\\u05D0\\u05F3 \\u05D1\\u05EA\\u05E9\\u05E8\\u05D9 \\u05E7\\u05F3") }, + { 0, 5774, 5, 1, 12, 0, CharsToUnicodeString("\\u05D0\\u05F3 \\u05D1\\u05D0\\u05D3\\u05E8 \\u05D0\\u05F3 \\u05EA\\u05E9\\u05E2\\u05F4\\u05D3") }, + { 0, 5999, 12, 29, 12, 0, CharsToUnicodeString("\\u05DB\\u05F4\\u05D8 \\u05D1\\u05D0\\u05DC\\u05D5\\u05DC \\u05EA\\u05EA\\u05E7\\u05E6\\u05F4\\u05D8") }, + { 0, 6100, 0, 1, 12, 0, CharsToUnicodeString("\\u05D0\\u05F3 \\u05D1\\u05EA\\u05E9\\u05E8\\u05D9 \\u05D5\\u05F3\\u05E7\\u05F3") }, + { 0, 0, 0, 0, 0, 0, UnicodeString("") } // terminator + }; + const CalAndFmtTestItem cafti_zh_chinese_custU[] = { + { 78, 31, 0, 1, 12, 0, CharsToUnicodeString("2014\\u7532\\u5348\\u5E74\\u6B63\\u67081") }, + { 77, 31, 0, 1, 12, 0, CharsToUnicodeString("1954\\u7532\\u5348\\u5E74\\u6B63\\u67081") }, + { 0, 0, 0, 0, 0, 0, UnicodeString("") } // terminator + }; + const CalAndFmtTestItem cafti_zh_chinese_custNoU[] = { + { 78, 31, 0, 1, 12, 0, CharsToUnicodeString("2014\\u5E74\\u6B63\\u67081") }, + { 77, 31, 0, 1, 12, 0, CharsToUnicodeString("1954\\u5E74\\u6B63\\u67081") }, + { 0, 0, 0, 0, 0, 0, UnicodeString("") } // terminator + }; + const CalAndFmtTestItem cafti_ja_japanese_custGy[] = { + {235, 26, 2, 5, 12, 0, CharsToUnicodeString("2014(\\u5E73\\u621026)\\u5E743\\u67085\\u65E5") }, + {234, 60, 2, 5, 12, 0, CharsToUnicodeString("1985(\\u662D\\u548C60)\\u5E743\\u67085\\u65E5") }, + { 0, 0, 0, 0, 0, 0, UnicodeString("") } // terminator + }; + const CalAndFmtTestItem cafti_ja_japanese_custNoGy[] = { + {235, 26, 2, 5, 12, 0, CharsToUnicodeString("2014\\u5E743\\u67085\\u65E5") }, + {234, 60, 2, 5, 12, 0, CharsToUnicodeString("1985\\u5E743\\u67085\\u65E5") }, + { 0, 0, 0, 0, 0, 0, UnicodeString("") } // terminator + }; + const CalAndFmtTestItem cafti_en_islamic_cust[] = { + { 0, 1384, 0, 1, 12, 0, UnicodeString("1 Muh. 1384 AH, 1964") }, + { 0, 1436, 0, 1, 12, 0, UnicodeString("1 Muh. 1436 AH, 2014") }, + { 0, 1487, 0, 1, 12, 0, UnicodeString("1 Muh. 1487 AH, 2064") }, + { 0, 0, 0, 0, 0, 0, UnicodeString("") } // terminator }; // overal test items const TestNonGregoItem items[] = { - { "he@calendar=hebrew", DateFormat::kLong, cafti_he_hebrew_long }, - { NULL, DateFormat::kNone, NULL } // terminator + { "he@calendar=hebrew", DateFormat::kLong, UnicodeString(""), cafti_he_hebrew_long }, + { "zh@calendar=chinese", DateFormat::kNone, CharsToUnicodeString("rU\\u5E74MMMd"), cafti_zh_chinese_custU }, + { "zh@calendar=chinese", DateFormat::kNone, CharsToUnicodeString("r\\u5E74MMMd"), cafti_zh_chinese_custNoU }, + { "ja@calendar=japanese", DateFormat::kNone, CharsToUnicodeString("r(Gy)\\u5E74M\\u6708d\\u65E5"), cafti_ja_japanese_custGy }, + { "ja@calendar=japanese", DateFormat::kNone, CharsToUnicodeString("r\\u5E74M\\u6708d\\u65E5"), cafti_ja_japanese_custNoGy }, + { "en@calendar=islamic", DateFormat::kNone, UnicodeString("d MMM y G, r"), cafti_en_islamic_cust }, + { NULL, DateFormat::kNone, UnicodeString(""), NULL } // terminator }; const TestNonGregoItem * itemPtr; for (itemPtr = items; itemPtr->locale != NULL; itemPtr++) { Locale locale = Locale::createFromName(itemPtr->locale); - DateFormat * dfmt = DateFormat::createDateInstance(itemPtr->style, locale); - if (dfmt == NULL) { + DateFormat * dfmt = NULL; + UErrorCode status = U_ZERO_ERROR; + if (itemPtr->style != DateFormat::kNone) { + dfmt = DateFormat::createDateInstance(itemPtr->style, locale); + } else { + dfmt = new SimpleDateFormat(itemPtr->pattern, locale, status); + } + if (U_FAILURE(status)) { + dataerrln("new SimpleDateFormat fails for locale %s", itemPtr->locale); + } else if (dfmt == NULL) { dataerrln("DateFormat::createDateInstance fails for locale %s", itemPtr->locale); } else { Calendar * cal = (dfmt->getCalendar())->clone(); @@ -4097,6 +4138,7 @@ void DateFormatTest::TestNonGregoFmtParse() const CalAndFmtTestItem * caftItemPtr; for (caftItemPtr = itemPtr->caftItems; caftItemPtr->year != 0; caftItemPtr++) { cal->clear(); + cal->set(UCAL_ERA, caftItemPtr->era); cal->set(UCAL_YEAR, caftItemPtr->year); cal->set(UCAL_MONTH, caftItemPtr->month); cal->set(UCAL_DATE, caftItemPtr->day); @@ -4112,13 +4154,16 @@ void DateFormatTest::TestNonGregoFmtParse() // formatted OK, try parse ParsePosition ppos(0); dfmt->parse(result, *cal, ppos); - UErrorCode status = U_ZERO_ERROR; + status = U_ZERO_ERROR; + int32_t era = cal->get(UCAL_ERA, status); int32_t year = cal->get(UCAL_YEAR, status); int32_t month = cal->get(UCAL_MONTH, status); int32_t day = cal->get(UCAL_DATE, status); - if ( U_FAILURE(status) || ppos.getIndex() < result.length() || year != caftItemPtr->year || month != caftItemPtr->month || day != caftItemPtr->day ) { - errln( UnicodeString("FAIL: date parse for locale ") + UnicodeString(itemPtr->locale) + ", style " + itemPtr->style + - ", string \"" + result + "\", expected " + caftItemPtr->year +"-"+caftItemPtr->month+"-"+caftItemPtr->day + ", got pos " + + if ( U_FAILURE(status) || ppos.getIndex() < result.length() || era != caftItemPtr->era || + year != caftItemPtr->year || month != caftItemPtr->month || day != caftItemPtr->day ) { + errln( UnicodeString("FAIL: date parse for locale ") + UnicodeString(itemPtr->locale) + + ", style " + itemPtr->style + ", string \"" + result + "\", expected " + + caftItemPtr->era +":"+caftItemPtr->year +"-"+caftItemPtr->month+"-"+caftItemPtr->day + ", got pos " + ppos.getIndex() + " " + year +"-"+month+"-"+day + " status " + UnicodeString(u_errorName(status)) ); } }