diff --git a/icu4c/source/i18n/zonemeta.cpp b/icu4c/source/i18n/zonemeta.cpp index b80ac3ea379..84a96578029 100644 --- a/icu4c/source/i18n/zonemeta.cpp +++ b/icu4c/source/i18n/zonemeta.cpp @@ -28,6 +28,7 @@ #include "uresimp.h" #include "uhash.h" #include "olsontz.h" +#include "uinvchar.h" static UMutex gZoneMetaLock = U_MUTEX_INITIALIZER; @@ -255,6 +256,12 @@ ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) { tzid.extract(utzid, ZID_KEY_MAX + 1, tmpStatus); U_ASSERT(tmpStatus == U_ZERO_ERROR); // we checked the length of tzid already + if (!uprv_isInvariantUString(utzid, -1)) { + // All of known tz IDs are only containing ASCII invariant characters. + status = U_ILLEGAL_ARGUMENT_ERROR; + return NULL; + } + // Check if it was already cached umtx_lock(&gZoneMetaLock); { diff --git a/icu4c/source/i18n/zonemeta.h b/icu4c/source/i18n/zonemeta.h index e0f22b779ed..9dbcc878a22 100644 --- a/icu4c/source/i18n/zonemeta.h +++ b/icu4c/source/i18n/zonemeta.h @@ -41,7 +41,11 @@ public: /** * Return the canonical id for this tzid defined by CLDR, which might be the id itself. * This overload method returns a persistent const UChar*, which is guranteed to persist - * (a pointer to a resource). + * (a pointer to a resource). If the given system tzid is not known, U_ILLEGAL_ARGUMENT_ERROR + * is set in the status. + * @param tzid Zone ID + * @param status Receives the status + * @return The canonical ID for the input time zone ID */ static const UChar* U_EXPORT2 getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status); diff --git a/icu4c/source/test/intltest/tzfmttst.cpp b/icu4c/source/test/intltest/tzfmttst.cpp index b0d9dd5212d..e673d0aa4ae 100644 --- a/icu4c/source/test/intltest/tzfmttst.cpp +++ b/icu4c/source/test/intltest/tzfmttst.cpp @@ -82,6 +82,7 @@ TimeZoneFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &name TESTCASE(3, TestISOFormat); TESTCASE(4, TestFormat); TESTCASE(5, TestFormatTZDBNames); + TESTCASE(6, TestFormatCustomZone); default: name = ""; break; } } @@ -1213,5 +1214,38 @@ TimeZoneFormatTest::TestFormatTZDBNames(void) { } } +void +TimeZoneFormatTest::TestFormatCustomZone(void) { + struct { + const char* id; + int32_t offset; + const char* expected; + } TESTDATA[] = { + { "abc", 3600000, "GMT+01:00" }, // unknown ID + { "$abc", -3600000, "GMT-01:00" }, // unknown, with ASCII variant char '$' + { "\\u00c1\\u00df\\u00c7", 5400000, "GMT+01:30"}, // unknown, with non-ASCII chars + { 0, 0, 0 } + }; + + UDate now = Calendar::getNow(); + + for (int32_t i = 0; ; i++) { + const char *id = TESTDATA[i].id; + if (id == 0) { + break; + } + UnicodeString tzid = UnicodeString(id, -1, US_INV).unescape(); + SimpleTimeZone tz(TESTDATA[i].offset, tzid); + + UErrorCode status = U_ZERO_ERROR; + LocalPointer tzfmt(TimeZoneFormat::createInstance(Locale("en"), status)); + UnicodeString tzstr; + UnicodeString expected = UnicodeString(TESTDATA[i].expected, -1, US_INV).unescape(); + + tzfmt->format(UTZFMT_STYLE_SPECIFIC_LONG, tz, now, tzstr, NULL); + assertEquals(UnicodeString("Format result for ") + tzid, expected, tzstr); + } +} + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/tzfmttst.h b/icu4c/source/test/intltest/tzfmttst.h index 9e70f117da2..0bf91ae0e8e 100644 --- a/icu4c/source/test/intltest/tzfmttst.h +++ b/icu4c/source/test/intltest/tzfmttst.h @@ -27,6 +27,7 @@ class TimeZoneFormatTest : public IntlTest { void TestISOFormat(void); void TestFormat(void); void TestFormatTZDBNames(void); + void TestFormatCustomZone(void); void RunTimeRoundTripTests(int32_t threadNumber); };