From e2e48c9dce85e1a460c32df5e6b8bd84c6fcf0fb Mon Sep 17 00:00:00 2001 From: Peter Edberg Date: Wed, 24 May 2017 06:57:45 +0000 Subject: [PATCH] ICU-13183 for compatibility, get(Base)Skeleton should not include 'a' added by DateTimeMatcher; add tests X-SVN-Rev: 40133 --- icu4c/source/i18n/dtptngen.cpp | 23 +++++- icu4c/source/i18n/dtptngen_impl.h | 1 + icu4c/source/test/intltest/dtptngts.cpp | 70 +++++++++---------- .../icu/text/DateTimePatternGenerator.java | 37 ++++++++-- .../test/format/DateTimeGeneratorTest.java | 6 +- 5 files changed, 90 insertions(+), 47 deletions(-) diff --git a/icu4c/source/i18n/dtptngen.cpp b/icu4c/source/i18n/dtptngen.cpp index c91fbbe6f23..187342e4af2 100644 --- a/icu4c/source/i18n/dtptngen.cpp +++ b/icu4c/source/i18n/dtptngen.cpp @@ -1917,6 +1917,10 @@ DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton for (i=0; iset(pattern); for (i=0; i < fp->itemNumber; i++) { const UnicodeString& value = fp->items[i]; @@ -1955,6 +1959,7 @@ DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton skeletonResult.original.populate(UDATPG_DAYPERIOD_FIELD, dtTypes[i].patternChar, dtTypes[i].minLen); skeletonResult.baseOriginal.populate(UDATPG_DAYPERIOD_FIELD, dtTypes[i].patternChar, dtTypes[i].minLen); skeletonResult.type[UDATPG_DAYPERIOD_FIELD] = dtTypes[i].type; + skeletonResult.addedDefaultDayPeriod = TRUE; break; } } @@ -2387,13 +2392,27 @@ PtnSkeleton::equals(const PtnSkeleton& other) const { UnicodeString PtnSkeleton::getSkeleton() const { UnicodeString result; - return original.appendTo(result); + result = original.appendTo(result); + int32_t pos; + if (addedDefaultDayPeriod && (pos = result.indexOf(LOW_A)) >= 0) { + // for backward compatibility: if DateTimeMatcher.set added a single 'a' that + // was not in the provided skeleton, remove it here before returning skeleton. + result.remove(pos, 1); + } + return result; } UnicodeString PtnSkeleton::getBaseSkeleton() const { UnicodeString result; - return baseOriginal.appendTo(result); + result = baseOriginal.appendTo(result); + int32_t pos; + if (addedDefaultDayPeriod && (pos = result.indexOf(LOW_A)) >= 0) { + // for backward compatibility: if DateTimeMatcher.set added a single 'a' that + // was not in the provided skeleton, remove it here before returning skeleton. + result.remove(pos, 1); + } + return result; } UChar diff --git a/icu4c/source/i18n/dtptngen_impl.h b/icu4c/source/i18n/dtptngen_impl.h index 4fbadbb9331..2ea31a75c48 100644 --- a/icu4c/source/i18n/dtptngen_impl.h +++ b/icu4c/source/i18n/dtptngen_impl.h @@ -156,6 +156,7 @@ public: int32_t type[UDATPG_FIELD_COUNT]; SkeletonFields original; SkeletonFields baseOriginal; + UBool addedDefaultDayPeriod; PtnSkeleton(); PtnSkeleton(const PtnSkeleton& other); diff --git a/icu4c/source/test/intltest/dtptngts.cpp b/icu4c/source/test/intltest/dtptngts.cpp index 714ee2e19b8..3e21df8aec8 100644 --- a/icu4c/source/test/intltest/dtptngts.cpp +++ b/icu4c/source/test/intltest/dtptngts.cpp @@ -356,6 +356,16 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/) UnicodeString("MMMMMd"), }; + const char* testGetSkeletonAndBase[][3] = { + // pattern skeleton baseSkeleton + { "dd-MMM", "MMMdd", "MMMd" }, + { "dd/MMMM/yy", "yyMMMMdd", "yMMMMd" }, + { "h", "h", "h" }, + { "ah", "ah", "ah" }, + { "aaaah", "aaaah", "aaaah" }, + { "Bh", "Bh", "Bh" } + }; + UnicodeString newDecimal(" "); // space UnicodeString newAppendItemName("hrs."); UnicodeString newAppendItemFormat("{1} {0}"); @@ -511,34 +521,25 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/) } // ======== Test getSkeleton and getBaseSkeleton - status = U_ZERO_ERROR; - pattern = UnicodeString("dd-MMM"); - UnicodeString expectedSkeleton = UnicodeString("MMMdd"); - UnicodeString expectedBaseSkeleton = UnicodeString("MMMd"); - UnicodeString retSkeleton = gen->getSkeleton(pattern, status); - if(U_FAILURE(status) || retSkeleton != expectedSkeleton ) { - errln("ERROR: Unexpected result from getSkeleton().\n"); - errln(UnicodeString(" Got: ") + retSkeleton + UnicodeString(" Expected: ") + expectedSkeleton ); - } - retSkeleton = gen->getBaseSkeleton(pattern, status); - if(U_FAILURE(status) || retSkeleton != expectedBaseSkeleton) { - errln("ERROR: Unexpected result from getBaseSkeleton().\n"); - errln(UnicodeString(" Got: ") + retSkeleton + UnicodeString(" Expected:")+ expectedBaseSkeleton); + + int32_t i, count = UPRV_LENGTHOF(testGetSkeletonAndBase); + for (i = 0; i < count; i++) { + status = U_ZERO_ERROR; + pattern = UnicodeString(testGetSkeletonAndBase[i][0]); + UnicodeString expectedSkeleton = UnicodeString(testGetSkeletonAndBase[i][1]); + UnicodeString expectedBaseSkeleton = UnicodeString(testGetSkeletonAndBase[i][2]); + UnicodeString retSkeleton = gen->getSkeleton(pattern, status); + if(U_FAILURE(status) || retSkeleton != expectedSkeleton ) { + errln("ERROR: Unexpected result from getSkeleton().\n"); + errln(UnicodeString(" Got: ") + retSkeleton + UnicodeString(" Expected: ") + expectedSkeleton ); + } + retSkeleton = gen->getBaseSkeleton(pattern, status); + if(U_FAILURE(status) || retSkeleton != expectedBaseSkeleton) { + errln("ERROR: Unexpected result from getBaseSkeleton().\n"); + errln(UnicodeString(" Got: ") + retSkeleton + UnicodeString(" Expected:")+ expectedBaseSkeleton); + } } - pattern = UnicodeString("dd/MMMM/yy"); - expectedSkeleton = UnicodeString("yyMMMMdd"); - expectedBaseSkeleton = UnicodeString("yMMMMd"); - retSkeleton = gen->getSkeleton(pattern, status); - if(U_FAILURE(status) || retSkeleton != expectedSkeleton ) { - errln("ERROR: Unexpected result from getSkeleton().\n"); - errln(UnicodeString(" Got: ") + retSkeleton + UnicodeString(" Expected: ") + expectedSkeleton ); - } - retSkeleton = gen->getBaseSkeleton(pattern, status); - if(U_FAILURE(status) || retSkeleton != expectedBaseSkeleton) { - errln("ERROR: Unexpected result from getBaseSkeleton().\n"); - errln(UnicodeString(" Got: ") + retSkeleton + UnicodeString(" Expected:")+ expectedBaseSkeleton); - } delete format; delete zone; delete gen; @@ -711,7 +712,6 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/) return; } UChar newChar; - int32_t i; for (i=0; i<10; ++i) { UnicodeString randomSkeleton; int32_t len = rand() % 20; @@ -771,7 +771,7 @@ void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/) } UnicodeString returnPattern, *ptrSkeleton; ptrSkeletonEnum->reset(status); - int32_t count=ptrSkeletonEnum->count(status); + count=ptrSkeletonEnum->count(status); for (i=0; isnext(status); returnPattern = test->getPatternForSkeleton(*ptrSkeleton); @@ -1137,14 +1137,14 @@ enum { kCharBufMax = 31 }; void IntlTestDateTimePatternGeneratorAPI::testSkeletonsWithDayPeriods() { const char * patterns[] = { // since icu4c getEmptyInstance does not call addCanonicalItems (unlike J), set these here: - "a", // should get skeleton a - "H", // should get skeleton H - "m", // should get skeleton m - "s", // should get skeleton s + "a", // should get internal skeleton a + "H", // should get internalskeleton H + "m", // should get internalskeleton m + "s", // should get internalskeleton s // patterns from which to construct sample data for a locale - //"H", // should get skeleton H - "h a", // should get skeleton ah - "B h", // should get skeleton Bh + //"H", // should get internalskeleton H + "h a", // should get internalskeleton ah + "B h", // should get internalskeleton Bh }; const char* testItems[][2] = { // sample requested skeletons and results diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java index 21bf7615eb0..1b87e5fbf32 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DateTimePatternGenerator.java @@ -2353,19 +2353,30 @@ public class DateTimePatternGenerator implements Freezable DateTimeMatcher.set + // added a single 'a' that was not in the provided skeleton, and it will be + // removed when generating the skeleton to return. + return original.toString(addedDefaultDayPeriod); } // returns a string like toString but using the canonical character for most types, // e.g. M for M or L, E for E or c, y for y or U, etc. The hour field is canonicalized // to 'H' (for 24-hour types) or 'h' (for 12-hour types) public String toCanonicalString() { - return original.toCanonicalString(); + // for backward compatibility: addedDefaultDayPeriod true => DateTimeMatcher.set + // added a single 'a' that was not in the provided skeleton, and it will be + // removed when generating the skeleton to return. + return original.toCanonicalString(addedDefaultDayPeriod); } String getBasePattern() { - return baseOriginal.toString(); + // for backward compatibility: addedDefaultDayPeriod true => DateTimeMatcher.set + // added a single 'a' that was not in the provided skeleton, and it will be + // removed when generating the skeleton to return. + return baseOriginal.toString(addedDefaultDayPeriod); } DateTimeMatcher set(String pattern, FormatParser fp, boolean allowDuplicateFields) { @@ -2450,6 +2471,7 @@ public class DateTimePatternGenerator implements Freezable