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.Arrays; 20 import java.util.Comparator; 21 import java.util.HashMap; 22 import java.util.Locale; 23 import java.util.TimeZone; 24 import libcore.util.BasicLruCache; 25 26 /** 27 * Provides access to ICU's time zone data. 28 */ 29 public final class TimeZones { 30 private static final String[] availableTimeZones = 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(availableTimeZones.length); 63 } 64 65 @Override protected String[][] create(Locale locale) { 66 long start, nativeStart; 67 start = nativeStart = System.currentTimeMillis(); 68 String[][] result = getZoneStringsImpl(locale.toString(), availableTimeZones); 69 long nativeEnd = System.currentTimeMillis(); 70 internStrings(result); 71 // Ending up in this method too often is an easy way to make your app slow, so we ensure 72 // it's easy to tell from the log (a) what we were doing, (b) how long it took, and 73 // (c) that it's all ICU's fault. 74 long end = System.currentTimeMillis(); 75 long duration = end - start; 76 long nativeDuration = nativeEnd - nativeStart; 77 System.logI("Loaded time zone names for " + locale + " in " + duration + "ms" + 78 " (" + nativeDuration + "ms in ICU)"); 79 return result; 80 } 81 82 private synchronized void internStrings(String[][] result) { 83 for (int i = 0; i < result.length; ++i) { 84 for (int j = 1; j < NAME_COUNT; ++j) { 85 String original = result[i][j]; 86 String nonDuplicate = internTable.get(original); 87 if (nonDuplicate == null) { 88 internTable.put(original, original); 89 } else { 90 result[i][j] = nonDuplicate; 91 } 92 } 93 } 94 } 95 } 96 97 private static final Comparator<String[]> ZONE_STRINGS_COMPARATOR = new Comparator<String[]>() { 98 public int compare(String[] lhs, String[] rhs) { 99 return lhs[OLSON_NAME].compareTo(rhs[OLSON_NAME]); 100 } 101 }; 102 103 private TimeZones() {} 104 105 /** 106 * Returns the appropriate string from 'zoneStrings'. Used with getZoneStrings. 107 */ 108 public static String getDisplayName(String[][] zoneStrings, String id, boolean daylight, int style) { 109 String[] needle = new String[] { id }; 110 int index = Arrays.binarySearch(zoneStrings, needle, ZONE_STRINGS_COMPARATOR); 111 if (index >= 0) { 112 String[] row = zoneStrings[index]; 113 if (daylight) { 114 return (style == TimeZone.LONG) ? row[LONG_NAME_DST] : row[SHORT_NAME_DST]; 115 } else { 116 return (style == TimeZone.LONG) ? row[LONG_NAME] : row[SHORT_NAME]; 117 } 118 } 119 return null; 120 } 121 122 /** 123 * Returns an array of time zone strings, as used by DateFormatSymbols.getZoneStrings. 124 */ 125 public static String[][] getZoneStrings(Locale locale) { 126 if (locale == null) { 127 locale = Locale.getDefault(); 128 } 129 return cachedZoneStrings.get(locale); 130 } 131 132 /** 133 * Returns an array containing the time zone ids in use in the country corresponding to 134 * the given locale. This is not necessary for Java API, but is used by telephony as a 135 * fallback. 136 */ 137 public static String[] forLocale(Locale locale) { 138 return forCountryCode(locale.getCountry()); 139 } 140 141 private static native String[] forCountryCode(String countryCode); 142 private static native String[][] getZoneStringsImpl(String locale, String[] timeZoneIds); 143 } 144