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 com.android.icu.text.TimeZoneNamesNative; 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 28 /** 29 * Provides access to ICU's time zone name data. 30 * @hide 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 private static final int OLSON_NAME = 0; 39 private static final int LONG_NAME = 1; 40 private static final int SHORT_NAME = 2; 41 private static final int LONG_NAME_DST = 3; 42 private static final int SHORT_NAME_DST = 4; 43 private static final int NAME_COUNT = 5; 44 45 private static final ZoneStringsCache cachedZoneStrings = new ZoneStringsCache(); 46 47 private static class ZoneStringsCache extends BasicLruCache<Locale, String[][]> { ZoneStringsCache()48 public ZoneStringsCache() { 49 super(5); // Room for a handful of locales. 50 } 51 create(Locale locale)52 @Override protected String[][] create(Locale locale) { 53 long start = System.nanoTime(); 54 55 long nativeStart = System.nanoTime(); 56 String[][] result = TimeZoneNamesNative.getFilledZoneStrings(locale, availableTimeZoneIds); 57 long nativeEnd = System.nanoTime(); 58 59 addOffsetStrings(result); 60 internStrings(result); 61 // Ending up in this method too often is an easy way to make your app slow, so we ensure 62 // it's easy to tell from the log (a) what we were doing, (b) how long it took, and 63 // (c) that it's all ICU's fault. 64 long end = System.nanoTime(); 65 long nativeDuration = TimeUnit.NANOSECONDS.toMillis(nativeEnd - nativeStart); 66 long duration = TimeUnit.NANOSECONDS.toMillis(end - start); 67 System.logI("Loaded time zone names for \"" + locale + "\" in " + duration + "ms" + 68 " (" + nativeDuration + "ms in ICU)"); 69 return result; 70 } 71 72 /** 73 * Generate offset strings for cases where we don't have a name. Note that this is a 74 * potentially slow operation, as we need to load the timezone data for all affected 75 * time zones. 76 */ addOffsetStrings(String[][] result)77 private void addOffsetStrings(String[][] result) { 78 for (int i = 0; i < result.length; ++i) { 79 TimeZone tz = null; 80 for (int j = 1; j < NAME_COUNT; ++j) { 81 if (result[i][j] != null) { 82 continue; 83 } 84 if (tz == null) { 85 tz = TimeZone.getTimeZone(result[i][0]); 86 } 87 int offsetMillis = tz.getRawOffset(); 88 if (j == LONG_NAME_DST || j == SHORT_NAME_DST) { 89 offsetMillis += tz.getDSTSavings(); 90 } 91 result[i][j] = TimeZone.createGmtOffsetString( 92 /* includeGmt */ true, /*includeMinuteSeparator */true, offsetMillis); 93 } 94 } 95 } 96 97 // De-duplicate the strings (http://b/2672057). internStrings(String[][] result)98 private void internStrings(String[][] result) { 99 HashMap<String, String> internTable = new HashMap<String, String>(); 100 for (int i = 0; i < result.length; ++i) { 101 for (int j = 1; j < NAME_COUNT; ++j) { 102 String original = result[i][j]; 103 String nonDuplicate = internTable.get(original); 104 if (nonDuplicate == null) { 105 internTable.put(original, original); 106 } else { 107 result[i][j] = nonDuplicate; 108 } 109 } 110 } 111 } 112 } 113 114 private static final Comparator<String[]> ZONE_STRINGS_COMPARATOR = new Comparator<String[]>() { 115 public int compare(String[] lhs, String[] rhs) { 116 return lhs[OLSON_NAME].compareTo(rhs[OLSON_NAME]); 117 } 118 }; 119 TimeZoneNames()120 private TimeZoneNames() {} 121 122 /** 123 * Returns the appropriate string from 'zoneStrings'. Used with getZoneStrings. 124 */ getDisplayName(String[][] zoneStrings, String id, boolean daylight, int style)125 public static String getDisplayName(String[][] zoneStrings, String id, boolean daylight, int style) { 126 String[] needle = new String[] { id }; 127 int index = Arrays.binarySearch(zoneStrings, needle, ZONE_STRINGS_COMPARATOR); 128 if (index >= 0) { 129 String[] row = zoneStrings[index]; 130 if (daylight) { 131 return (style == TimeZone.LONG) ? row[LONG_NAME_DST] : row[SHORT_NAME_DST]; 132 } else { 133 return (style == TimeZone.LONG) ? row[LONG_NAME] : row[SHORT_NAME]; 134 } 135 } 136 return null; 137 } 138 139 /** 140 * Returns an array of time zone strings, as used by DateFormatSymbols.getZoneStrings. 141 */ getZoneStrings(Locale locale)142 public static String[][] getZoneStrings(Locale locale) { 143 if (locale == null) { 144 locale = Locale.getDefault(); 145 } 146 return cachedZoneStrings.get(locale); 147 } 148 149 /** 150 * A utility method to get display names in various {@param namesTypes} from 151 * ICU4J's {@param timeZoneNames}. 152 */ getDisplayNames(android.icu.text.TimeZoneNames timeZoneNames, String tzId, android.icu.text.TimeZoneNames.NameType[] nameTypes, long date, String[] names, int namesOffset)153 public static void getDisplayNames(android.icu.text.TimeZoneNames timeZoneNames, String tzId, 154 android.icu.text.TimeZoneNames.NameType[] nameTypes, long date, String[] names, 155 int namesOffset) { 156 for (int i = 0; i < nameTypes.length; i++) { 157 android.icu.text.TimeZoneNames.NameType nameType = nameTypes[i]; 158 names[namesOffset + i] = timeZoneNames.getDisplayName(tzId, nameType, date); 159 } 160 } 161 } 162