ICU-8278 New APIs TimeZone::createTimeZoneIDEnumeration and ucal_openTimeZoneIDEnumeration for supporting canonical/canonical location zone iteration. The fix for ticket#8349 Etc/Unknown problem was also done at the same time.

X-SVN-Rev: 29498
This commit is contained in:
Yoshito Umaoka 2011-03-01 18:29:13 +00:00
parent 00e54087ea
commit 34bc79c98b
10 changed files with 1161 additions and 654 deletions

View File

@ -124,6 +124,14 @@ static U_NAMESPACE_QUALIFIER TimeZone* _GMT = NULL; // cf. TimeZone::GMT
static char TZDATA_VERSION[16];
static UBool TZDataVersionInitialized = FALSE;
static int32_t* MAP_SYSTEM_ZONES = NULL;
static int32_t* MAP_CANONICAL_SYSTEM_ZONES = NULL;
static int32_t* MAP_CANONICAL_SYSTEM_LOCATION_ZONES = NULL;
int32_t LEN_SYSTEM_ZONES = 0;
int32_t LEN_CANONICAL_SYSTEM_ZONES = 0;
int32_t LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
U_CDECL_BEGIN
static UBool U_CALLCONV timeZone_cleanup(void)
{
@ -136,6 +144,18 @@ static UBool U_CALLCONV timeZone_cleanup(void)
uprv_memset(TZDATA_VERSION, 0, sizeof(TZDATA_VERSION));
TZDataVersionInitialized = FALSE;
LEN_SYSTEM_ZONES = 0;
uprv_free(MAP_SYSTEM_ZONES);
MAP_SYSTEM_ZONES = 0;
LEN_CANONICAL_SYSTEM_ZONES = 0;
uprv_free(MAP_CANONICAL_SYSTEM_ZONES);
MAP_CANONICAL_SYSTEM_ZONES = 0;
LEN_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
uprv_free(MAP_CANONICAL_SYSTEM_LOCATION_ZONES);
MAP_CANONICAL_SYSTEM_LOCATION_ZONES = 0;
if (LOCK) {
umtx_destroy(&LOCK);
LOCK = NULL;
@ -151,49 +171,6 @@ U_CDECL_END
U_NAMESPACE_BEGIN
/**
* The Olson data is stored the "zoneinfo" resource bundle.
* Sub-resources are organized into three ranges of data: Zones, final
* rules, and country tables. There is also a meta-data resource
* which has 3 integers: The number of zones, rules, and countries,
* respectively. The country count includes the non-country 'Default'.
*/
static int32_t OLSON_ZONE_COUNT = 0; // count of zones
/**
* Given a pointer to an open "zoneinfo" resource, load up the Olson
* meta-data. Return TRUE if successful.
*/
static UBool getOlsonMeta(const UResourceBundle* top) {
if (OLSON_ZONE_COUNT == 0) {
UErrorCode ec = U_ZERO_ERROR;
UResourceBundle res;
ures_initStackObject(&res);
ures_getByKey(top, kZONES, &res, &ec);
if(U_SUCCESS(ec)) {
OLSON_ZONE_COUNT = ures_getSize(&res);
U_DEBUG_TZ_MSG(("OZC%d\n",OLSON_ZONE_COUNT));
}
ures_close(&res);
}
return (OLSON_ZONE_COUNT > 0);
}
/**
* Load up the Olson meta-data. Return TRUE if successful.
*/
static UBool getOlsonMeta() {
if (OLSON_ZONE_COUNT == 0) {
UErrorCode ec = U_ZERO_ERROR;
UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
if (U_SUCCESS(ec)) {
getOlsonMeta(top);
}
ures_close(top);
}
return (OLSON_ZONE_COUNT > 0);
}
static int32_t findInStringArray(UResourceBundle* array, const UnicodeString& id, UErrorCode &status)
{
UnicodeString copy;
@ -304,7 +281,7 @@ static UResourceBundle* openOlsonResource(const UnicodeString& id,
// Dereference if this is an alias. Docs say result should be 1
// but it is 0 in 2.8 (?).
U_DEBUG_TZ_MSG(("Loading zone '%s' (%s, size %d) - %s\n", buf, ures_getKey((UResourceBundle*)&res), ures_getSize(&res), u_errorName(ec)));
if (ures_getType(&res) == URES_INT && getOlsonMeta(top)) {
if (ures_getType(&res) == URES_INT) {
int32_t deref = ures_getInt(&res, &ec) + 0;
U_DEBUG_TZ_MSG(("getInt: %s - type is %d\n", u_errorName(ec), ures_getType(&res)));
UResourceBundle *ares = ures_getByKey(top, kZONES, NULL, &ec); // dereference Zones section
@ -424,8 +401,16 @@ TimeZone::createTimeZone(const UnicodeString& ID)
*/
TimeZone*
TimeZone::createSystemTimeZone(const UnicodeString& id) {
TimeZone* z = 0;
UErrorCode ec = U_ZERO_ERROR;
return createSystemTimeZone(id, ec);
}
TimeZone*
TimeZone::createSystemTimeZone(const UnicodeString& id, UErrorCode& ec) {
if (U_FAILURE(ec)) {
return NULL;
}
TimeZone* z = 0;
UResourceBundle res;
ures_initStackObject(&res);
U_DEBUG_TZ_MSG(("pre-err=%s\n", u_errorName(ec)));
@ -657,10 +642,17 @@ private:
// Map into to zones. Our results are zone[map[i]] for
// i=0..len-1, where zone[i] is the i-th Olson zone. If map==NULL
// then our results are zone[i] for i=0..len-1. Len will be zero
// iff the zone data could not be loaded.
// if the zone data could not be loaded.
int32_t* map;
int32_t len;
int32_t pos;
int32_t* localMap;
TZEnumeration(int32_t* mapData, int32_t mapLen, UBool adoptMapData) : pos(0) {
map = mapData;
localMap = adoptMapData ? mapData : NULL;
len = mapLen;
}
UBool getID(int32_t i) {
UErrorCode ec = U_ZERO_ERROR;
@ -679,118 +671,272 @@ private:
return U_SUCCESS(ec);
}
public:
TZEnumeration() : map(NULL), len(0), pos(0) {
if (getOlsonMeta()) {
len = OLSON_ZONE_COUNT;
static int32_t* getMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) {
len = 0;
if (U_FAILURE(ec)) {
return NULL;
}
int32_t* m = NULL;
switch (type) {
case UCAL_ZONE_TYPE_ANY:
m = MAP_SYSTEM_ZONES;
len = LEN_SYSTEM_ZONES;
break;
case UCAL_ZONE_TYPE_CANONICAL:
m = MAP_CANONICAL_SYSTEM_ZONES;
len = LEN_CANONICAL_SYSTEM_ZONES;
break;
case UCAL_ZONE_TYPE_CANONICAL_LOCATION:
m = MAP_CANONICAL_SYSTEM_LOCATION_ZONES;
len = LEN_CANONICAL_SYSTEM_LOCATION_ZONES;
break;
}
UBool needsInit = FALSE;
UMTX_CHECK(&LOCK, (len == 0), needsInit);
if (needsInit) {
m = initMap(type, len, ec);
}
return m;
}
TZEnumeration(int32_t rawOffset) : map(NULL), len(0), pos(0) {
if (!getOlsonMeta()) {
return;
static int32_t* initMap(USystemTimeZoneType type, int32_t& len, UErrorCode& ec) {
len = 0;
if (U_FAILURE(ec)) {
return NULL;
}
// Allocate more space than we'll need. The end of the array will
// be blank.
map = (int32_t*)uprv_malloc(OLSON_ZONE_COUNT * sizeof(int32_t));
if (map == 0) {
return;
}
int32_t *result = NULL;
uprv_memset(map, 0, sizeof(int32_t) * OLSON_ZONE_COUNT);
UnicodeString s;
for (int32_t i=0; i<OLSON_ZONE_COUNT; ++i) {
if (getID(i)) {
// This is VERY inefficient.
TimeZone* z = TimeZone::createTimeZone(unistr);
// Make sure we get back the ID we wanted (if the ID is
// invalid we get back GMT).
if (z != 0 && z->getID(s) == unistr &&
z->getRawOffset() == rawOffset) {
map[len++] = i;
}
delete z;
}
}
}
TZEnumeration(const char* country) : map(NULL), len(0), pos(0) {
if (!getOlsonMeta()) {
return;
}
UErrorCode ec = U_ZERO_ERROR;
UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec);
ures_getByKey(res, kREGIONS, res, &ec);
if (U_SUCCESS(ec) && ures_getType(res) == URES_ARRAY) {
UChar uCountry[] = {0, 0, 0, 0};
if (country) {
u_charsToUChars(country, uCountry, 2);
res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
if (U_SUCCESS(ec)) {
int32_t size = ures_getSize(res);
int32_t *m = (int32_t *)uprv_malloc(size * sizeof(int32_t));
if (m == NULL) {
ec = U_MEMORY_ALLOCATION_ERROR;
} else {
u_strcpy(uCountry, WORLD);
}
// count matches
int32_t count = 0;
int32_t i;
const UChar *region;
for (i = 0; i < ures_getSize(res); i++) {
region = ures_getStringByIndex(res, i, NULL, &ec);
if (U_FAILURE(ec)) {
break;
}
if (u_strcmp(uCountry, region) == 0) {
count++;
}
}
if (count > 0) {
map = (int32_t*)uprv_malloc(sizeof(int32_t) * count);
if (map != NULL) {
int32_t idx = 0;
for (i = 0; i < ures_getSize(res); i++) {
region = ures_getStringByIndex(res, i, NULL, &ec);
int32_t numEntries = 0;
for (int32_t i = 0; i < size; i++) {
const UChar *id = ures_getStringByIndex(res, i, NULL, &ec);
if (U_FAILURE(ec)) {
break;
}
if (u_strcmp(id, UNKNOWN_ZONE_ID) == 0) {
// exclude Etc/Unknown
continue;
}
if (type == UCAL_ZONE_TYPE_CANONICAL || type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
UnicodeString canonicalID;
ZoneMeta::getCanonicalCLDRID(id, canonicalID, ec);
if (U_FAILURE(ec)) {
break;
}
if (u_strcmp(uCountry, region) == 0) {
map[idx++] = i;
if (canonicalID.compare(id, -1) != 0) {
// exclude aliases
continue;
}
}
if (U_SUCCESS(ec)) {
len = count;
} else {
uprv_free(map);
map = NULL;
if (type == UCAL_ZONE_TYPE_CANONICAL_LOCATION) {
const UChar *region = TimeZone::getRegion(id, ec);
if (U_FAILURE(ec)) {
break;
}
if (u_strcmp(region, WORLD) == 0) {
// exclude non-location ("001")
continue;
}
}
} else {
U_DEBUG_TZ_MSG(("Failed to load tz for region %s: %s\n", country, u_errorName(ec)));
m[numEntries++] = i;
}
if (U_SUCCESS(ec)) {
int32_t *tmp = m;
m = (int32_t *)uprv_realloc(tmp, numEntries * sizeof(int32_t));
if (m == NULL) {
// realloc failed.. use the original one even it has unused
// area at the end
m = tmp;
}
umtx_lock(&LOCK);
{
switch(type) {
case UCAL_ZONE_TYPE_ANY:
if (MAP_SYSTEM_ZONES == NULL) {
MAP_SYSTEM_ZONES = m;
LEN_SYSTEM_ZONES = numEntries;
m = NULL;
ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
}
result = MAP_SYSTEM_ZONES;
len = LEN_SYSTEM_ZONES;
break;
case UCAL_ZONE_TYPE_CANONICAL:
if (MAP_CANONICAL_SYSTEM_ZONES == NULL) {
MAP_CANONICAL_SYSTEM_ZONES = m;
LEN_CANONICAL_SYSTEM_ZONES = numEntries;
m = NULL;
ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
}
result = MAP_CANONICAL_SYSTEM_ZONES;
len = LEN_CANONICAL_SYSTEM_ZONES;
break;
case UCAL_ZONE_TYPE_CANONICAL_LOCATION:
if (MAP_CANONICAL_SYSTEM_LOCATION_ZONES == NULL) {
MAP_CANONICAL_SYSTEM_LOCATION_ZONES = m;
LEN_CANONICAL_SYSTEM_LOCATION_ZONES = numEntries;
m = NULL;
ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
}
result = MAP_CANONICAL_SYSTEM_LOCATION_ZONES;
len = LEN_CANONICAL_SYSTEM_LOCATION_ZONES;
break;
}
}
umtx_unlock(&LOCK);
}
uprv_free(m);
}
}
ures_close(res);
return result;
}
TZEnumeration(const TZEnumeration &other) : StringEnumeration(), map(NULL), len(0), pos(0) {
if(other.len > 0) {
if(other.map != NULL) {
map = (int32_t *)uprv_malloc(other.len * sizeof(int32_t));
if(map != NULL) {
len = other.len;
uprv_memcpy(map, other.map, len * sizeof(int32_t));
pos = other.pos;
}
} else {
len = other.len;
pos = other.pos;
public:
#define DEFAULT_FILTERED_MAP_SIZE 8
#define MAP_INCREMENT_SIZE 8
static TZEnumeration* create(USystemTimeZoneType type, const char* region, const int32_t* rawOffset, UErrorCode& ec) {
if (U_FAILURE(ec)) {
return NULL;
}
int32_t baseLen;
int32_t *baseMap = getMap(type, baseLen, ec);
if (U_FAILURE(ec)) {
return NULL;
}
// If any additional conditions are available,
// create instance local map filtered by the conditions.
int32_t *filteredMap = NULL;
int32_t numEntries = 0;
if (region != NULL || rawOffset != NULL) {
int32_t filteredMapSize = DEFAULT_FILTERED_MAP_SIZE;
filteredMap = (int32_t *)uprv_malloc(filteredMapSize * sizeof(int32_t));
if (filteredMap == NULL) {
ec = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
// Walk through the base map
UResourceBundle *res = ures_openDirect(0, kZONEINFO, &ec);
res = ures_getByKey(res, kNAMES, res, &ec); // dereference Zones section
for (int32_t i = 0; i < baseLen; i++) {
int32_t zidx = baseMap[i];
const UChar *id = ures_getStringByIndex(res, zidx, NULL, &ec);
if (U_FAILURE(ec)) {
break;
}
if (region != NULL) {
// Filter by region
char tzregion[4]; // max 3 letters + null term
TimeZone::getRegion(id, tzregion, sizeof(tzregion), ec);
if (U_FAILURE(ec)) {
break;
}
if (uprv_stricmp(tzregion, region) != 0) {
// region does not match
continue;
}
}
if (rawOffset != NULL) {
// Filter by raw offset
// Note: This is VERY inefficient
TimeZone *z = TimeZone::createSystemTimeZone(id, ec);
if (U_FAILURE(ec)) {
break;
}
int32_t tzoffset = z->getRawOffset();
delete z;
if (tzoffset != *rawOffset) {
continue;
}
}
if (filteredMapSize <= numEntries) {
filteredMapSize += MAP_INCREMENT_SIZE;
int32_t *tmp = (int32_t *)uprv_realloc(filteredMap, filteredMapSize * sizeof(int32_t));
if (tmp == NULL) {
ec = U_MEMORY_ALLOCATION_ERROR;
break;
} else {
filteredMap = tmp;
}
}
filteredMap[numEntries++] = zidx;
}
if (U_FAILURE(ec)) {
uprv_free(filteredMap);
filteredMap = NULL;
}
ures_close(res);
}
TZEnumeration *result = NULL;
if (U_SUCCESS(ec)) {
// Finally, create a new enumeration instance
if (filteredMap == NULL) {
result = new TZEnumeration(baseMap, baseLen, FALSE);
} else {
result = new TZEnumeration(filteredMap, numEntries, TRUE);
filteredMap = NULL;
}
if (result == NULL) {
ec = U_MEMORY_ALLOCATION_ERROR;
}
}
if (filteredMap != NULL) {
uprv_free(filteredMap);
}
return result;
}
TZEnumeration(const TZEnumeration &other) : StringEnumeration(), map(NULL), localMap(NULL), len(0), pos(0) {
if (other.localMap != NULL) {
localMap = (int32_t *)uprv_malloc(other.len * sizeof(int32_t));
if (localMap != NULL) {
len = other.len;
uprv_memcpy(localMap, other.localMap, len * sizeof(int32_t));
pos = other.pos;
map = localMap;
} else {
len = 0;
pos = 0;
map = NULL;
}
} else {
map = other.map;
localMap = NULL;
len = other.len;
pos = other.pos;
}
}
virtual ~TZEnumeration() {
uprv_free(map);
if (localMap != NULL) {
uprv_free(localMap);
}
}
virtual StringEnumeration *clone() const {
@ -802,8 +948,8 @@ public:
}
virtual const UnicodeString* snext(UErrorCode& status) {
if (U_SUCCESS(status) && pos < len) {
getID((map == 0) ? pos : map[pos]);
if (U_SUCCESS(status) && map != NULL && pos < len) {
getID(map[pos]);
++pos;
return &unistr;
}
@ -821,19 +967,31 @@ public:
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TZEnumeration)
StringEnumeration* U_EXPORT2
TimeZone::createTimeZoneIDEnumeration(
USystemTimeZoneType zoneType,
const char* region,
const int32_t* rawOffset,
UErrorCode& ec) {
return TZEnumeration::create(zoneType, region, rawOffset, ec);
}
StringEnumeration* U_EXPORT2
TimeZone::createEnumeration() {
return new TZEnumeration();
UErrorCode ec = U_ZERO_ERROR;
return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec);
}
StringEnumeration* U_EXPORT2
TimeZone::createEnumeration(int32_t rawOffset) {
return new TZEnumeration(rawOffset);
UErrorCode ec = U_ZERO_ERROR;
return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec);
}
StringEnumeration* U_EXPORT2
TimeZone::createEnumeration(const char* country) {
return new TZEnumeration(country);
UErrorCode ec = U_ZERO_ERROR;
return TZEnumeration::create(UCAL_ZONE_TYPE_ANY, country, NULL, ec);
}
// ---------------------------------------
@ -876,7 +1034,7 @@ TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) {
ures_getByKey(&res, kLINKS, &r, &ec);
const int32_t* v = ures_getIntVector(&r, &size, &ec);
if (U_SUCCESS(ec)) {
if (index >= 0 && index < size && getOlsonMeta()) {
if (index >= 0 && index < size) {
zone = v[index];
}
}
@ -940,18 +1098,26 @@ TimeZone::dereferOlsonLink(const UnicodeString& id) {
const UChar*
TimeZone::getRegion(const UnicodeString& id) {
UErrorCode status = U_ZERO_ERROR;
return getRegion(id, status);
}
const UChar*
TimeZone::getRegion(const UnicodeString& id, UErrorCode& status) {
if (U_FAILURE(status)) {
return NULL;
}
const UChar *result = NULL;
UErrorCode ec = U_ZERO_ERROR;
UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec);
UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &status);
// resolve zone index by name
UResourceBundle *res = ures_getByKey(rb, kNAMES, NULL, &ec);
int32_t idx = findInStringArray(res, id, ec);
UResourceBundle *res = ures_getByKey(rb, kNAMES, NULL, &status);
int32_t idx = findInStringArray(res, id, status);
// get region mapping
ures_getByKey(rb, kREGIONS, res, &ec);
const UChar *tmp = ures_getStringByIndex(res, idx, NULL, &ec);
if (U_SUCCESS(ec)) {
ures_getByKey(rb, kREGIONS, res, &status);
const UChar *tmp = ures_getStringByIndex(res, idx, NULL, &status);
if (U_SUCCESS(status)) {
result = tmp;
}
@ -961,6 +1127,7 @@ TimeZone::getRegion(const UnicodeString& id) {
return result;
}
// ---------------------------------------
int32_t
TimeZone::getRegion(const UnicodeString& id, char *region, int32_t capacity, UErrorCode& status)
@ -1399,13 +1566,19 @@ TimeZone::getCanonicalID(const UnicodeString& id, UnicodeString& canonicalID, UB
if (U_FAILURE(status)) {
return canonicalID;
}
ZoneMeta::getCanonicalSystemID(id, canonicalID, status);
if (U_SUCCESS(status)) {
isSystemID = TRUE;
if (id.compare(UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH) == 0) {
// special case - Etc/Unknown is a canonical ID, but not system ID
canonicalID.fastCopyFrom(id);
isSystemID = FALSE;
} else {
// Not a system ID
status = U_ZERO_ERROR;
getCustomID(id, canonicalID, status);
ZoneMeta::getCanonicalCLDRID(id, canonicalID, status);
if (U_SUCCESS(status)) {
isSystemID = TRUE;
} else {
// Not a system ID
status = U_ZERO_ERROR;
getCustomID(id, canonicalID, status);
}
}
return canonicalID;
}

View File

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 1996-2010, International Business Machines
* Copyright (C) 1996-2011, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
*/
@ -45,6 +45,13 @@ _createTimeZone(const UChar* zoneID, int32_t len, UErrorCode* ec) {
return zone;
}
U_CAPI UEnumeration* U_EXPORT2
ucal_openTimeZoneIDEnumeration(USystemTimeZoneType zoneType, const char* region,
const int32_t* rawOffset, UErrorCode* ec) {
return uenum_openFromStringEnumeration(TimeZone::createTimeZoneIDEnumeration(
zoneType, region, rawOffset, *ec), ec);
}
U_CAPI UEnumeration* U_EXPORT2
ucal_openTimeZones(UErrorCode* ec) {
return uenum_openFromStringEnumeration(TimeZone::createEnumeration(), ec);

View File

@ -39,6 +39,7 @@
#include "unicode/uobject.h"
#include "unicode/unistr.h"
#include "unicode/ures.h"
#include "unicode/ucal.h"
U_NAMESPACE_BEGIN
@ -146,6 +147,28 @@ public:
*/
static TimeZone* U_EXPORT2 createTimeZone(const UnicodeString& ID);
/**
* Returns an enumeration over system time zone IDs with the given
* filter conditions.
* @param zoneType The system time zone type.
* @param region The ISO 3166 two-letter country code or UN M.49
* three-digit area code. When NULL, no filtering
* done by region.
* @param rawOffset An offset from GMT in milliseconds, ignoring
* the effect of daylight savings time, if any.
* When NULL, no filtering done by zone offset.
* @param ec Output param to filled in with a success or
* an error.
* @return an enumeration object, owned by the caller.
* @draft ICU 4.8
*/
static StringEnumeration* U_EXPORT2 createTimeZoneIDEnumeration(
USystemTimeZoneType zoneType,
const char* region,
const int32_t* rawOffset,
UErrorCode& ec);
/**
* Returns an enumeration over all recognized time zone IDs. (i.e.,
* all strings that createTimeZone() accepts)
@ -725,6 +748,15 @@ private:
*/
static const UChar* getRegion(const UnicodeString& id);
/**
* Returns the region code associated with the given zone,
* or NULL if the zone is not known.
* @param id zone id string
* @param status Status parameter
* @return the region associated with the given zone
*/
static const UChar* getRegion(const UnicodeString& id, UErrorCode& status);
/**
* Parses the given custom time zone identifier
* @param id id A string of the form GMT[+-]hh:mm, GMT[+-]hhmm, or
@ -779,9 +811,11 @@ private:
* @return the TimeZone indicated by the 'name'.
*/
static TimeZone* createSystemTimeZone(const UnicodeString& name);
static TimeZone* createSystemTimeZone(const UnicodeString& name, UErrorCode& ec);
UnicodeString fID; // this time zone's ID
friend class TZEnumeration;
};

View File

@ -521,6 +521,53 @@ enum UCalendarAMPMs {
/** @stable ICU 2.0 */
typedef enum UCalendarAMPMs UCalendarAMPMs;
/**
* System time zone type constants used by filtering zones
* in ucal_openTimeZoneIDEnumeration.
* @see ucal_openTimeZoneIDEnumeration
* @draft ICU 4.8
*/
enum USystemTimeZoneType {
/**
* Any system zones.
* @draft ICU 4.8
*/
UCAL_ZONE_TYPE_ANY,
/**
* Canonical system zones.
* @draft ICU 4.8
*/
UCAL_ZONE_TYPE_CANONICAL,
/**
* Canonical system zones associated with actual locations.
* @draft ICU 4.8
*/
UCAL_ZONE_TYPE_CANONICAL_LOCATION
};
/** @draft ICU 4.8 */
typedef enum USystemTimeZoneType USystemTimeZoneType;
/**
* Create an enumeration over system time zone IDs with the given
* filter conditions.
* @param zoneType The system time zone type.
* @param region The ISO 3166 two-letter country code or UN M.49
* three-digit area code. When NULL, no filtering
* done by region.
* @param rawOffset An offset from GMT in milliseconds, ignoring the
* effect of daylight savings time, if any. When NULL,
* no filtering done by zone offset.
* @param ec A pointer to an UErrorCode to receive any errors
* @return an enumeration object that the caller must dispose of
* using enum_close(), or NULL upon failure. In case of failure,
* *ec will indicate the error.
* @draft ICU 4.8
*/
U_DRAFT UEnumeration* U_EXPORT2
ucal_openTimeZoneIDEnumeration(USystemTimeZoneType zoneType, const char* region,
const int32_t* rawOffset, UErrorCode* ec);
/**
* Create an enumeration over all time zones.
*

View File

@ -185,7 +185,7 @@ parseDate (const UChar *text, UErrorCode &status) {
}
UnicodeString& U_EXPORT2
ZoneMeta::getCanonicalSystemID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status) {
ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status) {
int32_t len = tzid.length();
if ( len >= ZID_KEY_MAX ) {
status = U_ILLEGAL_ARGUMENT_ERROR;
@ -348,27 +348,11 @@ ZoneMeta::getSingleCountry(const UnicodeString &tzid, UnicodeString &country) {
char buf[] = {0, 0, 0};
u_UCharsToChars(region, buf, 2);
StringEnumeration *ids = TimeZone::createEnumeration(buf);
StringEnumeration *ids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, buf, NULL, status);
int32_t idsLen = ids->count(status);
if (U_SUCCESS(status) && idsLen > 1) {
// multiple zones are available for the region
UnicodeString canonical, tmp;
const UnicodeString *id = ids->snext(status);
getCanonicalSystemID(*id, canonical, status);
if (U_SUCCESS(status)) {
// check if there are any other canonical zone in the group
while ((id = ids->snext(status))!=NULL) {
getCanonicalSystemID(*id, tmp, status);
if (U_FAILURE(status)) {
break;
}
if (canonical != tmp) {
// another canonical zone was found
multiZones = TRUE;
break;
}
}
}
// multiple canonical zones are available for the region
multiZones = TRUE;
}
if (U_FAILURE(status)) {
// no single country by default for any error cases
@ -519,7 +503,7 @@ ZoneMeta::createMetazoneMappings(const UnicodeString &tzid) {
UnicodeString canonicalID;
UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
ures_getByKey(rb, gMetazoneInfo, rb, &status);
TimeZone::getCanonicalID(tzid, canonicalID, status);
getCanonicalCLDRID(tzid, canonicalID, status);
if (U_SUCCESS(status)) {
char tzKey[ZID_KEY_MAX];

View File

@ -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. *
*******************************************************************************
*/
@ -27,10 +27,13 @@ class UVector;
class U_I18N_API ZoneMeta {
public:
/**
* Return the canonical id for this system tzid, which might be the id itself.
* If the given system tzid is not know, U_ILLEGAL_ARGUMENT_ERROR is set in the status.
* Return the canonical id for this tzid defined by CLDR, which might be the id itself.
* If the given system tzid is not known, U_ILLEGAL_ARGUMENT_ERROR is set in the status.
*
* Note: this internal API supports all known system IDs and "Etc/Unknown" (which is
* NOT a system ID).
*/
static UnicodeString& U_EXPORT2 getCanonicalSystemID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status);
static UnicodeString& U_EXPORT2 getCanonicalCLDRID(const UnicodeString &tzid, UnicodeString &systemID, UErrorCode& status);
/**
* Return the canonical country code for this tzid. If we have none, or if the time zone

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/********************************************************************
* Copyright (c) 1997-2010, International Business Machines
* Copyright (c) 1997-2011, International Business Machines
* Corporation and others. All Rights Reserved.
********************************************************************
*
@ -123,12 +123,36 @@ static void TestCalendar()
status=U_ZERO_ERROR;
#endif
/*Test ucal_openTimeZones & ucal_openCountryTimeZones*/
for (j=0; j<2; ++j) {
const char* api = (j==0) ? "ucal_openTimeZones()" :
"ucal_openCountryTimeZones(US)";
uenum = (j==0) ? ucal_openTimeZones(&status) :
ucal_openCountryTimeZones("US", &status);
/*Test ucal_openTimeZones, ucal_openCountryTimeZones and ucal_openTimeZoneIDEnumeration */
for (j=0; j<6; ++j) {
const char *api;
const int32_t offsetMinus5 = -5*60*60*1000;
switch (j) {
case 0:
api = "ucal_openTimeZones()";
uenum = ucal_openTimeZones(&status);
break;
case 1:
api = "ucal_openCountryTimeZones(US)";
uenum = ucal_openCountryTimeZones("US", &status);
break;
case 2:
api = "ucal_openTimeZoneIDEnumerarion(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL)";
uenum = ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, &status);
break;
case 3:
api = "ucal_openTimeZoneIDEnumerarion(UCAL_ZONE_TYPE_CANONICAL_LOCATION, CA, NULL)";
uenum = ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, "CA", NULL, &status);
break;
case 4:
api = "ucal_openTimeZoneIDEnumerarion(UCAL_ZONE_TYPE_ANY, NULL, -5 hour)";
uenum = ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, NULL, &offsetMinus5, &status);
break;
case 5:
api = "ucal_openTimeZoneIDEnumerarion(UCAL_ZONE_TYPE_ANY, US, -5 hour)";
uenum = ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "US", &offsetMinus5, &status);
break;
}
if (U_FAILURE(status)) {
log_err("FAIL: %s failed with %s", api,
u_errorName(status));

View File

@ -62,7 +62,8 @@ void TimeZoneTest::runIndexedTest( int32_t index, UBool exec, const char* &name,
CASE(16, TestCanonicalID);
CASE(17, TestDisplayNamesMeta);
CASE(18, TestGetRegion);
default: name = ""; break;
CASE(19, TestGetAvailableIDsNew);
default: name = ""; break;
}
}
@ -510,6 +511,234 @@ TimeZoneTest::TestGetAvailableIDs913()
delete s;
}
void
TimeZoneTest::TestGetAvailableIDsNew()
{
UErrorCode ec = U_ZERO_ERROR;
StringEnumeration *any, *canonical, *canonicalLoc;
StringEnumeration *any_US, *canonical_US, *canonicalLoc_US;
StringEnumeration *any_W5, *any_CA_W5;
StringEnumeration *any_US_E14;
int32_t rawOffset;
const UnicodeString *id1, *id2;
UnicodeString canonicalID;
UBool isSystemID;
char region[4];
int32_t zoneCount;
any = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec);
if (U_FAILURE(ec)) {
errln("Failed to create enumration for ANY");
goto cleanup;
}
canonical = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, ec);
if (U_FAILURE(ec)) {
errln("Failed to create enumration for CANONICAL");
goto cleanup;
}
canonicalLoc = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, NULL, NULL, ec);
if (U_FAILURE(ec)) {
errln("Failed to create enumration for CANONICALLOC");
goto cleanup;
}
any_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "US", NULL, ec);
if (U_FAILURE(ec)) {
errln("Failed to create enumration for ANY_US");
goto cleanup;
}
canonical_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, "US", NULL, ec);
if (U_FAILURE(ec)) {
errln("Failed to create enumration for CANONICAL_US");
goto cleanup;
}
canonicalLoc_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, "US", NULL, ec);
if (U_FAILURE(ec)) {
errln("Failed to create enumration for CANONICALLOC_US");
goto cleanup;
}
rawOffset = (-5)*60*60*1000;
any_W5 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec);
if (U_FAILURE(ec)) {
errln("Failed to create enumration for ANY_W5");
goto cleanup;
}
any_CA_W5 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "CA", &rawOffset, ec);
if (U_FAILURE(ec)) {
errln("Failed to create enumration for ANY_CA_W5");
goto cleanup;
}
rawOffset = 14*60*60*1000;
any_US_E14 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "US", &rawOffset, ec);
if (U_FAILURE(ec)) {
errln("Failed to create enumration for ANY_US_E14");
goto cleanup;
}
checkContainsAll(any, "ANY", canonical, "CANONICAL");
checkContainsAll(canonical, "CANONICAL", canonicalLoc, "CANONICALLOC");
checkContainsAll(any, "ANY", any_US, "ANY_US");
checkContainsAll(canonical, "CANONICAL", canonical_US, "CANONICAL_US");
checkContainsAll(canonicalLoc, "CANONICALLOC", canonicalLoc_US, "CANONICALLOC_US");
checkContainsAll(any_US, "ANY_US", canonical_US, "CANONICAL_US");
checkContainsAll(canonical_US, "CANONICAL_US", canonicalLoc_US, "CANONICALLOC_US");
checkContainsAll(any, "ANY", any_W5, "ANY_W5");
checkContainsAll(any_W5, "ANY_W5", any_CA_W5, "ANY_CA_W5");
// And ID in any set, but not in canonical set must not be a canonical ID
any->reset(ec);
while (id1 = any->snext(ec)) {
UBool found = FALSE;
canonical->reset(ec);
while (id2 = canonical->snext(ec)) {
if (*id1 == *id2) {
found = TRUE;
break;
}
}
if (U_FAILURE(ec)) {
break;
}
if (!found) {
TimeZone::getCanonicalID(*id1, canonicalID, isSystemID, ec);
if (U_FAILURE(ec)) {
break;
}
if (*id1 == canonicalID) {
errln((UnicodeString)"FAIL: canonicalID [" + *id1 + "] is not in CANONICAL");
}
if (!isSystemID) {
errln((UnicodeString)"FAIL: ANY contains non-system ID: " + *id1);
}
}
}
if (U_FAILURE(ec)) {
errln("Error checking IDs in ANY, but not in CANONICAL");
ec = U_ZERO_ERROR;
}
// canonical set must contains only canonical IDs
canonical->reset(ec);
while (id1 = canonical->snext(ec)) {
TimeZone::getCanonicalID(*id1, canonicalID, isSystemID, ec);
if (U_FAILURE(ec)) {
break;
}
if (*id1 != canonicalID) {
errln((UnicodeString)"FAIL: CANONICAL contains non-canonical ID: " + *id1);
}
if (!isSystemID) {
errln((UnicodeString)"FAILE: CANONICAL contains non-system ID: " + *id1);
}
}
if (U_FAILURE(ec)) {
errln("Error checking IDs in CANONICAL");
ec = U_ZERO_ERROR;
}
// canonicalLoc set must contain only canonical location IDs
canonicalLoc->reset(ec);
while (id1 = canonicalLoc->snext(ec)) {
TimeZone::getRegion(*id1, region, sizeof(region), ec);
if (U_FAILURE(ec)) {
break;
}
if (uprv_strcmp(region, "001") == 0) {
errln((UnicodeString)"FAIL: CANONICALLOC contains non location zone: " + *id1);
}
}
if (U_FAILURE(ec)) {
errln("Error checking IDs in CANONICALLOC");
ec = U_ZERO_ERROR;
}
// any_US must contain only US zones
any_US->reset(ec);
while (id1 = any_US->snext(ec)) {
TimeZone::getRegion(*id1, region, sizeof(region), ec);
if (U_FAILURE(ec)) {
break;
}
if (uprv_strcmp(region, "US") != 0) {
errln((UnicodeString)"FAIL: ANY_US contains non-US zone ID: " + *id1);
}
}
if (U_FAILURE(ec)) {
errln("Error checking IDs in ANY_US");
ec = U_ZERO_ERROR;
}
// any_W5 must contain only GMT-05:00 zones
any_W5->reset(ec);
while (id1 = any_W5->snext(ec)) {
TimeZone *tz = TimeZone::createTimeZone(*id1);
if (tz->getRawOffset() != (-5)*60*60*1000) {
errln((UnicodeString)"FAIL: ANY_W5 contains a zone whose offset is not -05:00: " + *id1);
}
}
if (U_FAILURE(ec)) {
errln("Error checking IDs in ANY_W5");
ec = U_ZERO_ERROR;
}
// No US zone swith GMT+14:00
zoneCount = any_US_E14->count(ec);
if (U_FAILURE(ec)) {
errln("Error checking IDs in ANY_US_E14");
ec = U_ZERO_ERROR;
} else if (zoneCount != 0) {
errln("FAIL: ANY_US_E14 must be empty");
}
cleanup:
delete any;
delete canonical;
delete canonicalLoc;
delete any_US;
delete canonical_US;
delete canonicalLoc_US;
delete any_W5;
delete any_CA_W5;
}
void
TimeZoneTest::checkContainsAll(StringEnumeration *s1, const char *name1,
StringEnumeration *s2, const char *name2)
{
UErrorCode ec = U_ZERO_ERROR;
const UnicodeString *id1, *id2;
s2->reset(ec);
while (id2 = s2->snext(ec)) {
UBool found = FALSE;
s1->reset(ec);
while (id1 = s1->snext(ec)) {
if (*id1 == *id2) {
found = TRUE;
break;
}
}
if (!found) {
errln((UnicodeString)"FAIL: " + name1 + "does not contain "
+ *id2 + " in " + name2);
}
}
if (U_FAILURE(ec)) {
errln((UnicodeString)"Error checkContainsAll for " + name1 + " - " + name2);
}
}
/**
* NOTE: As of ICU 2.8, this test confirms that the "tz.alias"
@ -1847,6 +2076,7 @@ void TimeZoneTest::TestCanonicalID() {
{"GMT-091015", "GMT-09:10:15", FALSE},
{"GMT+1:90", 0, FALSE},
{"America/Argentina/Buenos_Aires", "America/Buenos_Aires", TRUE},
{"Etc/Unknown", "Etc/Unknown", FALSE},
{"bogus", 0, FALSE},
{"", 0, FALSE},
{0, 0, FALSE}

View File

@ -37,6 +37,8 @@ public:
*/
virtual void TestGetAvailableIDs913(void);
virtual void TestGetAvailableIDsNew(void);
/**
* Generic API testing for API coverage.
*/
@ -105,6 +107,9 @@ private:
// when year/rule are changed.
static const int32_t REFERENCE_YEAR;
static const char *REFERENCE_DATA_VERSION;
void checkContainsAll(StringEnumeration *s1, const char *name1,
StringEnumeration *s2, const char *name2);
};
#endif /* #if !UCONFIG_NO_FORMATTING */