static void TimeZoneNames_fillZoneStrings(JNIEnv* env, jclass, jstring javaLocaleName, jobjectArray result) { ScopedIcuLocale icuLocale(env, javaLocaleName); if (!icuLocale.valid()) { return; } UErrorCode status = U_ZERO_ERROR; UniquePtr<TimeZoneNames> names(TimeZoneNames::createInstance(icuLocale.locale(), status)); if (maybeThrowIcuException(env, "TimeZoneNames::createInstance", status)) { return; } const UDate now(Calendar::getNow()); static const UnicodeString kUtc("UTC", 3, US_INV); size_t id_count = env->GetArrayLength(result); for (size_t i = 0; i < id_count; ++i) { ScopedLocalRef<jobjectArray> java_row(env, reinterpret_cast<jobjectArray>(env->GetObjectArrayElement(result, i))); ScopedLocalRef<jstring> java_zone_id(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(java_row.get(), 0))); ScopedJavaUnicodeString zone_id(env, java_zone_id.get()); if (!zone_id.valid()) { return; } UnicodeString long_std; names->getDisplayName(zone_id.unicodeString(), UTZNM_LONG_STANDARD, now, long_std); UnicodeString short_std; names->getDisplayName(zone_id.unicodeString(), UTZNM_SHORT_STANDARD, now, short_std); UnicodeString long_dst; names->getDisplayName(zone_id.unicodeString(), UTZNM_LONG_DAYLIGHT, now, long_dst); UnicodeString short_dst; names->getDisplayName(zone_id.unicodeString(), UTZNM_SHORT_DAYLIGHT, now, short_dst); if (isUtc(zone_id.unicodeString())) { // ICU doesn't have names for the UTC zones; it just says "GMT+00:00" for both // long and short names. We don't want this. The best we can do is use "UTC" // for everything (since we don't know how to say "Universal Coordinated Time" in // every language). // TODO: check CLDR doesn't actually have this somewhere. long_std = short_std = long_dst = short_dst = kUtc; } bool okay = setStringArrayElement(env, java_row.get(), 1, long_std) && setStringArrayElement(env, java_row.get(), 2, short_std) && setStringArrayElement(env, java_row.get(), 3, long_dst) && setStringArrayElement(env, java_row.get(), 4, short_dst); if (!okay) { return; } } }
static jobjectArray TimeZones_getZoneStringsImpl(JNIEnv* env, jclass, jstring localeName, jobjectArray timeZoneIds) { Locale locale = getLocale(env, localeName); // We could use TimeZone::getDisplayName, but that's even slower // because it creates a new SimpleDateFormat each time. // We're better off using SimpleDateFormat directly. // We can't use DateFormatSymbols::getZoneStrings because that // uses its own set of time zone ids and contains empty strings // instead of GMT offsets (a pity, because it's a bit faster than this code). UErrorCode status = U_ZERO_ERROR; UnicodeString longPattern("zzzz", 4, US_INV); SimpleDateFormat longFormat(longPattern, locale, status); // 'z' only uses "common" abbreviations. 'V' allows all known abbreviations. // For example, "PST" is in common use in en_US, but "CET" isn't. UnicodeString commonShortPattern("z", 1, US_INV); SimpleDateFormat shortFormat(commonShortPattern, locale, status); UnicodeString allShortPattern("V", 1, US_INV); SimpleDateFormat allShortFormat(allShortPattern, locale, status); UnicodeString utc("UTC", 3, US_INV); // TODO: use of fixed dates prevents us from using the correct historical name when formatting dates. // TODO: use of dates not in the current year could cause us to output obsoleted names. // 15th January 2008 UDate date1 = 1203105600000.0; // 15th July 2008 UDate date2 = 1218826800000.0; // In the first pass, we get the long names for the time zone. // We also get any commonly-used abbreviations. std::vector<TimeZoneNames> table; typedef std::map<UnicodeString, UnicodeString*> AbbreviationMap; AbbreviationMap usedAbbreviations; size_t idCount = env->GetArrayLength(timeZoneIds); for (size_t i = 0; i < idCount; ++i) { ScopedLocalRef<jstring> javaZoneId(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(timeZoneIds, i))); ScopedJavaUnicodeString zoneId(env, javaZoneId.get()); UnicodeString id(zoneId.unicodeString()); TimeZoneNames row; if (isUtc(id)) { // ICU doesn't have names for the UTC zones; it just says "GMT+00:00" for both // long and short names. We don't want this. The best we can do is use "UTC" // for everything (since we don't know how to say "Universal Coordinated Time"). row.tz = NULL; row.longStd = row.shortStd = row.longDst = row.shortDst = utc; table.push_back(row); usedAbbreviations[utc] = &utc; continue; } row.tz = TimeZone::createTimeZone(id); longFormat.setTimeZone(*row.tz); shortFormat.setTimeZone(*row.tz); int32_t daylightOffset; int32_t rawOffset; row.tz->getOffset(date1, false, rawOffset, daylightOffset, status); if (daylightOffset != 0) { // The TimeZone is reporting that we are in daylight time for the winter date. // The dates are for the wrong hemisphere, so swap them. row.standardDate = date2; row.daylightSavingDate = date1; } else { row.standardDate = date1; row.daylightSavingDate = date2; } longFormat.format(row.standardDate, row.longStd); shortFormat.format(row.standardDate, row.shortStd); if (row.tz->useDaylightTime()) { longFormat.format(row.daylightSavingDate, row.longDst); shortFormat.format(row.daylightSavingDate, row.shortDst); } else { row.longDst = row.longStd; row.shortDst = row.shortStd; } table.push_back(row); usedAbbreviations[row.shortStd] = &row.longStd; usedAbbreviations[row.shortDst] = &row.longDst; } // In the second pass, we create the Java String[][]. // We also look for any uncommon abbreviations that don't conflict with ones we've already seen. jobjectArray result = env->NewObjectArray(idCount, JniConstants::stringArrayClass, NULL); UnicodeString gmt("GMT", 3, US_INV); for (size_t i = 0; i < table.size(); ++i) { TimeZoneNames& row(table[i]); // Did we get a GMT offset instead of an abbreviation? if (row.shortStd.length() > 3 && row.shortStd.startsWith(gmt)) { // See if we can do better... UnicodeString uncommonStd, uncommonDst; allShortFormat.setTimeZone(*row.tz); allShortFormat.format(row.standardDate, uncommonStd); if (row.tz->useDaylightTime()) { allShortFormat.format(row.daylightSavingDate, uncommonDst); } else { uncommonDst = uncommonStd; } // If this abbreviation isn't already in use, we can use it. AbbreviationMap::iterator it = usedAbbreviations.find(uncommonStd); if (it == usedAbbreviations.end() || *(it->second) == row.longStd) { row.shortStd = uncommonStd; usedAbbreviations[row.shortStd] = &row.longStd; } it = usedAbbreviations.find(uncommonDst); if (it == usedAbbreviations.end() || *(it->second) == row.longDst) { row.shortDst = uncommonDst; usedAbbreviations[row.shortDst] = &row.longDst; } } // Fill in whatever we got. ScopedLocalRef<jobjectArray> javaRow(env, env->NewObjectArray(5, JniConstants::stringClass, NULL)); ScopedLocalRef<jstring> id(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(timeZoneIds, i))); env->SetObjectArrayElement(javaRow.get(), 0, id.get()); setStringArrayElement(env, javaRow.get(), 1, row.longStd); setStringArrayElement(env, javaRow.get(), 2, row.shortStd); setStringArrayElement(env, javaRow.get(), 3, row.longDst); setStringArrayElement(env, javaRow.get(), 4, row.shortDst); env->SetObjectArrayElement(result, i, javaRow.get()); delete row.tz; } return result; }