• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "TimeZones"
18 
19 #include <map>
20 #include <vector>
21 
22 #include "JNIHelp.h"
23 #include "JniConstants.h"
24 #include "JniException.h"
25 #include "ScopedJavaUnicodeString.h"
26 #include "ScopedLocalRef.h"
27 #include "ScopedUtfChars.h"
28 #include "UniquePtr.h"
29 #include "unicode/smpdtfmt.h"
30 #include "unicode/timezone.h"
31 
32 extern Locale getLocale(JNIEnv* env, jstring localeName);
33 
TimeZones_forCountryCode(JNIEnv * env,jclass,jstring countryCode)34 static jobjectArray TimeZones_forCountryCode(JNIEnv* env, jclass, jstring countryCode) {
35     ScopedUtfChars countryChars(env, countryCode);
36     if (countryChars.c_str() == NULL) {
37         return NULL;
38     }
39 
40     UniquePtr<StringEnumeration> ids(TimeZone::createEnumeration(countryChars.c_str()));
41     if (ids.get() == NULL) {
42         return NULL;
43     }
44     UErrorCode status = U_ZERO_ERROR;
45     int32_t idCount = ids->count(status);
46     if (maybeThrowIcuException(env, status)) {
47         return NULL;
48     }
49 
50     jobjectArray result = env->NewObjectArray(idCount, JniConstants::stringClass, NULL);
51     for (int32_t i = 0; i < idCount; ++i) {
52         const UnicodeString* id = ids->snext(status);
53         if (maybeThrowIcuException(env, status)) {
54             return NULL;
55         }
56         ScopedLocalRef<jstring> idString(env, env->NewString(id->getBuffer(), id->length()));
57         env->SetObjectArrayElement(result, i, idString.get());
58     }
59     return result;
60 }
61 
62 struct TimeZoneNames {
63     TimeZone* tz;
64 
65     UnicodeString longStd;
66     UnicodeString shortStd;
67     UnicodeString longDst;
68     UnicodeString shortDst;
69 
70     UDate standardDate;
71     UDate daylightSavingDate;
72 };
73 
setStringArrayElement(JNIEnv * env,jobjectArray array,int i,const UnicodeString & s)74 static void setStringArrayElement(JNIEnv* env, jobjectArray array, int i, const UnicodeString& s) {
75     ScopedLocalRef<jstring> javaString(env, env->NewString(s.getBuffer(), s.length()));
76     env->SetObjectArrayElement(array, i, javaString.get());
77 }
78 
isUtc(const UnicodeString & id)79 static bool isUtc(const UnicodeString& id) {
80     static UnicodeString etcUct("Etc/UCT", 7, US_INV);
81     static UnicodeString etcUtc("Etc/UTC", 7, US_INV);
82     static UnicodeString etcUniversal("Etc/Universal", 13, US_INV);
83     static UnicodeString etcZulu("Etc/Zulu", 8, US_INV);
84 
85     static UnicodeString uct("UCT", 3, US_INV);
86     static UnicodeString utc("UTC", 3, US_INV);
87     static UnicodeString universal("Universal", 9, US_INV);
88     static UnicodeString zulu("Zulu", 4, US_INV);
89 
90     return id == etcUct || id == etcUtc || id == etcUniversal || id == etcZulu ||
91             id == uct || id == utc || id == universal || id == zulu;
92 }
93 
TimeZones_getZoneStringsImpl(JNIEnv * env,jclass,jstring localeName,jobjectArray timeZoneIds)94 static jobjectArray TimeZones_getZoneStringsImpl(JNIEnv* env, jclass, jstring localeName, jobjectArray timeZoneIds) {
95     Locale locale = getLocale(env, localeName);
96 
97     // We could use TimeZone::getDisplayName, but that's even slower
98     // because it creates a new SimpleDateFormat each time.
99     // We're better off using SimpleDateFormat directly.
100 
101     // We can't use DateFormatSymbols::getZoneStrings because that
102     // uses its own set of time zone ids and contains empty strings
103     // instead of GMT offsets (a pity, because it's a bit faster than this code).
104 
105     UErrorCode status = U_ZERO_ERROR;
106     UnicodeString longPattern("zzzz", 4, US_INV);
107     SimpleDateFormat longFormat(longPattern, locale, status);
108     // 'z' only uses "common" abbreviations. 'V' allows all known abbreviations.
109     // For example, "PST" is in common use in en_US, but "CET" isn't.
110     UnicodeString commonShortPattern("z", 1, US_INV);
111     SimpleDateFormat shortFormat(commonShortPattern, locale, status);
112     UnicodeString allShortPattern("V", 1, US_INV);
113     SimpleDateFormat allShortFormat(allShortPattern, locale, status);
114 
115     UnicodeString utc("UTC", 3, US_INV);
116 
117     // TODO: use of fixed dates prevents us from using the correct historical name when formatting dates.
118     // TODO: use of dates not in the current year could cause us to output obsoleted names.
119     // 15th January 2008
120     UDate date1 = 1203105600000.0;
121     // 15th July 2008
122     UDate date2 = 1218826800000.0;
123 
124     // In the first pass, we get the long names for the time zone.
125     // We also get any commonly-used abbreviations.
126     std::vector<TimeZoneNames> table;
127     typedef std::map<UnicodeString, UnicodeString*> AbbreviationMap;
128     AbbreviationMap usedAbbreviations;
129     size_t idCount = env->GetArrayLength(timeZoneIds);
130     for (size_t i = 0; i < idCount; ++i) {
131         ScopedLocalRef<jstring> javaZoneId(env,
132                 reinterpret_cast<jstring>(env->GetObjectArrayElement(timeZoneIds, i)));
133         ScopedJavaUnicodeString zoneId(env, javaZoneId.get());
134         UnicodeString id(zoneId.unicodeString());
135 
136         TimeZoneNames row;
137         if (isUtc(id)) {
138             // ICU doesn't have names for the UTC zones; it just says "GMT+00:00" for both
139             // long and short names. We don't want this. The best we can do is use "UTC"
140             // for everything (since we don't know how to say "Universal Coordinated Time").
141             row.tz = NULL;
142             row.longStd = row.shortStd = row.longDst = row.shortDst = utc;
143             table.push_back(row);
144             usedAbbreviations[utc] = &utc;
145             continue;
146         }
147 
148         row.tz = TimeZone::createTimeZone(id);
149         longFormat.setTimeZone(*row.tz);
150         shortFormat.setTimeZone(*row.tz);
151 
152         int32_t daylightOffset;
153         int32_t rawOffset;
154         row.tz->getOffset(date1, false, rawOffset, daylightOffset, status);
155         if (daylightOffset != 0) {
156             // The TimeZone is reporting that we are in daylight time for the winter date.
157             // The dates are for the wrong hemisphere, so swap them.
158             row.standardDate = date2;
159             row.daylightSavingDate = date1;
160         } else {
161             row.standardDate = date1;
162             row.daylightSavingDate = date2;
163         }
164 
165         longFormat.format(row.standardDate, row.longStd);
166         shortFormat.format(row.standardDate, row.shortStd);
167         if (row.tz->useDaylightTime()) {
168             longFormat.format(row.daylightSavingDate, row.longDst);
169             shortFormat.format(row.daylightSavingDate, row.shortDst);
170         } else {
171             row.longDst = row.longStd;
172             row.shortDst = row.shortStd;
173         }
174 
175         table.push_back(row);
176         usedAbbreviations[row.shortStd] = &row.longStd;
177         usedAbbreviations[row.shortDst] = &row.longDst;
178     }
179 
180     // In the second pass, we create the Java String[][].
181     // We also look for any uncommon abbreviations that don't conflict with ones we've already seen.
182     jobjectArray result = env->NewObjectArray(idCount, JniConstants::stringArrayClass, NULL);
183     UnicodeString gmt("GMT", 3, US_INV);
184     for (size_t i = 0; i < table.size(); ++i) {
185         TimeZoneNames& row(table[i]);
186         // Did we get a GMT offset instead of an abbreviation?
187         if (row.shortStd.length() > 3 && row.shortStd.startsWith(gmt)) {
188             // See if we can do better...
189             UnicodeString uncommonStd, uncommonDst;
190             allShortFormat.setTimeZone(*row.tz);
191             allShortFormat.format(row.standardDate, uncommonStd);
192             if (row.tz->useDaylightTime()) {
193                 allShortFormat.format(row.daylightSavingDate, uncommonDst);
194             } else {
195                 uncommonDst = uncommonStd;
196             }
197 
198             // If this abbreviation isn't already in use, we can use it.
199             AbbreviationMap::iterator it = usedAbbreviations.find(uncommonStd);
200             if (it == usedAbbreviations.end() || *(it->second) == row.longStd) {
201                 row.shortStd = uncommonStd;
202                 usedAbbreviations[row.shortStd] = &row.longStd;
203             }
204             it = usedAbbreviations.find(uncommonDst);
205             if (it == usedAbbreviations.end() || *(it->second) == row.longDst) {
206                 row.shortDst = uncommonDst;
207                 usedAbbreviations[row.shortDst] = &row.longDst;
208             }
209         }
210         // Fill in whatever we got.
211         ScopedLocalRef<jobjectArray> javaRow(env, env->NewObjectArray(5, JniConstants::stringClass, NULL));
212         ScopedLocalRef<jstring> id(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(timeZoneIds, i)));
213         env->SetObjectArrayElement(javaRow.get(), 0, id.get());
214         setStringArrayElement(env, javaRow.get(), 1, row.longStd);
215         setStringArrayElement(env, javaRow.get(), 2, row.shortStd);
216         setStringArrayElement(env, javaRow.get(), 3, row.longDst);
217         setStringArrayElement(env, javaRow.get(), 4, row.shortDst);
218         env->SetObjectArrayElement(result, i, javaRow.get());
219         delete row.tz;
220     }
221 
222     return result;
223 }
224 
225 static JNINativeMethod gMethods[] = {
226     NATIVE_METHOD(TimeZones, forCountryCode, "(Ljava/lang/String;)[Ljava/lang/String;"),
227     NATIVE_METHOD(TimeZones, getZoneStringsImpl, "(Ljava/lang/String;[Ljava/lang/String;)[[Ljava/lang/String;"),
228 };
register_libcore_icu_TimeZones(JNIEnv * env)229 void register_libcore_icu_TimeZones(JNIEnv* env) {
230     jniRegisterNativeMethods(env, "libcore/icu/TimeZones", gMethods, NELEM(gMethods));
231 }
232