TimeZoneNames.java revision ac7cf58962995825464af08ae6fa5e006c94f3fa
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
17package libcore.icu;
18
19import java.util.Arrays;
20import java.util.Comparator;
21import java.util.HashMap;
22import java.util.Locale;
23import java.util.TimeZone;
24import libcore.util.BasicLruCache;
25
26/**
27 * Provides access to ICU's time zone name data.
28 */
29public final class TimeZoneNames {
30    private static final String[] availableTimeZoneIds = TimeZone.getAvailableIDs();
31
32    /*
33     * Offsets into the arrays returned by DateFormatSymbols.getZoneStrings.
34     */
35    public static final int OLSON_NAME = 0;
36    public static final int LONG_NAME = 1;
37    public static final int SHORT_NAME = 2;
38    public static final int LONG_NAME_DST = 3;
39    public static final int SHORT_NAME_DST = 4;
40    public static final int NAME_COUNT = 5;
41
42    private static final ZoneStringsCache cachedZoneStrings = new ZoneStringsCache();
43    static {
44        // Ensure that we pull in the zone strings for the root locale, en_US, and the
45        // user's default locale. (All devices must support the root locale and en_US,
46        // and they're used for various system things like HTTP headers.) Pre-populating
47        // the cache is especially useful on Android because we'll share this via the Zygote.
48        cachedZoneStrings.get(Locale.ROOT);
49        cachedZoneStrings.get(Locale.US);
50        cachedZoneStrings.get(Locale.getDefault());
51    }
52
53    public static class ZoneStringsCache extends BasicLruCache<Locale, String[][]> {
54        // De-duplicate the strings (http://b/2672057).
55        private final HashMap<String, String> internTable = new HashMap<String, String>();
56
57        public ZoneStringsCache() {
58            // We make room for all the time zones known to the system, since each set of strings
59            // isn't particularly large (and we remove duplicates), but is currently (Honeycomb)
60            // really expensive to compute.
61            // If you change this, you might want to change the scope of the intern table too.
62            super(availableTimeZoneIds.length);
63        }
64
65        @Override protected String[][] create(Locale locale) {
66            long start = System.currentTimeMillis();
67
68            // Set up the 2D array used to hold the names. The first column contains the Olson ids.
69            String[][] result = new String[availableTimeZoneIds.length][5];
70            for (int i = 0; i < availableTimeZoneIds.length; ++i) {
71                result[i][0] = availableTimeZoneIds[i];
72            }
73
74            long nativeStart = System.currentTimeMillis();
75            fillZoneStrings(locale.toString(), result);
76            long nativeEnd = System.currentTimeMillis();
77
78            internStrings(result);
79            // Ending up in this method too often is an easy way to make your app slow, so we ensure
80            // it's easy to tell from the log (a) what we were doing, (b) how long it took, and
81            // (c) that it's all ICU's fault.
82            long end = System.currentTimeMillis();
83            long nativeDuration = nativeEnd - nativeStart;
84            long duration = end - start;
85            System.logI("Loaded time zone names for \"" + locale + "\" in " + duration + "ms" +
86                    " (" + nativeDuration + "ms in ICU)");
87            return result;
88        }
89
90        private synchronized void internStrings(String[][] result) {
91            for (int i = 0; i < result.length; ++i) {
92                for (int j = 1; j < NAME_COUNT; ++j) {
93                    String original = result[i][j];
94                    String nonDuplicate = internTable.get(original);
95                    if (nonDuplicate == null) {
96                        internTable.put(original, original);
97                    } else {
98                        result[i][j] = nonDuplicate;
99                    }
100                }
101            }
102        }
103    }
104
105    private static final Comparator<String[]> ZONE_STRINGS_COMPARATOR = new Comparator<String[]>() {
106        public int compare(String[] lhs, String[] rhs) {
107            return lhs[OLSON_NAME].compareTo(rhs[OLSON_NAME]);
108        }
109    };
110
111    private TimeZoneNames() {}
112
113    /**
114     * Returns the appropriate string from 'zoneStrings'. Used with getZoneStrings.
115     */
116    public static String getDisplayName(String[][] zoneStrings, String id, boolean daylight, int style) {
117        String[] needle = new String[] { id };
118        int index = Arrays.binarySearch(zoneStrings, needle, ZONE_STRINGS_COMPARATOR);
119        if (index >= 0) {
120            String[] row = zoneStrings[index];
121            if (daylight) {
122                return (style == TimeZone.LONG) ? row[LONG_NAME_DST] : row[SHORT_NAME_DST];
123            } else {
124                return (style == TimeZone.LONG) ? row[LONG_NAME] : row[SHORT_NAME];
125            }
126        }
127        return null;
128    }
129
130    /**
131     * Returns an array of time zone strings, as used by DateFormatSymbols.getZoneStrings.
132     */
133    public static String[][] getZoneStrings(Locale locale) {
134        if (locale == null) {
135            locale = Locale.getDefault();
136        }
137        return cachedZoneStrings.get(locale);
138    }
139
140    /**
141     * Returns an array containing the time zone ids in use in the country corresponding to
142     * the given locale. This is not necessary for Java API, but is used by telephony as a
143     * fallback.
144     */
145    public static String[] forLocale(Locale locale) {
146        return forCountryCode(locale.getCountry());
147    }
148
149    private static native String[] forCountryCode(String countryCode);
150    private static native void fillZoneStrings(String locale, String[][] result);
151}
152