diff --git a/icu4c/source/i18n/udat.cpp b/icu4c/source/i18n/udat.cpp index ab91bcff412..897ac26c01d 100644 --- a/icu4c/source/i18n/udat.cpp +++ b/icu4c/source/i18n/udat.cpp @@ -82,13 +82,18 @@ static UCalendarDateFields gDateFieldMapping[] = { UCAL_ZONE_OFFSET, // UDAT_TIMEZONE_ISO_FIELD = 32 (also UCAL_DST_OFFSET) UCAL_ZONE_OFFSET, // UDAT_TIMEZONE_ISO_LOCAL_FIELD = 33 (also UCAL_DST_OFFSET) UCAL_EXTENDED_YEAR, // UDAT_RELATED_YEAR_FIELD = 34 (not an exact match) - UCAL_FIELD_COUNT, // UDAT_FIELD_COUNT = 35 + UCAL_FIELD_COUNT, // UDAT_AM_PM_MIDNIGHT_NOON_FIELD=35 (no match) + UCAL_FIELD_COUNT, // UDAT_FLEXIBLE_DAY_PERIOD_FIELD=36 (no match) + UCAL_FIELD_COUNT, // UDAT_TIME_SEPARATOR_FIELD = 37 (no match) + // UDAT_FIELD_COUNT = 38 as of ICU 67 // UCAL_IS_LEAP_MONTH is not the target of a mapping }; U_CAPI UCalendarDateFields U_EXPORT2 udat_toCalendarDateField(UDateFormatField field) { - return gDateFieldMapping[field]; + static_assert(UDAT_FIELD_COUNT == UPRV_LENGTHOF(gDateFieldMapping), + "UDateFormatField and gDateFieldMapping should have the same number of entries and be kept in sync."); + return (field >= UDAT_ERA_FIELD && field < UPRV_LENGTHOF(gDateFieldMapping))? gDateFieldMapping[field]: UCAL_FIELD_COUNT; } /* For now- one opener. */ diff --git a/icu4c/source/i18n/unicode/udat.h b/icu4c/source/i18n/unicode/udat.h index cf7a165e70a..4610203ddca 100644 --- a/icu4c/source/i18n/unicode/udat.h +++ b/icu4c/source/i18n/unicode/udat.h @@ -832,10 +832,24 @@ typedef enum UDateFormatField { /** * Maps from a UDateFormatField to the corresponding UCalendarDateFields. - * Note: since the mapping is many-to-one, there is no inverse mapping. + * + * Note 1: Since the mapping is many-to-one, there is no inverse mapping. + * + * Note 2: There is no UErrorCode parameter, so in case of error (UDateFormatField is + * unknown or has no corresponding UCalendarDateFields value), the function returns the + * current value of UCAL_FIELD_COUNT. However, that value may change from release to + * release and is consequently deprecated. For a future-proof runtime way of checking + * for errors: + * a) First save the value returned by the function when it is passed an invalid value + * such as "(UDateFormatField)-1". + * b) Then, to test for errors when passing some other UDateFormatField value, check + * whether the function returns that saved value. + * * @param field the UDateFormatField. - * @return the UCalendarDateField. This will be UCAL_FIELD_COUNT in case - * of error (e.g., the input field is UDAT_FIELD_COUNT). + * @return the UCalendarDateField. In case of error (UDateFormatField is unknown or has + * no corresponding UCalendarDateFields value) this will be the current value of + * UCAL_FIELD_COUNT, but that value may change from release to release. + * See Note 2 above. * @stable ICU 4.4 */ U_CAPI UCalendarDateFields U_EXPORT2 diff --git a/icu4c/source/test/cintltst/cdattst.c b/icu4c/source/test/cintltst/cdattst.c index d5e3ba2ccd9..854eb3b6911 100644 --- a/icu4c/source/test/cintltst/cdattst.c +++ b/icu4c/source/test/cintltst/cdattst.c @@ -43,6 +43,7 @@ static void TestCalendarDateParse(void); static void TestParseErrorReturnValue(void); static void TestFormatForFields(void); static void TestForceGannenNumbering(void); +static void TestMapDateToCalFields(void); void addDateForTest(TestNode** root); @@ -63,6 +64,7 @@ void addDateForTest(TestNode** root) TESTCASE(TestParseErrorReturnValue); TESTCASE(TestFormatForFields); TESTCASE(TestForceGannenNumbering); + TESTCASE(TestMapDateToCalFields); } /* Testing the DateFormat API */ static void TestDateFormat() @@ -1905,4 +1907,35 @@ static void TestForceGannenNumbering(void) { udatpg_close(dtpgen); } } + +typedef struct { + UChar patternChar; // for future use + UDateFormatField dateField; + UCalendarDateFields calField; +} PatternCharToFieldsItem; + +static const PatternCharToFieldsItem patCharToFieldsItems[] = { + { u'G', UDAT_ERA_FIELD, UCAL_ERA }, + { u'y', UDAT_YEAR_FIELD, UCAL_YEAR }, + { u'Y', UDAT_YEAR_WOY_FIELD, UCAL_YEAR_WOY }, + { u'Q', UDAT_QUARTER_FIELD, UCAL_MONTH }, + { u'H', UDAT_HOUR_OF_DAY0_FIELD, UCAL_HOUR_OF_DAY }, + { u'r', UDAT_RELATED_YEAR_FIELD, UCAL_EXTENDED_YEAR }, + { u'B', UDAT_FLEXIBLE_DAY_PERIOD_FIELD, UCAL_FIELD_COUNT }, + { u'$', UDAT_FIELD_COUNT, UCAL_FIELD_COUNT }, + { 0xFFFF, (UDateFormatField)-1, UCAL_FIELD_COUNT }, // patternChar ignored here + { (UChar)0, (UDateFormatField)0, (UCalendarDateFields)0 } // terminator +}; + +static void TestMapDateToCalFields(void){ + const PatternCharToFieldsItem* itemPtr; + for ( itemPtr=patCharToFieldsItems; itemPtr->patternChar!=(UChar)0; itemPtr++) { + UCalendarDateFields calField = udat_toCalendarDateField(itemPtr->dateField); + if (calField != itemPtr->calField) { + log_err("for pattern char 0x%04X, dateField %d, expect calField %d and got %d\n", + itemPtr->patternChar, itemPtr->dateField, itemPtr->calField, calField); + } + } +} + #endif /* #if !UCONFIG_NO_FORMATTING */