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