ICU-5708 Generic time zone support

X-SVN-Rev: 22283
This commit is contained in:
John Emmons 2007-08-06 15:17:35 +00:00
parent 3727ad903b
commit a2d03cc0fb
5 changed files with 242 additions and 11 deletions

View File

@ -19,13 +19,13 @@
* 10/12/05 emmons Added setters for eraNames, month/day by width/context
*******************************************************************************
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/ustring.h"
#include "unicode/dtfmtsym.h"
#include "unicode/smpdtfmt.h"
#include "unicode/msgfmt.h"
#include "cpputils.h"
#include "ucln_in.h"
#include "umutex.h"
@ -173,6 +173,12 @@ static const char gAmPmMarkersTag[]="AmPmMarkers";
static const char gQuartersTag[]="quarters";
static const char gMaptimezonesTag[]="mapTimezones";
static const char gMetazonesTag[]="metazones";
static const char gTerritoryTag[]="territory";
static const char gCountriesTag[]="Countries";
static const char gZoneFormattingTag[]="zoneFormatting";
static const char gMultizoneTag[]="multizone";
static const char gRegionFormatTag[]="zoneStrings/regionFormat";
static const char gFallbackFormatTag[]="zoneStrings/fallbackFormat";
/**
* These are the tags we expect to see in time zone data resource bundle files
@ -1977,6 +1983,106 @@ DateFormatSymbols::getMetazoneString(const UnicodeString &zid, const TimeZoneTra
ures_close(um);
return result;
}
UnicodeString&
DateFormatSymbols::getFallbackString(const UnicodeString &zid, UnicodeString &result, UErrorCode &status)
{
UnicodeString exemplarCity;
char zidkey[ZID_KEY_MAX];
char zoneTerritoryChars[ULOC_COUNTRY_CAPACITY];
UnicodeString displayCountry;
UnicodeString solidus = UNICODE_STRING_SIMPLE("/");
UnicodeString und = UNICODE_STRING_SIMPLE("_");
UnicodeString spc = UNICODE_STRING_SIMPLE(" ");
const UChar* aZone = NULL;
UBool IsMultiZone = FALSE;
int32_t len = zid.length();
len = (len >= (ZID_KEY_MAX-1) ? ZID_KEY_MAX-1 : len);
u_UCharsToChars(zid.getBuffer(), zidkey, len);
zidkey[len] = 0; // NULL terminate
// Replace / with : for zid
len = (int32_t)uprv_strlen(zidkey);
for (int i = 0; i < len; i++) {
if (zidkey[i] == '/') {
zidkey[i] = ':';
}
}
result.remove();
UResourceBundle* supplementalDataBundle = ures_openDirect(NULL, kSUPPLEMENTAL, &status);
if (U_FAILURE(status) || fResourceBundle == NULL ) {
return result;
}
UResourceBundle* zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status);
UResourceBundle* thisZone = ures_getByKey(zoneFormatting, zidkey, NULL, &status);
if (U_FAILURE(status)) {
ures_close(zoneFormatting);
ures_close(supplementalDataBundle);
return result;
}
UResourceBundle* multiZone = ures_getByKey(zoneFormatting, gMultizoneTag, NULL, &status);
const UChar *zoneTerritory = ures_getStringByKey(thisZone,gTerritoryTag,&len,&status);
u_UCharsToChars(zoneTerritory, zoneTerritoryChars, u_strlen(zoneTerritory));
zoneTerritoryChars[u_strlen(zoneTerritory)] = 0; // NULL terminate
UResourceBundle* countries = ures_getByKey(fResourceBundle, gCountriesTag, NULL, &status);
if ( u_strlen(zoneTerritory) > 0 && countries != NULL ) {
displayCountry = ures_getStringByKeyWithFallback(countries,zoneTerritoryChars,&len,&status);
}
if ( U_FAILURE(status) ) {
status = U_ZERO_ERROR;
displayCountry = UnicodeString(zoneTerritory);
}
while ( ures_hasNext(multiZone) ) {
aZone = ures_getNextString(multiZone,&len,NULL,&status);
if ( u_strcmp(aZone,zoneTerritory) == 0 ) {
IsMultiZone = TRUE;
continue;
}
}
if ( IsMultiZone ) {
getZoneString(zid, TIMEZONE_EXEMPLAR_CITY, exemplarCity, status);
if ( exemplarCity.length()==0 ) {
exemplarCity.setTo(UnicodeString(zid,zid.lastIndexOf(solidus)+1));
exemplarCity.findAndReplace(und,spc);
}
Formattable cityCountryArray[2];
UnicodeString pattern = UnicodeString(ures_getStringByKeyWithFallback(fResourceBundle,gFallbackFormatTag,&len,&status));
if ( U_FAILURE(status) ) {
pattern = UNICODE_STRING_SIMPLE("{1} ({0})");
status = U_ZERO_ERROR;
}
cityCountryArray[0].adoptString(new UnicodeString(exemplarCity));
cityCountryArray[1].adoptString(new UnicodeString(displayCountry));
MessageFormat::format(pattern,cityCountryArray, 2, result, status);
} else {
Formattable countryArray[1];
UnicodeString pattern = UnicodeString(ures_getStringByKeyWithFallback(fResourceBundle,gRegionFormatTag,&len,&status));
if ( U_FAILURE(status) ) {
pattern = UNICODE_STRING_SIMPLE("{0}");
status = U_ZERO_ERROR;
}
countryArray[0].adoptString(new UnicodeString(displayCountry));
MessageFormat::format(pattern,countryArray, 1, result, status);
}
if (thisZone) ures_close(thisZone);
if (zoneFormatting) ures_close(zoneFormatting);
if (supplementalDataBundle) ures_close(supplementalDataBundle);
if (countries) ures_close(countries);
return result;
}
StringEnumeration*
DateFormatSymbols::createZoneStringIDs(UErrorCode &status){
if(U_FAILURE(status)){
@ -2132,13 +2238,38 @@ DateFormatSymbols::findZoneIDTypeValue( UnicodeString& zid, const UnicodeString&
if (myKey->startsWith(UNICODE_STRING_SIMPLE("meta"))) {
zid.setTo(resolveParsedMetazone(*myKey));
}
else
else {
zid.setTo(*myKey);
}
return;
}
}
}
}
// Check for generic tz fallback strings if we have gone through all zone strings and haven't found
// anything.
UnicodeString fbString;
StringEnumeration *tzKeys = TimeZone::createEnumeration();
tzKeys->reset(status);
while( (myKey=tzKeys->snext(status))!= NULL){
char outpt[1000];
status = U_ZERO_ERROR;
this->getFallbackString(*myKey,fbString,status);
if ( U_FAILURE(status) ) {
status = U_ZERO_ERROR;
continue;
}
if(fbString.length()>0 && text.compare(start, fbString.length(), fbString)==0){
type = (TimeZoneTranslationType) TIMEZONE_LONG_GENERIC;
value.setTo(fbString);
zid.setTo(*myKey);
return;
}
}
}
UnicodeString

View File

@ -24,6 +24,8 @@
********************************************************************************
*/
#define ZID_KEY_MAX 128
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
@ -81,6 +83,9 @@ static const UChar SUPPRESS_NEGATIVE_PREFIX[] = {0xAB00, 0};
* These are the tags we expect to see in normal resource bundle files associated
* with a locale.
*/
static const char kSUPPLEMENTAL[]="supplementalData";
static const char gZoneFormattingTag[]="zoneFormatting";
static const char gAliases[]="aliases";
static const char gDateTimePatternsTag[]="DateTimePatterns";
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleDateFormat)
@ -780,16 +785,20 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
appendGMT(appendTo, cal, status);
}
else {
zoneIDCanonicalize(zid);
if (patternCharIndex == UDAT_TIMEZONE_GENERIC_FIELD) {
if(count < 4){
fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_GENERIC, displayString, status);
if(displayString.length()==0)
fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_SHORT_GENERIC, cal, displayString, status);
if(displayString.length()==0)
fSymbols->getFallbackString(zid, displayString, status);
}else{
fSymbols->getZoneString(zid, DateFormatSymbols::TIMEZONE_LONG_GENERIC, displayString, status);
if(displayString.length()==0)
fSymbols->getMetazoneString(zid, DateFormatSymbols::TIMEZONE_LONG_GENERIC, cal, displayString, status);
if(displayString.length()==0)
fSymbols->getFallbackString(zid, displayString, status);
}
} else {
if (cal.get(UCAL_DST_OFFSET, status) != 0) {
@ -876,7 +885,86 @@ SimpleDateFormat::subFormat(UnicodeString &appendTo,
pos.setEndIndex(appendTo.length());
}
}
//----------------------------------------------------------------------
void
SimpleDateFormat::zoneIDCanonicalize(UnicodeString &zid) const
{
UnicodeString colon = UNICODE_STRING_SIMPLE(":");
UnicodeString solidus = UNICODE_STRING_SIMPLE("/");
char zidkey[ZID_KEY_MAX];
int32_t len;
UErrorCode status = U_ZERO_ERROR;
UResourceBundle* supplementalDataBundle = ures_openDirect(NULL, kSUPPLEMENTAL, &status);
if (U_FAILURE(status)) {
return;
}
UResourceBundle* zoneFormatting = ures_getByKey(supplementalDataBundle, gZoneFormattingTag, NULL, &status);
// First try to lookup the zone itself, since most of the time the canonical ID and the ID itself
// will be the same. This should save us a significant amount of time.
len = zid.length();
len = (len >= (ZID_KEY_MAX-1) ? ZID_KEY_MAX-1 : len);
u_UCharsToChars(zid.getBuffer(), zidkey, len);
zidkey[len] = 0; // NULL terminate
// Replace / with : for zid
len = (int32_t)uprv_strlen(zidkey);
for (int i = 0; i < len; i++) {
if (zidkey[i] == '/') {
zidkey[i] = ':';
}
}
UResourceBundle* tryThisZone = ures_getByKey(zoneFormatting,zidkey,NULL,&status);
if (U_SUCCESS(status)) {
ures_close(tryThisZone);
ures_close(zoneFormatting);
ures_close(supplementalDataBundle);
return;
}
// Didn't find it, so go searching for an alias
while ( ures_hasNext(zoneFormatting)) {
UResourceBundle *currentZone = ures_getNextResource(zoneFormatting,NULL,&status);
if (U_FAILURE(status)) {
ures_close(supplementalDataBundle);
return;
}
const char *currentZoneString= ures_getKey(currentZone);
UResourceBundle *zoneAliases = ures_getByKey(currentZone,gAliases,NULL, &status);
if (U_FAILURE(status)) {
status = U_ZERO_ERROR;
ures_close(currentZone);
continue;
}
while ( ures_hasNext(zoneAliases)) {
int32_t len;
const UChar* alias = ures_getNextString(zoneAliases,&len,NULL,&status);
if ( zid.compare(alias)==0 ) {
zid.setTo(UnicodeString(currentZoneString,(const char *)0));
zid.findAndReplace(colon,solidus);
ures_close(zoneAliases);
ures_close(currentZone);
ures_close(zoneFormatting);
ures_close(supplementalDataBundle);
return;
}
}
ures_close(zoneAliases);
ures_close(currentZone);
}
ures_close(zoneFormatting);
ures_close(supplementalDataBundle);
return;
}
//----------------------------------------------------------------------
void

View File

@ -497,6 +497,18 @@ public:
*/
UnicodeString& getMetazoneString(const UnicodeString &ID, const TimeZoneTranslationType type, Calendar &cal, UnicodeString &result, UErrorCode &status);
/**
* Gets fallback string given the key
* @param ID The ID of zone strings, e.g: "America/Los_Angeles".
* The time zone ID is for programmatic lookup.
* @param result Output parameter to recieve the translation string
* @param status Input/output parameter, set to success or
* failure code upon return.
* @return the input UnicodeString parameter for chaining
* @internal ICU 3.8
*/
UnicodeString& getFallbackString(const UnicodeString &ID, UnicodeString &result, UErrorCode &status);
/**
* Sets timezone string for the given the ID and translation type
* @param ID The ID of zone strings, e.g: "America/Los_Angeles".

View File

@ -649,6 +649,13 @@ private:
Calendar& cal,
UErrorCode& status) const; // in case of illegal argument
/**
* Used to resolve Time Zone aliases
*
* @param zid Time Zone ID to Canonicalize ( resolve aliases )
*/
void zoneIDCanonicalize( UnicodeString & ) const;
/**
* Used by subFormat() to format a numeric value.
* Appends to toAppendTo a string representation of "value"

View File

@ -914,8 +914,6 @@ void TimeZoneTest::TestCustomParse()
}
}
static const UVersionInfo ICU_39 = {3,9,0,0};
void
TimeZoneTest::TestAliasedNames()
{
@ -1047,10 +1045,6 @@ TimeZoneTest::TestAliasedNames()
UBool useDst[] = { FALSE, TRUE };
int32_t noLoc = uloc_countAvailable();
if(isICUVersionAtLeast(ICU_39)) {
errln("This test needs to be fixed. This test fails in exhaustive mode because we need to implement generic timezones.\n");
}
int32_t i, j, k, loc;
UnicodeString fromName, toName;
TimeZone *from = NULL, *to = NULL;
@ -1060,8 +1054,7 @@ TimeZoneTest::TestAliasedNames()
if(!from->hasSameRules(*to)) {
errln("different at %i\n", i);
}
if(!quick && isICUVersionAtLeast(ICU_39)) {
errln("This test needs to be fixed. This test fails in exhaustive mode because we need to implement generic timezones.\n");
if(!quick) {
for(loc = 0; loc < noLoc; loc++) {
const char* locale = uloc_getAvailable(loc);
for(j = 0; j < (int32_t)(sizeof(styles)/sizeof(styles[0])); j++) {