/** * Append a representation of the time zone of 'calendar' to 'buffer'. * * @param count the number of z or Z characters in the format string; "zzz" would be 3, for * example. * @param generalTimeZone true if we should use a display name ("PDT") if available; false implies * that we should use RFC 822 format ("-0800") instead. This corresponds to 'z' versus 'Z' in * the format string. */ private void appendTimeZone(StringBuffer buffer, int count, boolean generalTimeZone) { if (generalTimeZone) { TimeZone tz = calendar.getTimeZone(); boolean daylight = (calendar.get(Calendar.DST_OFFSET) != 0); int style = count < 4 ? TimeZone.SHORT : TimeZone.LONG; if (!formatData.customZoneStrings) { buffer.append(tz.getDisplayName(daylight, style, formatData.locale)); return; } // We can't call TimeZone.getDisplayName() because it would not use // the custom DateFormatSymbols of this SimpleDateFormat. String custom = TimeZoneNames.getDisplayName(formatData.zoneStrings, tz.getID(), daylight, style); if (custom != null) { buffer.append(custom); return; } } // We didn't find what we were looking for, so default to a numeric time zone. appendNumericTimeZone(buffer, count, generalTimeZone); }
public static List<Map<String, Object>> getZonesList(Context context) { final Locale locale = Locale.getDefault(); final Date now = new Date(); // The display name chosen for each zone entry depends on whether the zone is one associated // with the country of the user's chosen locale. For "local" zones we prefer the "long name" // (e.g. "Europe/London" -> "British Summer Time" for people in the UK). For "non-local" // zones we prefer the exemplar location (e.g. "Europe/London" -> "London" for English // speakers from outside the UK). This heuristic is based on the fact that people are // typically familiar with their local timezones and exemplar locations don't always match // modern-day expectations for people living in the country covered. Large countries like // China that mostly use a single timezone (olson id: "Asia/Shanghai") may not live near // "Shanghai" and prefer the long name over the exemplar location. The only time we don't // follow this policy for local zones is when Android supplies multiple olson IDs to choose // from and the use of a zone's long name leads to ambiguity. For example, at the time of // writing Android lists 5 olson ids for Australia which collapse to 2 different zone names // in winter but 4 different zone names in summer. The ambiguity leads to the users // selecting the wrong olson ids. // Get the list of olson ids to display to the user. List<String> olsonIdsToDisplay = readTimezonesToDisplay(context); // Create a lookup of local zone IDs. Set<String> localZoneIds = new TreeSet<String>(); for (String olsonId : TimeZoneNames.forLocale(locale)) { localZoneIds.add(olsonId); } // Work out whether the long names for the local entries that we would show by default would // be ambiguous. Set<String> localZoneNames = new TreeSet<String>(); boolean localLongNamesAreAmbiguous = false; for (String olsonId : olsonIdsToDisplay) { if (localZoneIds.contains(olsonId)) { TimeZone tz = TimeZone.getTimeZone(olsonId); String zoneLongName = getZoneLongName(locale, tz, now); boolean longNameIsUnique = localZoneNames.add(zoneLongName); if (!longNameIsUnique) { localLongNamesAreAmbiguous = true; break; } } } // Generate the list of zone entries to return. List<Map<String, Object>> zones = new ArrayList<Map<String, Object>>(); for (String olsonId : olsonIdsToDisplay) { final TimeZone tz = TimeZone.getTimeZone(olsonId); // Exemplar location display is the default. The only time we intend to display the long // name is when the olsonId is local AND long names are not ambiguous. boolean isLocalZoneId = localZoneIds.contains(olsonId); boolean preferLongName = isLocalZoneId && !localLongNamesAreAmbiguous; String displayName = getZoneDisplayName(locale, tz, now, preferLongName); String gmtOffsetString = getGmtOffsetString(locale, tz, now); int offsetMillis = tz.getOffset(now.getTime()); Map<String, Object> displayEntry = createDisplayEntry(tz, gmtOffsetString, displayName, offsetMillis); zones.add(displayEntry); } return zones; }
private static String getZoneExemplarLocation(Locale locale, TimeZone tz) { return TimeZoneNames.getExemplarLocation(locale.toString(), tz.getID()); }