/* GENERATED SOURCE. DO NOT MODIFY. */ // © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* * Copyright (C) 2011-2016, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* */ package android.icu.impl; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.MissingResourceException; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import android.icu.impl.TextTrieMap.ResultHandler; import android.icu.text.TimeZoneNames; import android.icu.util.TimeZone; import android.icu.util.TimeZone.SystemTimeZoneType; import android.icu.util.ULocale; import android.icu.util.UResourceBundle; /** * The standard ICU implementation of TimeZoneNames * @hide Only a subset of ICU is exposed in Android */ public class TimeZoneNamesImpl extends TimeZoneNames { private static final long serialVersionUID = -2179814848495897472L; private static final String ZONE_STRINGS_BUNDLE = "zoneStrings"; private static final String MZ_PREFIX = "meta:"; private static volatile Set METAZONE_IDS; private static final TZ2MZsCache TZ_TO_MZS_CACHE = new TZ2MZsCache(); private static final MZ2TZsCache MZ_TO_TZS_CACHE = new MZ2TZsCache(); private transient ICUResourceBundle _zoneStrings; // These are hard cache. We create only one TimeZoneNamesImpl per locale // and it's stored in SoftCache, so we do not need to worry about the // footprint much. private transient ConcurrentHashMap _mzNamesMap; private transient ConcurrentHashMap _tzNamesMap; private transient boolean _namesFullyLoaded; private transient TextTrieMap _namesTrie; private transient boolean _namesTrieFullyLoaded; public TimeZoneNamesImpl(ULocale locale) { initialize(locale); } /* (non-Javadoc) * @see android.icu.text.TimeZoneNames#getAvailableMetaZoneIDs() */ @Override public Set getAvailableMetaZoneIDs() { return _getAvailableMetaZoneIDs(); } static Set _getAvailableMetaZoneIDs() { if (METAZONE_IDS == null) { synchronized (TimeZoneNamesImpl.class) { if (METAZONE_IDS == null) { UResourceBundle bundle = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "metaZones"); UResourceBundle mapTimezones = bundle.get("mapTimezones"); Set keys = mapTimezones.keySet(); METAZONE_IDS = Collections.unmodifiableSet(keys); } } } return METAZONE_IDS; } /* (non-Javadoc) * @see android.icu.text.TimeZoneNames#getAvailableMetaZoneIDs(java.lang.String) */ @Override public Set getAvailableMetaZoneIDs(String tzID) { return _getAvailableMetaZoneIDs(tzID); } static Set _getAvailableMetaZoneIDs(String tzID) { if (tzID == null || tzID.length() == 0) { return Collections.emptySet(); } List maps = TZ_TO_MZS_CACHE.getInstance(tzID, tzID); if (maps.isEmpty()) { return Collections.emptySet(); } Set mzIDs = new HashSet(maps.size()); for (MZMapEntry map : maps) { mzIDs.add(map.mzID()); } // make it unmodifiable because of the API contract. We may cache the results in futre. return Collections.unmodifiableSet(mzIDs); } /* (non-Javadoc) * @see android.icu.text.TimeZoneNames#getMetaZoneID(java.lang.String, long) */ @Override public String getMetaZoneID(String tzID, long date) { return _getMetaZoneID(tzID, date); } static String _getMetaZoneID(String tzID, long date) { if (tzID == null || tzID.length() == 0) { return null; } String mzID = null; List maps = TZ_TO_MZS_CACHE.getInstance(tzID, tzID); for (MZMapEntry map : maps) { if (date >= map.from() && date < map.to()) { mzID = map.mzID(); break; } } return mzID; } /* (non-Javadoc) * @see android.icu.text.TimeZoneNames#getReferenceZoneID(java.lang.String, java.lang.String) */ @Override public String getReferenceZoneID(String mzID, String region) { return _getReferenceZoneID(mzID, region); } static String _getReferenceZoneID(String mzID, String region) { if (mzID == null || mzID.length() == 0) { return null; } String refID = null; Map regionTzMap = MZ_TO_TZS_CACHE.getInstance(mzID, mzID); if (!regionTzMap.isEmpty()) { refID = regionTzMap.get(region); if (refID == null) { refID = regionTzMap.get("001"); } } return refID; } /* * (non-Javadoc) * @see android.icu.text.TimeZoneNames#getMetaZoneDisplayName(java.lang.String, android.icu.text.TimeZoneNames.NameType) */ @Override public String getMetaZoneDisplayName(String mzID, NameType type) { if (mzID == null || mzID.length() == 0) { return null; } return loadMetaZoneNames(mzID).getName(type); } /* * (non-Javadoc) * @see android.icu.text.TimeZoneNames#getTimeZoneDisplayName(java.lang.String, android.icu.text.TimeZoneNames.NameType) */ @Override public String getTimeZoneDisplayName(String tzID, NameType type) { if (tzID == null || tzID.length() == 0) { return null; } return loadTimeZoneNames(tzID).getName(type); } /* (non-Javadoc) * @see android.icu.text.TimeZoneNames#getExemplarLocationName(java.lang.String) */ @Override public String getExemplarLocationName(String tzID) { if (tzID == null || tzID.length() == 0) { return null; } String locName = loadTimeZoneNames(tzID).getName(NameType.EXEMPLAR_LOCATION); return locName; } /* (non-Javadoc) * @see android.icu.text.TimeZoneNames#find(java.lang.CharSequence, int, java.util.Set) */ @Override public synchronized Collection find(CharSequence text, int start, EnumSet nameTypes) { if (text == null || text.length() == 0 || start < 0 || start >= text.length()) { throw new IllegalArgumentException("bad input text or range"); } NameSearchHandler handler = new NameSearchHandler(nameTypes); Collection matches; // First try of lookup. matches = doFind(handler, text, start); if (matches != null) { return matches; } // All names are not yet loaded into the trie. // We may have loaded names for formatting several time zones, // and might be parsing one of those. // Populate the parsing trie from all of the already-loaded names. addAllNamesIntoTrie(); // Second try of lookup. matches = doFind(handler, text, start); if (matches != null) { return matches; } // There are still some names we haven't loaded into the trie yet. // Load everything now. internalLoadAllDisplayNames(); // Set default time zone location names // for time zones without explicit display names. // TODO: Should this logic be moved into internalLoadAllDisplayNames? Set tzIDs = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL, null, null); for (String tzID : tzIDs) { if (!_tzNamesMap.containsKey(tzID)) { ZNames.createTimeZoneAndPutInCache(_tzNamesMap, null, tzID); } } addAllNamesIntoTrie(); _namesTrieFullyLoaded = true; // Third try: we must return this one. return doFind(handler, text, start); } private Collection doFind(NameSearchHandler handler, CharSequence text, int start) { handler.resetResults(); _namesTrie.find(text, start, handler); if (handler.getMaxMatchLen() == (text.length() - start) || _namesTrieFullyLoaded) { return handler.getMatches(); } return null; } @Override public synchronized void loadAllDisplayNames() { internalLoadAllDisplayNames(); } @Override public void getDisplayNames(String tzID, NameType[] types, long date, String[] dest, int destOffset) { if (tzID == null || tzID.length() == 0) { return; } ZNames tzNames = loadTimeZoneNames(tzID); ZNames mzNames = null; for (int i = 0; i < types.length; ++i) { NameType type = types[i]; String name = tzNames.getName(type); if (name == null) { if (mzNames == null) { String mzID = getMetaZoneID(tzID, date); if (mzID == null || mzID.length() == 0) { mzNames = ZNames.EMPTY_ZNAMES; } else { mzNames = loadMetaZoneNames(mzID); } } name = mzNames.getName(type); } dest[destOffset + i] = name; } } /** Caller must synchronize. */ private void internalLoadAllDisplayNames() { if (!_namesFullyLoaded) { _namesFullyLoaded = true; new ZoneStringsLoader().load(); } } /** Caller must synchronize. */ private void addAllNamesIntoTrie() { for (Map.Entry entry : _tzNamesMap.entrySet()) { entry.getValue().addAsTimeZoneIntoTrie(entry.getKey(), _namesTrie); } for (Map.Entry entry : _mzNamesMap.entrySet()) { entry.getValue().addAsMetaZoneIntoTrie(entry.getKey(), _namesTrie); } } /** * Loads all meta zone and time zone names for this TimeZoneNames' locale. */ private final class ZoneStringsLoader extends UResource.Sink { /** * Prepare for several hundred time zones and meta zones. * _zoneStrings.getSize() is ineffective in a sparsely populated locale like en-GB. */ private static final int INITIAL_NUM_ZONES = 300; private HashMap keyToLoader = new HashMap(INITIAL_NUM_ZONES); private StringBuilder sb = new StringBuilder(32); /** Caller must synchronize. */ void load() { _zoneStrings.getAllItemsWithFallback("", this); for (Map.Entry entry : keyToLoader.entrySet()) { ZNamesLoader loader = entry.getValue(); if (loader == ZNamesLoader.DUMMY_LOADER) { continue; } UResource.Key key = entry.getKey(); if (isMetaZone(key)) { String mzID = mzIDFromKey(key); ZNames.createMetaZoneAndPutInCache(_mzNamesMap, loader.getNames(), mzID); } else { String tzID = tzIDFromKey(key); ZNames.createTimeZoneAndPutInCache(_tzNamesMap, loader.getNames(), tzID); } } } @Override public void put(UResource.Key key, UResource.Value value, boolean noFallback) { UResource.Table timeZonesTable = value.getTable(); for (int j = 0; timeZonesTable.getKeyAndValue(j, key, value); ++j) { assert !value.isNoInheritanceMarker(); if (value.getType() == UResourceBundle.TABLE) { consumeNamesTable(key, value, noFallback); } else { // Ignore fields that aren't tables (e.g., fallbackFormat and regionFormatStandard). // All time zone fields are tables. } } } private void consumeNamesTable(UResource.Key key, UResource.Value value, boolean noFallback) { ZNamesLoader loader = keyToLoader.get(key); if (loader == null) { if (isMetaZone(key)) { String mzID = mzIDFromKey(key); if (_mzNamesMap.containsKey(mzID)) { // We have already loaded the names for this meta zone. loader = ZNamesLoader.DUMMY_LOADER; } else { loader = new ZNamesLoader(); } } else { String tzID = tzIDFromKey(key); if (_tzNamesMap.containsKey(tzID)) { // We have already loaded the names for this time zone. loader = ZNamesLoader.DUMMY_LOADER; } else { loader = new ZNamesLoader(); } } UResource.Key newKey = createKey(key); keyToLoader.put(newKey, loader); } if (loader != ZNamesLoader.DUMMY_LOADER) { // Let the ZNamesLoader consume the names table. loader.put(key, value, noFallback); } } UResource.Key createKey(UResource.Key key) { return key.clone(); } boolean isMetaZone(UResource.Key key) { return key.startsWith(MZ_PREFIX); } /** * Equivalent to key.substring(MZ_PREFIX.length()) * except reuses our StringBuilder. */ private String mzIDFromKey(UResource.Key key) { sb.setLength(0); for (int i = MZ_PREFIX.length(); i < key.length(); ++i) { sb.append(key.charAt(i)); } return sb.toString(); } private String tzIDFromKey(UResource.Key key) { sb.setLength(0); for (int i = 0; i < key.length(); ++i) { char c = key.charAt(i); if (c == ':') { c = '/'; } sb.append(c); } return sb.toString(); } } /** * Initialize the transient fields, called from the constructor and * readObject. * * @param locale The locale */ private void initialize(ULocale locale) { ICUResourceBundle bundle = (ICUResourceBundle)ICUResourceBundle.getBundleInstance( ICUData.ICU_ZONE_BASE_NAME, locale); _zoneStrings = (ICUResourceBundle)bundle.get(ZONE_STRINGS_BUNDLE); // TODO: Access is synchronized, can we use a non-concurrent map? _tzNamesMap = new ConcurrentHashMap(); _mzNamesMap = new ConcurrentHashMap(); _namesFullyLoaded = false; _namesTrie = new TextTrieMap(true); _namesTrieFullyLoaded = false; // Preload zone strings for the default time zone TimeZone tz = TimeZone.getDefault(); String tzCanonicalID = ZoneMeta.getCanonicalCLDRID(tz); if (tzCanonicalID != null) { loadStrings(tzCanonicalID); } } /** * Load all strings used by the specified time zone. * This is called from the initializer to load default zone's * strings. * @param tzCanonicalID the canonical time zone ID */ private synchronized void loadStrings(String tzCanonicalID) { if (tzCanonicalID == null || tzCanonicalID.length() == 0) { return; } loadTimeZoneNames(tzCanonicalID); Set mzIDs = getAvailableMetaZoneIDs(tzCanonicalID); for (String mzID : mzIDs) { loadMetaZoneNames(mzID); } } /* * The custom serialization method. * This implementation only preserve locale object used for the names. */ private void writeObject(ObjectOutputStream out) throws IOException { ULocale locale = _zoneStrings.getULocale(); out.writeObject(locale); } /* * The custom deserialization method. * This implementation only read locale object used by the object. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { ULocale locale = (ULocale)in.readObject(); initialize(locale); } /** * Returns a set of names for the given meta zone ID. This method loads * the set of names into the internal map and trie for future references. * @param mzID the meta zone ID * @return An instance of ZNames that includes a set of meta zone display names. */ private synchronized ZNames loadMetaZoneNames(String mzID) { ZNames mznames = _mzNamesMap.get(mzID); if (mznames == null) { ZNamesLoader loader = new ZNamesLoader(); loader.loadMetaZone(_zoneStrings, mzID); mznames = ZNames.createMetaZoneAndPutInCache(_mzNamesMap, loader.getNames(), mzID); } return mznames; } /** * Returns a set of names for the given time zone ID. This method loads * the set of names into the internal map and trie for future references. * @param tzID the canonical time zone ID * @return An instance of ZNames that includes a set of time zone display names. */ private synchronized ZNames loadTimeZoneNames(String tzID) { ZNames tznames = _tzNamesMap.get(tzID); if (tznames == null) { ZNamesLoader loader = new ZNamesLoader(); loader.loadTimeZone(_zoneStrings, tzID); tznames = ZNames.createTimeZoneAndPutInCache(_tzNamesMap, loader.getNames(), tzID); } return tznames; } /** * An instance of NameInfo is stored in the zone names trie. */ private static class NameInfo { String tzID; String mzID; NameType type; } /** * NameSearchHandler is used for collecting name matches. */ private static class NameSearchHandler implements ResultHandler { private EnumSet _nameTypes; private Collection _matches; private int _maxMatchLen; NameSearchHandler(EnumSet nameTypes) { _nameTypes = nameTypes; } /* (non-Javadoc) * @see android.icu.impl.TextTrieMap.ResultHandler#handlePrefixMatch(int, java.util.Iterator) */ @Override public boolean handlePrefixMatch(int matchLength, Iterator values) { while (values.hasNext()) { NameInfo ninfo = values.next(); if (_nameTypes != null && !_nameTypes.contains(ninfo.type)) { continue; } MatchInfo minfo; if (ninfo.tzID != null) { minfo = new MatchInfo(ninfo.type, ninfo.tzID, null, matchLength); } else { assert(ninfo.mzID != null); minfo = new MatchInfo(ninfo.type, null, ninfo.mzID, matchLength); } if (_matches == null) { _matches = new LinkedList(); } _matches.add(minfo); if (matchLength > _maxMatchLen) { _maxMatchLen = matchLength; } } return true; } /** * Returns the match results * @return the match results */ public Collection getMatches() { if (_matches == null) { return Collections.emptyList(); } return _matches; } /** * Returns the maximum match length, or 0 if no match was found * @return the maximum match length */ public int getMaxMatchLen() { return _maxMatchLen; } /** * Resets the match results */ public void resetResults() { _matches = null; _maxMatchLen = 0; } } private static final class ZNamesLoader extends UResource.Sink { private String[] names; /** * Does not load any names, for no-fallback handling. */ private static ZNamesLoader DUMMY_LOADER = new ZNamesLoader(); void loadMetaZone(ICUResourceBundle zoneStrings, String mzID) { String key = MZ_PREFIX + mzID; loadNames(zoneStrings, key); } void loadTimeZone(ICUResourceBundle zoneStrings, String tzID) { String key = tzID.replace('/', ':'); loadNames(zoneStrings, key); } void loadNames(ICUResourceBundle zoneStrings, String key) { assert zoneStrings != null; assert key != null; assert key.length() > 0; // Reset names so that this instance can be used to load data multiple times. names = null; try { zoneStrings.getAllItemsWithFallback(key, this); } catch (MissingResourceException e) { } } private static ZNames.NameTypeIndex nameTypeIndexFromKey(UResource.Key key) { // Avoid key.toString() object creation. if (key.length() != 2) { return null; } char c0 = key.charAt(0); char c1 = key.charAt(1); if (c0 == 'l') { return c1 == 'g' ? ZNames.NameTypeIndex.LONG_GENERIC : c1 == 's' ? ZNames.NameTypeIndex.LONG_STANDARD : c1 == 'd' ? ZNames.NameTypeIndex.LONG_DAYLIGHT : null; } else if (c0 == 's') { return c1 == 'g' ? ZNames.NameTypeIndex.SHORT_GENERIC : c1 == 's' ? ZNames.NameTypeIndex.SHORT_STANDARD : c1 == 'd' ? ZNames.NameTypeIndex.SHORT_DAYLIGHT : null; } else if (c0 == 'e' && c1 == 'c') { return ZNames.NameTypeIndex.EXEMPLAR_LOCATION; } return null; } private void setNameIfEmpty(UResource.Key key, UResource.Value value) { if (names == null) { names = new String[ZNames.NUM_NAME_TYPES]; } ZNames.NameTypeIndex index = nameTypeIndexFromKey(key); if (index == null) { return; } assert index.ordinal() < ZNames.NUM_NAME_TYPES; if (names[index.ordinal()] == null) { names[index.ordinal()] = value.getString(); } } @Override public void put(UResource.Key key, UResource.Value value, boolean noFallback) { UResource.Table namesTable = value.getTable(); for (int i = 0; namesTable.getKeyAndValue(i, key, value); ++i) { assert value.getType() == UResourceBundle.STRING; setNameIfEmpty(key, value); // could be value.isNoInheritanceMarker() } } private String[] getNames() { if (Utility.sameObjects(names, null)) { return null; } int length = 0; for (int i = 0; i < ZNames.NUM_NAME_TYPES; ++i) { String name = names[i]; if (name != null) { if (name.equals(ICUResourceBundle.NO_INHERITANCE_MARKER)) { names[i] = null; } else { length = i + 1; } } } String[] result; if (length == ZNames.NUM_NAME_TYPES) { // Return the full array if the last name is set. result = names; } else if (length == 0) { // Return null instead of a zero-length array. result = null; } else { // Return a shorter array for permanent storage. // Copy all names into the minimal array. result = Arrays.copyOfRange(names, 0, length); } return result; } } /** * This class stores name data for a meta zone or time zone. */ private static class ZNames { /** * Private enum corresponding to the public TimeZoneNames::NameType for the order in * which fields are stored in a ZNames instance. EXEMPLAR_LOCATION is stored first * for efficiency. */ private static enum NameTypeIndex { EXEMPLAR_LOCATION, LONG_GENERIC, LONG_STANDARD, LONG_DAYLIGHT, SHORT_GENERIC, SHORT_STANDARD, SHORT_DAYLIGHT; static final NameTypeIndex values[] = values(); }; public static final int NUM_NAME_TYPES = 7; private static int getNameTypeIndex(NameType type) { switch (type) { case EXEMPLAR_LOCATION: return NameTypeIndex.EXEMPLAR_LOCATION.ordinal(); case LONG_GENERIC: return NameTypeIndex.LONG_GENERIC.ordinal(); case LONG_STANDARD: return NameTypeIndex.LONG_STANDARD.ordinal(); case LONG_DAYLIGHT: return NameTypeIndex.LONG_DAYLIGHT.ordinal(); case SHORT_GENERIC: return NameTypeIndex.SHORT_GENERIC.ordinal(); case SHORT_STANDARD: return NameTypeIndex.SHORT_STANDARD.ordinal(); case SHORT_DAYLIGHT: return NameTypeIndex.SHORT_DAYLIGHT.ordinal(); default: throw new AssertionError("No NameTypeIndex match for " + type); } } private static NameType getNameType(int index) { switch (NameTypeIndex.values[index]) { case EXEMPLAR_LOCATION: return NameType.EXEMPLAR_LOCATION; case LONG_GENERIC: return NameType.LONG_GENERIC; case LONG_STANDARD: return NameType.LONG_STANDARD; case LONG_DAYLIGHT: return NameType.LONG_DAYLIGHT; case SHORT_GENERIC: return NameType.SHORT_GENERIC; case SHORT_STANDARD: return NameType.SHORT_STANDARD; case SHORT_DAYLIGHT: return NameType.SHORT_DAYLIGHT; default: throw new AssertionError("No NameType match for " + index); } } static final ZNames EMPTY_ZNAMES = new ZNames(null); // A meta zone names instance never has an exemplar location string. private static final int EX_LOC_INDEX = NameTypeIndex.EXEMPLAR_LOCATION.ordinal(); private String[] _names; private boolean didAddIntoTrie; protected ZNames(String[] names) { _names = names; didAddIntoTrie = names == null; } public static ZNames createMetaZoneAndPutInCache(Map cache, String[] names, String mzID) { String key = mzID.intern(); ZNames value; if (names == null) { value = EMPTY_ZNAMES; } else { value = new ZNames(names); } cache.put(key, value); return value; } public static ZNames createTimeZoneAndPutInCache(Map cache, String[] names, String tzID) { // For time zones, check that the exemplar city name is populated. If necessary, use // "getDefaultExemplarLocationName" to extract it from the time zone name. names = (names == null) ? new String[EX_LOC_INDEX + 1] : names; if (names[EX_LOC_INDEX] == null) { names[EX_LOC_INDEX] = getDefaultExemplarLocationName(tzID); } String key = tzID.intern(); ZNames value = new ZNames(names); cache.put(key, value); return value; } public String getName(NameType type) { int index = getNameTypeIndex(type); if (_names != null && index < _names.length) { return _names[index]; } else { return null; } } public void addAsMetaZoneIntoTrie(String mzID, TextTrieMap trie) { addNamesIntoTrie(mzID, null, trie); } public void addAsTimeZoneIntoTrie(String tzID, TextTrieMap trie) { addNamesIntoTrie(null, tzID, trie); } private void addNamesIntoTrie(String mzID, String tzID, TextTrieMap trie) { if (_names == null || didAddIntoTrie) { return; } didAddIntoTrie = true; for (int i = 0; i < _names.length; ++i) { String name = _names[i]; if (name != null) { NameInfo info = new NameInfo(); info.mzID = mzID; info.tzID = tzID; info.type = getNameType(i); trie.put(name, info); } } } } // // Canonical time zone ID -> meta zone ID // private static class MZMapEntry { private String _mzID; private long _from; private long _to; MZMapEntry(String mzID, long from, long to) { _mzID = mzID; _from = from; _to = to; } String mzID() { return _mzID; } long from() { return _from; } long to() { return _to; } } private static class TZ2MZsCache extends SoftCache, String> { /* (non-Javadoc) * @see android.icu.impl.CacheBase#createInstance(java.lang.Object, java.lang.Object) */ @Override protected List createInstance(String key, String data) { List mzMaps = null; UResourceBundle bundle = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "metaZones"); UResourceBundle metazoneInfoBundle = bundle.get("metazoneInfo"); String tzkey = data.replace('/', ':'); try { UResourceBundle zoneBundle = metazoneInfoBundle.get(tzkey); mzMaps = new ArrayList(zoneBundle.getSize()); for (int idx = 0; idx < zoneBundle.getSize(); idx++) { UResourceBundle mz = zoneBundle.get(idx); String mzid = mz.getString(0); String fromStr = "1970-01-01 00:00"; String toStr = "9999-12-31 23:59"; if (mz.getSize() == 3) { fromStr = mz.getString(1); toStr = mz.getString(2); } long from, to; from = parseDate(fromStr); to = parseDate(toStr); mzMaps.add(new MZMapEntry(mzid, from, to)); } } catch (MissingResourceException mre) { mzMaps = Collections.emptyList(); } return mzMaps; } /** * Private static method parsing the date text used by meta zone to * time zone mapping data in locale resource. * * @param text the UTC date text in the format of "yyyy-MM-dd HH:mm", * for example - "1970-01-01 00:00" * @return the date */ private static long parseDate (String text) { int year = 0, month = 0, day = 0, hour = 0, min = 0; int idx; int n; // "yyyy" (0 - 3) for (idx = 0; idx <= 3; idx++) { n = text.charAt(idx) - '0'; if (n >= 0 && n < 10) { year = 10*year + n; } else { throw new IllegalArgumentException("Bad year"); } } // "MM" (5 - 6) for (idx = 5; idx <= 6; idx++) { n = text.charAt(idx) - '0'; if (n >= 0 && n < 10) { month = 10*month + n; } else { throw new IllegalArgumentException("Bad month"); } } // "dd" (8 - 9) for (idx = 8; idx <= 9; idx++) { n = text.charAt(idx) - '0'; if (n >= 0 && n < 10) { day = 10*day + n; } else { throw new IllegalArgumentException("Bad day"); } } // "HH" (11 - 12) for (idx = 11; idx <= 12; idx++) { n = text.charAt(idx) - '0'; if (n >= 0 && n < 10) { hour = 10*hour + n; } else { throw new IllegalArgumentException("Bad hour"); } } // "mm" (14 - 15) for (idx = 14; idx <= 15; idx++) { n = text.charAt(idx) - '0'; if (n >= 0 && n < 10) { min = 10*min + n; } else { throw new IllegalArgumentException("Bad minute"); } } long date = Grego.fieldsToDay(year, month - 1, day) * Grego.MILLIS_PER_DAY + (long)hour * Grego.MILLIS_PER_HOUR + (long)min * Grego.MILLIS_PER_MINUTE; return date; } } // // Meta zone ID -> time zone ID // private static class MZ2TZsCache extends SoftCache, String> { /* (non-Javadoc) * @see android.icu.impl.CacheBase#createInstance(java.lang.Object, java.lang.Object) */ @Override protected Map createInstance(String key, String data) { Map map = null; UResourceBundle bundle = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "metaZones"); UResourceBundle mapTimezones = bundle.get("mapTimezones"); try { UResourceBundle regionMap = mapTimezones.get(key); Set regions = regionMap.keySet(); map = new HashMap(regions.size()); for (String region : regions) { String tzID = regionMap.getString(region).intern(); map.put(region.intern(), tzID); } } catch (MissingResourceException e) { map = Collections.emptyMap(); } return map; } } private static final Pattern LOC_EXCLUSION_PATTERN = Pattern.compile("Etc/.*|SystemV/.*|.*/Riyadh8[7-9]"); /** * Default exemplar location name based on time zone ID. * For example, "America/New_York" -> "New York" * @param tzID the time zone ID * @return the exemplar location name or null if location is not available. */ public static String getDefaultExemplarLocationName(String tzID) { if (tzID == null || tzID.length() == 0 || LOC_EXCLUSION_PATTERN.matcher(tzID).matches()) { return null; } String location = null; int sep = tzID.lastIndexOf('/'); if (sep > 0 && sep + 1 < tzID.length()) { location = tzID.substring(sep + 1).replace('_', ' '); } return location; } }