private static String[] getDisplayNameArray(int field, boolean isLong, Locale locale) { DateFormatSymbols dfs = new DateFormatSymbols(locale); switch (field) { case Calendar.AM_PM: return dfs.getAmPmStrings(); case Calendar.DAY_OF_WEEK: return isLong ? dfs.getWeekdays() : dfs.getShortWeekdays(); case Calendar.ERA: return dfs.getEras(); case Calendar.MONTH: return isLong ? dfs.getMonths() : dfs.getShortMonths(); } return null; }
/** * This method parses the specified string into a date. * * @param dateStr The date string to parse. * @param pos The input and output parse position * @return The parsed date, or <code>null</code> if the string cannot be parsed. */ public Date parse(String dateStr, ParsePosition pos) { int fmt_index = 0; int fmt_max = pattern.length(); calendar.clear(); boolean saw_timezone = false; int quote_start = -1; boolean is2DigitYear = false; try { for (; fmt_index < fmt_max; ++fmt_index) { char ch = pattern.charAt(fmt_index); if (ch == '\'') { int index = pos.getIndex(); if (fmt_index < fmt_max - 1 && pattern.charAt(fmt_index + 1) == '\'') { if (!expect(dateStr, pos, ch)) return null; ++fmt_index; } else quote_start = quote_start < 0 ? fmt_index : -1; continue; } if (quote_start != -1 || ((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z'))) { if (!expect(dateStr, pos, ch)) return null; continue; } // We've arrived at a potential pattern character in the // pattern. int fmt_count = 1; while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch) { ++fmt_count; } // We might need to limit the number of digits to parse in // some cases. We look to the next pattern character to // decide. boolean limit_digits = false; if (fmt_index < fmt_max && standardChars.indexOf(pattern.charAt(fmt_index)) >= 0) limit_digits = true; --fmt_index; // We can handle most fields automatically: most either are // numeric or are looked up in a string vector. In some cases // we need an offset. When numeric, `offset' is added to the // resulting value. When doing a string lookup, offset is the // initial index into the string array. int calendar_field; boolean is_numeric = true; int offset = 0; boolean maybe2DigitYear = false; boolean oneBasedHour = false; boolean oneBasedHourOfDay = false; Integer simpleOffset; String[] set1 = null; String[] set2 = null; switch (ch) { case 'd': calendar_field = Calendar.DATE; break; case 'D': calendar_field = Calendar.DAY_OF_YEAR; break; case 'F': calendar_field = Calendar.DAY_OF_WEEK_IN_MONTH; break; case 'E': is_numeric = false; offset = 1; calendar_field = Calendar.DAY_OF_WEEK; set1 = formatData.getWeekdays(); set2 = formatData.getShortWeekdays(); break; case 'w': calendar_field = Calendar.WEEK_OF_YEAR; break; case 'W': calendar_field = Calendar.WEEK_OF_MONTH; break; case 'M': calendar_field = Calendar.MONTH; if (fmt_count <= 2) offset = -1; else { is_numeric = false; set1 = formatData.getMonths(); set2 = formatData.getShortMonths(); } break; case 'y': calendar_field = Calendar.YEAR; if (fmt_count <= 2) maybe2DigitYear = true; break; case 'K': calendar_field = Calendar.HOUR; break; case 'h': calendar_field = Calendar.HOUR; oneBasedHour = true; break; case 'H': calendar_field = Calendar.HOUR_OF_DAY; break; case 'k': calendar_field = Calendar.HOUR_OF_DAY; oneBasedHourOfDay = true; break; case 'm': calendar_field = Calendar.MINUTE; break; case 's': calendar_field = Calendar.SECOND; break; case 'S': calendar_field = Calendar.MILLISECOND; break; case 'a': is_numeric = false; calendar_field = Calendar.AM_PM; set1 = formatData.getAmPmStrings(); break; case 'z': case 'Z': // We need a special case for the timezone, because it // uses a different data structure than the other cases. is_numeric = false; calendar_field = Calendar.ZONE_OFFSET; String[][] zoneStrings = formatData.getZoneStrings(); int zoneCount = zoneStrings.length; int index = pos.getIndex(); boolean found_zone = false; simpleOffset = computeOffset(dateStr.substring(index)); if (simpleOffset != null) { found_zone = true; saw_timezone = true; calendar.set(Calendar.DST_OFFSET, 0); offset = simpleOffset.intValue(); } else { for (int j = 0; j < zoneCount; j++) { String[] strings = zoneStrings[j]; int k; for (k = 0; k < strings.length; ++k) { if (dateStr.startsWith(strings[k], index)) break; } if (k != strings.length) { found_zone = true; saw_timezone = true; TimeZone tz = TimeZone.getTimeZone(strings[0]); // Check if it's a DST zone or ordinary if (k == 3 || k == 4) calendar.set(Calendar.DST_OFFSET, tz.getDSTSavings()); else calendar.set(Calendar.DST_OFFSET, 0); offset = tz.getRawOffset(); pos.setIndex(index + strings[k].length()); break; } } } if (!found_zone) { pos.setErrorIndex(pos.getIndex()); return null; } break; default: pos.setErrorIndex(pos.getIndex()); return null; } // Compute the value we should assign to the field. int value; int index = -1; if (is_numeric) { numberFormat.setMinimumIntegerDigits(fmt_count); if (limit_digits) numberFormat.setMaximumIntegerDigits(fmt_count); if (maybe2DigitYear) index = pos.getIndex(); Number n = numberFormat.parse(dateStr, pos); if (pos == null || !(n instanceof Long)) return null; value = n.intValue() + offset; } else if (set1 != null) { index = pos.getIndex(); int i; boolean found = false; for (i = offset; i < set1.length; ++i) { if (set1[i] != null) if (dateStr.toUpperCase().startsWith(set1[i].toUpperCase(), index)) { found = true; pos.setIndex(index + set1[i].length()); break; } } if (!found && set2 != null) { for (i = offset; i < set2.length; ++i) { if (set2[i] != null) if (dateStr.toUpperCase().startsWith(set2[i].toUpperCase(), index)) { found = true; pos.setIndex(index + set2[i].length()); break; } } } if (!found) { pos.setErrorIndex(index); return null; } value = i; } else value = offset; if (maybe2DigitYear) { // Parse into default century if the numeric year string has // exactly 2 digits. int digit_count = pos.getIndex() - index; if (digit_count == 2) { is2DigitYear = true; value += defaultCentury; } } // Calendar uses 0-based hours. // I.e. 00:00 AM is midnight, not 12 AM or 24:00 if (oneBasedHour && value == 12) value = 0; if (oneBasedHourOfDay && value == 24) value = 0; // Assign the value and move on. calendar.set(calendar_field, value); } if (is2DigitYear) { // Apply the 80-20 heuristic to dermine the full year based on // defaultCenturyStart. int year = calendar.get(Calendar.YEAR); if (calendar.getTime().compareTo(defaultCenturyStart) < 0) calendar.set(Calendar.YEAR, year + 100); } if (!saw_timezone) { // Use the real rules to determine whether or not this // particular time is in daylight savings. calendar.clear(Calendar.DST_OFFSET); calendar.clear(Calendar.ZONE_OFFSET); } return calendar.getTime(); } catch (IllegalArgumentException x) { pos.setErrorIndex(pos.getIndex()); return null; } }
/** * Returns a list of Rules given a pattern. * * @return a {@code List} of Rule objects * @throws IllegalArgumentException if pattern is invalid */ protected List<Rule> parsePattern() { DateFormatSymbols symbols = new DateFormatSymbols(mLocale); List<Rule> rules = new ArrayList<Rule>(); String[] ERAs = symbols.getEras(); String[] months = symbols.getMonths(); String[] shortMonths = symbols.getShortMonths(); String[] weekdays = symbols.getWeekdays(); String[] shortWeekdays = symbols.getShortWeekdays(); String[] AmPmStrings = symbols.getAmPmStrings(); int length = mPattern.length(); int[] indexRef = new int[1]; for (int i = 0; i < length; i++) { indexRef[0] = i; String token = parseToken(mPattern, indexRef); i = indexRef[0]; int tokenLen = token.length(); if (tokenLen == 0) { break; } Rule rule; char c = token.charAt(0); switch (c) { case 'G': // era designator (text) rule = new TextField(Calendar.ERA, ERAs); break; case 'y': // year (number) if (tokenLen == 2) { rule = TwoDigitYearField.INSTANCE; } else { rule = selectNumberRule(Calendar.YEAR, tokenLen < 4 ? 4 : tokenLen); } break; case 'M': // month in year (text and number) if (tokenLen >= 4) { rule = new TextField(Calendar.MONTH, months); } else if (tokenLen == 3) { rule = new TextField(Calendar.MONTH, shortMonths); } else if (tokenLen == 2) { rule = TwoDigitMonthField.INSTANCE; } else { rule = UnpaddedMonthField.INSTANCE; } break; case 'd': // day in month (number) rule = selectNumberRule(Calendar.DAY_OF_MONTH, tokenLen); break; case 'h': // hour in am/pm (number, 1..12) rule = new TwelveHourField(selectNumberRule(Calendar.HOUR, tokenLen)); break; case 'H': // hour in day (number, 0..23) rule = selectNumberRule(Calendar.HOUR_OF_DAY, tokenLen); break; case 'm': // minute in hour (number) rule = selectNumberRule(Calendar.MINUTE, tokenLen); break; case 's': // second in minute (number) rule = selectNumberRule(Calendar.SECOND, tokenLen); break; case 'S': // millisecond (number) rule = selectNumberRule(Calendar.MILLISECOND, tokenLen); break; case 'E': // day in week (text) rule = new TextField(Calendar.DAY_OF_WEEK, tokenLen < 4 ? shortWeekdays : weekdays); break; case 'D': // day in year (number) rule = selectNumberRule(Calendar.DAY_OF_YEAR, tokenLen); break; case 'F': // day of week in month (number) rule = selectNumberRule(Calendar.DAY_OF_WEEK_IN_MONTH, tokenLen); break; case 'w': // week in year (number) rule = selectNumberRule(Calendar.WEEK_OF_YEAR, tokenLen); break; case 'W': // week in month (number) rule = selectNumberRule(Calendar.WEEK_OF_MONTH, tokenLen); break; case 'a': // am/pm marker (text) rule = new TextField(Calendar.AM_PM, AmPmStrings); break; case 'k': // hour in day (1..24) rule = new TwentyFourHourField(selectNumberRule(Calendar.HOUR_OF_DAY, tokenLen)); break; case 'K': // hour in am/pm (0..11) rule = selectNumberRule(Calendar.HOUR, tokenLen); break; case 'z': // time zone (text) if (tokenLen >= 4) { rule = new TimeZoneNameRule(mTimeZone, mLocale, TimeZone.LONG); } else { rule = new TimeZoneNameRule(mTimeZone, mLocale, TimeZone.SHORT); } break; case 'Z': // time zone (value) if (tokenLen == 1) { rule = TimeZoneNumberRule.INSTANCE_NO_COLON; } else { rule = TimeZoneNumberRule.INSTANCE_COLON; } break; case '\'': // literal text String sub = token.substring(1); if (sub.length() == 1) { rule = new CharacterLiteral(sub.charAt(0)); } else { rule = new StringLiteral(sub); } break; default: throw new IllegalArgumentException("Illegal pattern component: " + token); } rules.add(rule); } return rules; }
static String describeLocale(String name) { final StringBuilder result = new StringBuilder(); result.append("<html>"); final Locale locale = localeByName(name); result.append("<p>"); append(result, "Display Name", locale.getDisplayName()); append(result, "Localized Display Name", locale.getDisplayName(locale)); if (locale.getLanguage().length() > 0) { String iso3Language = "(not available)"; try { iso3Language = locale.getISO3Language(); } catch (MissingResourceException ignored) { } result.append("<p>"); append(result, "Display Language", locale.getDisplayLanguage()); append(result, "Localized Display Language", locale.getDisplayLanguage(locale)); append(result, "2-Letter Language Code", locale.getLanguage()); append(result, "3-Letter Language Code", iso3Language); } if (locale.getCountry().length() > 0) { String iso3Country = "(not available)"; try { iso3Country = locale.getISO3Country(); } catch (MissingResourceException ignored) { } result.append("<p>"); append(result, "Display Country", locale.getDisplayCountry()); append(result, "Localized Display Country", locale.getDisplayCountry(locale)); append(result, "2-Letter Country Code", locale.getCountry()); append(result, "3-Letter Country Code", iso3Country); } if (locale.getVariant().length() > 0) { result.append("<p>"); append(result, "Display Variant", locale.getDisplayVariant()); append(result, "Localized Display Variant", locale.getDisplayVariant(locale)); append(result, "Variant Code", locale.getVariant()); } result.append("<p><b>Number Formatting</b>"); describeNumberFormat(result, "Decimal", NumberFormat.getInstance(locale), 1234.5, -1234.5); describeNumberFormat(result, "Integer", NumberFormat.getIntegerInstance(locale), 1234, -1234); describeNumberFormat( result, "Currency", NumberFormat.getCurrencyInstance(locale), 1234.5, -1234.5); describeNumberFormat(result, "Percent", NumberFormat.getPercentInstance(locale), 12.3); boolean hasLocaleData = hasLocaleData(); if (!hasLocaleData) { result.append("<p><b>Decimal Format Symbols</b>"); NumberFormat nf = NumberFormat.getInstance(locale); if (nf instanceof DecimalFormat) { describeDecimalFormatSymbols(result, ((DecimalFormat) nf).getDecimalFormatSymbols()); } else { result.append("(Didn't expect " + nf.getClass() + ".)"); } } Date now = new Date(); // FIXME: it might be more useful to always show a time in the afternoon, to // make 24-hour patterns more obvious. result.append("<p><b>Date/Time Formatting</b>"); describeDateFormat( result, "Full Date", DateFormat.getDateInstance(DateFormat.FULL, locale), now); describeDateFormat( result, "Long Date", DateFormat.getDateInstance(DateFormat.LONG, locale), now); describeDateFormat( result, "Medium Date", DateFormat.getDateInstance(DateFormat.MEDIUM, locale), now); describeDateFormat( result, "Short Date", DateFormat.getDateInstance(DateFormat.SHORT, locale), now); result.append("<p>"); describeDateFormat( result, "Full Time", DateFormat.getTimeInstance(DateFormat.FULL, locale), now); describeDateFormat( result, "Long Time", DateFormat.getTimeInstance(DateFormat.LONG, locale), now); describeDateFormat( result, "Medium Time", DateFormat.getTimeInstance(DateFormat.MEDIUM, locale), now); describeDateFormat( result, "Short Time", DateFormat.getTimeInstance(DateFormat.SHORT, locale), now); result.append("<p>"); describeDateFormat( result, "Full Date/Time", DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, locale), now); describeDateFormat( result, "Long Date/Time", DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale), now); describeDateFormat( result, "Medium Date/Time", DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, locale), now); describeDateFormat( result, "Short Date/Time", DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale), now); if (!hasLocaleData) { result.append("<p><b>Date Format Symbols</b><p>"); DateFormat edf = DateFormat.getDateInstance(DateFormat.FULL, Locale.US); DateFormatSymbols edfs = ((SimpleDateFormat) edf).getDateFormatSymbols(); DateFormat df = DateFormat.getDateInstance(DateFormat.FULL, locale); DateFormatSymbols dfs = ((SimpleDateFormat) df).getDateFormatSymbols(); append(result, "Local Pattern Chars", dfs.getLocalPatternChars()); append(result, "Am/pm", Arrays.toString(dfs.getAmPmStrings())); append(result, "Eras", Arrays.toString(dfs.getEras())); append(result, "Months", Arrays.toString(dfs.getMonths())); append(result, "Short Months", Arrays.toString(dfs.getShortMonths())); append(result, "Weekdays", Arrays.toString(dfs.getWeekdays())); append(result, "Short Weekdays", Arrays.toString(dfs.getShortWeekdays())); } result.append("<p><b>Calendar</b><p>"); Calendar c = Calendar.getInstance(locale); int firstDayOfWeek = c.getFirstDayOfWeek(); String firstDayOfWeekString = new DateFormatSymbols(locale).getWeekdays()[firstDayOfWeek]; String englishFirstDayOfWeekString = new DateFormatSymbols(Locale.US).getWeekdays()[firstDayOfWeek]; String firstDayOfWeekDetails = firstDayOfWeek + " '" + firstDayOfWeekString + "'"; if (!englishFirstDayOfWeekString.equals(firstDayOfWeekString)) { firstDayOfWeekDetails += " (" + englishFirstDayOfWeekString + ")"; } append(result, "First Day of the Week", firstDayOfWeekDetails); append(result, "Minimal Days in First Week", c.getMinimalDaysInFirstWeek()); // If this locale specifies a country, check out the currency. // Languages don't have currencies; countries do. if (!locale.getCountry().equals("")) { result.append("<p><b>Currency</b><p>"); try { Currency currency = Currency.getInstance(locale); append(result, "ISO 4217 Currency Code", currency.getCurrencyCode()); append( result, "Currency Symbol", unicodeString(currency.getSymbol(locale)) + " (" + currency.getSymbol(Locale.US) + ")"); append(result, "Default Fraction Digits", currency.getDefaultFractionDigits()); } catch (IllegalArgumentException ex) { result.append( "<p>(This version of Android is unable to return a Currency for this Locale.)"); } } result.append("<p><b>Data Availability</b><p>"); appendAvailability(result, locale, "BreakIterator", BreakIterator.class); appendAvailability(result, locale, "Calendar", NumberFormat.class); appendAvailability(result, locale, "Collator", Collator.class); appendAvailability(result, locale, "DateFormat", DateFormat.class); appendAvailability(result, locale, "DateFormatSymbols", DateFormatSymbols.class); appendAvailability(result, locale, "DecimalFormatSymbols", DecimalFormatSymbols.class); appendAvailability(result, locale, "NumberFormat", NumberFormat.class); if (hasLocaleData) { result.append("<p><b>libcore.icu.LocaleData</b>"); try { Object enUsData = getLocaleDataInstance(Locale.US); Object localeData = getLocaleDataInstance(locale); String[] previous; result.append("<p>"); describeStringArray(result, "amPm", enUsData, localeData, null); describeStringArray(result, "eras", enUsData, localeData, null); result.append("<p>"); previous = describeStringArray(result, "longMonthNames", enUsData, localeData, null); describeStringArray(result, "longStandAloneMonthNames", enUsData, localeData, previous); previous = describeStringArray(result, "shortMonthNames", enUsData, localeData, null); describeStringArray(result, "shortStandAloneMonthNames", enUsData, localeData, previous); previous = describeStringArray(result, "tinyMonthNames", enUsData, localeData, null); describeStringArray(result, "tinyStandAloneMonthNames", enUsData, localeData, previous); result.append("<p>"); previous = describeStringArray(result, "longWeekdayNames", enUsData, localeData, null); describeStringArray(result, "longStandAloneWeekdayNames", enUsData, localeData, previous); previous = describeStringArray(result, "shortWeekdayNames", enUsData, localeData, null); describeStringArray(result, "shortStandAloneWeekdayNames", enUsData, localeData, previous); previous = describeStringArray(result, "tinyWeekdayNames", enUsData, localeData, null); describeStringArray(result, "tinyStandAloneWeekdayNames", enUsData, localeData, previous); result.append("<p>"); describeString(result, "yesterday", enUsData, localeData); describeString(result, "today", enUsData, localeData); describeString(result, "tomorrow", enUsData, localeData); result.append("<p>"); describeString(result, "timeFormat12", enUsData, localeData); describeString(result, "timeFormat24", enUsData, localeData); result.append("<p>"); describeChar(result, "zeroDigit", enUsData, localeData); describeChar(result, "decimalSeparator", enUsData, localeData); describeChar(result, "groupingSeparator", enUsData, localeData); describeChar(result, "patternSeparator", enUsData, localeData); describeChar(result, "percent", enUsData, localeData); describeChar(result, "perMill", enUsData, localeData); describeChar(result, "monetarySeparator", enUsData, localeData); describeChar(result, "minusSign", enUsData, localeData); describeString(result, "exponentSeparator", enUsData, localeData); describeString(result, "infinity", enUsData, localeData); describeString(result, "NaN", enUsData, localeData); } catch (Exception ex) { result.append("(" + ex.getClass().getSimpleName() + " thrown: " + ex.getMessage() + ")"); System.err.println(ex); } } return result.toString(); }