Home | History | Annotate | Download | only in icu
      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