From 7c93a6eeace9744b22f2efaf6b61e3abb549ad25 Mon Sep 17 00:00:00 2001 From: Yoshito Umaoka Date: Fri, 18 Feb 2011 23:03:47 +0000 Subject: [PATCH] ICU-8279 TimeZone API - getRegion in ICU4C X-SVN-Rev: 29459 --- icu4c/source/i18n/timezone.cpp | 39 ++++++++++++- icu4c/source/i18n/unicode/timezone.h | 24 +++++++- icu4c/source/i18n/zonemeta.cpp | 8 +-- icu4c/source/test/intltest/tztest.cpp | 79 ++++++++++++++++++++++++++- icu4c/source/test/intltest/tztest.h | 6 +- 5 files changed, 146 insertions(+), 10 deletions(-) diff --git a/icu4c/source/i18n/timezone.cpp b/icu4c/source/i18n/timezone.cpp index 777bee77ade..08f3d814016 100644 --- a/icu4c/source/i18n/timezone.cpp +++ b/icu4c/source/i18n/timezone.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 1997-2010, International Business Machines Corporation and * +* Copyright (C) 1997-2011, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* * @@ -39,6 +39,7 @@ #include "unicode/utypes.h" #include "unicode/ustring.h" +#include "ustr_imp.h" #ifdef U_DEBUG_TZ # include @@ -103,6 +104,7 @@ static char gStrBuf[256]; static const UChar WORLD[] = {0x30, 0x30, 0x31, 0x00}; /* "001" */ static const UChar GMT_ID[] = {0x47, 0x4D, 0x54, 0x00}; /* "GMT" */ +static const UChar UNKNOWN_ZONE_ID[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0x00}; /* "Etc/Unknown" */ static const UChar Z_STR[] = {0x7A, 0x00}; /* "z" */ static const UChar ZZZZ_STR[] = {0x7A, 0x7A, 0x7A, 0x7A, 0x00}; /* "zzzz" */ static const UChar Z_UC_STR[] = {0x5A, 0x00}; /* "Z" */ @@ -112,6 +114,7 @@ static const UChar VVVV_STR[] = {0x76, 0x76, 0x76, 0x76, 0x00}; /* "vvvv static const UChar V_UC_STR[] = {0x56, 0x00}; /* "V" */ static const UChar VVVV_UC_STR[] = {0x56, 0x56, 0x56, 0x56, 0x00}; /* "VVVV" */ static const int32_t GMT_ID_LENGTH = 3; +static const int32_t UNKNOWN_ZONE_ID_LENGTH = 11; static UMTX LOCK; static UMTX TZSET_LOCK; @@ -942,7 +945,7 @@ TimeZone::dereferOlsonLink(const UnicodeString& id) { const UChar* TimeZone::getRegion(const UnicodeString& id) { - const UChar *result = WORLD; + const UChar *result = NULL; UErrorCode ec = U_ZERO_ERROR; UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec); @@ -963,6 +966,38 @@ TimeZone::getRegion(const UnicodeString& id) { return result; } +// --------------------------------------- +int32_t +TimeZone::getRegion(const UnicodeString& id, char *region, int32_t capacity, UErrorCode& status) +{ + int32_t resultLen = 0; + *region = 0; + if (U_FAILURE(status)) { + return 0; + } + + const UChar *uregion = NULL; + // "Etc/Unknown" is not a system zone ID, + // but in the zone data + if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) != 0) { + uregion = getRegion(id); + } + if (uregion == NULL) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + resultLen = u_strlen(uregion); + // A region code is represented by invariant characters + u_UCharsToChars(uregion, region, uprv_min(resultLen, capacity)); + + if (capacity < resultLen) { + status = U_BUFFER_OVERFLOW_ERROR; + return resultLen; + } + + return u_terminateChars(region, capacity, resultLen, &status); +} + // --------------------------------------- diff --git a/icu4c/source/i18n/unicode/timezone.h b/icu4c/source/i18n/unicode/timezone.h index 6182f979d6a..1d129cce23a 100644 --- a/icu4c/source/i18n/unicode/timezone.h +++ b/icu4c/source/i18n/unicode/timezone.h @@ -640,6 +640,26 @@ public: */ virtual int32_t getDSTSavings() const; + /** + * Gets the region code associated with the given + * system time zone ID. The region code is either ISO 3166 + * 2-letter country code or UN M.49 3-digit area code. + * When the time zone is not associated with a specific location, + * for example - "Etc/UTC", "EST5EDT", then this method returns + * "001" (UN M.49 area code for World). + * + * @param id The system time zone ID. + * @param region Output buffer for receiving the region code. + * @param capacity The size of the output buffer. + * @param status Receives the status. When the given time zone ID + * is not a known system time zone ID, + * U_ILLEGAL_ARGUMENT_ERROR is set. + * @return The length of the output region code. + * @draft ICU 4.8 + */ + static int32_t U_EXPORT2 getRegion(const UnicodeString& id, + char *region, int32_t capacity, UErrorCode& status); + protected: /** @@ -680,6 +700,7 @@ protected: */ static UResourceBundle* loadRule(const UResourceBundle* top, const UnicodeString& ruleid, UResourceBundle* oldbundle, UErrorCode&status); + private: friend class ZoneMeta; @@ -697,7 +718,8 @@ private: static const UChar* dereferOlsonLink(const UnicodeString& id); /** - * Returns the region code associated with the given zone. + * Returns the region code associated with the given zone, + * or NULL if the zone is not known. * @param id zone id string * @return the region associated with the given zone */ diff --git a/icu4c/source/i18n/zonemeta.cpp b/icu4c/source/i18n/zonemeta.cpp index 65291e688ef..6a91b8e6eb0 100644 --- a/icu4c/source/i18n/zonemeta.cpp +++ b/icu4c/source/i18n/zonemeta.cpp @@ -1,6 +1,6 @@ /* ******************************************************************************* -* Copyright (C) 2007-2010, International Business Machines Corporation and * +* Copyright (C) 2007-2011, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ @@ -274,7 +274,7 @@ ZoneMeta::getCanonicalSystemID(const UnicodeString &tzid, UnicodeString &systemI UnicodeString& U_EXPORT2 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &canonicalCountry) { const UChar *region = TimeZone::getRegion(tzid); - if (u_strcmp(gWorld, region) != 0) { + if (region != NULL && u_strcmp(gWorld, region) != 0) { canonicalCountry.setTo(region, -1); } else { canonicalCountry.remove(); @@ -286,8 +286,8 @@ UnicodeString& U_EXPORT2 ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) { // Get canonical country for the zone const UChar *region = TimeZone::getRegion(tzid); - if (u_strcmp(gWorld, region) == 0) { - // special case - "001" + if (region == NULL || u_strcmp(gWorld, region) == 0) { + // special case - unknown or "001" country.remove(); return country; } diff --git a/icu4c/source/test/intltest/tztest.cpp b/icu4c/source/test/intltest/tztest.cpp index 7b6eed99411..beccb0bf286 100644 --- a/icu4c/source/test/intltest/tztest.cpp +++ b/icu4c/source/test/intltest/tztest.cpp @@ -1,6 +1,6 @@ /*********************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2010, International Business Machines Corporation + * Copyright (c) 1997-2011, International Business Machines Corporation * and others. All Rights Reserved. ***********************************************************************/ @@ -61,6 +61,7 @@ void TimeZoneTest::runIndexedTest( int32_t index, UBool exec, const char* &name, CASE(15, TestFebruary); CASE(16, TestCanonicalID); CASE(17, TestDisplayNamesMeta); + CASE(18, TestGetRegion); default: name = ""; break; } } @@ -1976,4 +1977,80 @@ void TimeZoneTest::TestDisplayNamesMeta() { } } +void TimeZoneTest::TestGetRegion() +{ + static const struct { + const char *id; + const char *region; + } data[] = { + {"America/Los_Angeles", "US"}, + {"America/Indianapolis", "US"}, // CLDR canonical, Olson backward + {"America/Indiana/Indianapolis", "US"}, // CLDR alias + {"Mexico/General", "MX"}, // Link America/Mexico_City, Olson backward + {"Etc/UTC", "001"}, + {"EST5EDT", "001"}, + {"PST", "US"}, // Link America/Los_Angeles + {"Europe/Helsinki", "FI"}, + {"Europe/Mariehamn", "AX"}, // Link Europe/Helsinki, but in zone.tab + {"Asia/Riyadh", "SA"}, + {"Asia/Riyadh87", "001"}, // this should be "SA" actually, but not in zone.tab + {"Etc/Unknown", 0}, // CLDR canonical, but not a sysmte zone ID + {"bogus", 0}, // bogus + {"GMT+08:00", 0}, // a custom ID, not a system zone ID + {0, 0} + }; + + int32_t i; + char region[4]; + UErrorCode sts; + for (i = 0; data[i].id; i++) { + sts = U_ZERO_ERROR; + TimeZone::getRegion(data[i].id, region, sizeof(region), sts); + if (U_SUCCESS(sts)) { + if (data[i].region == 0) { + errln((UnicodeString)"Fail: getRegion(\"" + data[i].id + "\") returns " + + region + " [expected: U_ILLEGAL_ARGUMENT_ERROR]"); + } else if (uprv_strcmp(region, data[i].region) != 0) { + errln((UnicodeString)"Fail: getRegion(\"" + data[i].id + "\") returns " + + region + " [expected: " + data[i].region + "]"); + } + } else if (sts == U_ILLEGAL_ARGUMENT_ERROR) { + if (data[i].region != 0) { + errln((UnicodeString)"Fail: getRegion(\"" + data[i].id + + "\") returns error status U_ILLEGAL_ARGUMENT_ERROR [expected: " + + data[i].region + "]"); + } + } else { + errln((UnicodeString)"Fail: getRegion(\"" + data[i].id + + "\") returns an unexpected error status"); + } + } + + // Extra test cases for short buffer + int32_t len; + char region2[2]; + sts = U_ZERO_ERROR; + + len = TimeZone::getRegion("America/New_York", region2, sizeof(region2), sts); + if (sts != U_STRING_NOT_TERMINATED_WARNING) { + errln("Expected U_STRING_NOT_TERMINATED_WARNING"); + } + if (len != 2) { // length of "US" + errln("Incorrect result length"); + } + if (uprv_strncmp(region2, "US", 2) != 0) { + errln("Incorrect result"); + } + + char region1[1]; + sts = U_ZERO_ERROR; + + len = TimeZone::getRegion("America/Chicago", region1, sizeof(region1), sts); + if (sts != U_BUFFER_OVERFLOW_ERROR) { + errln("Expected U_BUFFER_OVERFLOW_ERROR"); + } + if (len != 2) { // length of "US" + errln("Incorrect result length"); + } +} #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/tztest.h b/icu4c/source/test/intltest/tztest.h index ec4be9c4179..d1c0e94b6d5 100644 --- a/icu4c/source/test/intltest/tztest.h +++ b/icu4c/source/test/intltest/tztest.h @@ -1,6 +1,6 @@ /******************************************************************** - * Copyright (c) 1997-2010, International Business Machines + * Copyright (c) 1997-2011, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************/ @@ -89,9 +89,11 @@ public: void TestFebruary(void); void TestCanonicalID(void); - + virtual void TestDisplayNamesMeta(); + void TestGetRegion(void); + static const UDate INTERVAL; private: