13b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller/*
23b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller * Based on the UCB version of strftime.c with the copyright notice appearing below.
33b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller */
43b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
53b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller/*
63b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** Copyright (c) 1989 The Regents of the University of California.
73b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** All rights reserved.
83b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller**
93b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** Redistribution and use in source and binary forms are permitted
103b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** provided that the above copyright notice and this paragraph are
113b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** duplicated in all such forms and that any documentation,
123b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** advertising materials, and other materials related to such
133b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** distribution and use acknowledge that the software was developed
143b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** by the University of California, Berkeley. The name of the
153b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** University may not be used to endorse or promote products derived
163b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** from this software without specific prior written permission.
173b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
183b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
193b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
203b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller*/
213b852e3489e995600fce19dfdbf3a5d769374d74Neil Fullerpackage android.text.format;
223b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
233b852e3489e995600fce19dfdbf3a5d769374d74Neil Fullerimport android.content.res.Resources;
243b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
2516566656cd2f81f62c8693e64de68062f2680161Neil Fullerimport java.nio.CharBuffer;
263b852e3489e995600fce19dfdbf3a5d769374d74Neil Fullerimport java.util.Formatter;
273b852e3489e995600fce19dfdbf3a5d769374d74Neil Fullerimport java.util.Locale;
283b852e3489e995600fce19dfdbf3a5d769374d74Neil Fullerimport java.util.TimeZone;
293b852e3489e995600fce19dfdbf3a5d769374d74Neil Fullerimport libcore.icu.LocaleData;
303b852e3489e995600fce19dfdbf3a5d769374d74Neil Fullerimport libcore.util.ZoneInfo;
313b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
323b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller/**
3316566656cd2f81f62c8693e64de68062f2680161Neil Fuller * Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java.
343b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller *
353b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller * <p>This class is not thread safe.
363b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller */
373b852e3489e995600fce19dfdbf3a5d769374d74Neil Fullerclass TimeFormatter {
3816566656cd2f81f62c8693e64de68062f2680161Neil Fuller    // An arbitrary value outside the range representable by a char.
3916566656cd2f81f62c8693e64de68062f2680161Neil Fuller    private static final int FORCE_LOWER_CASE = -1;
403b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
413b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static final int SECSPERMIN = 60;
423b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static final int MINSPERHOUR = 60;
433b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static final int DAYSPERWEEK = 7;
443b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static final int MONSPERYEAR = 12;
453b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static final int HOURSPERDAY = 24;
463b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static final int DAYSPERLYEAR = 366;
473b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static final int DAYSPERNYEAR = 365;
483b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
493b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    /**
503b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller     * The Locale for which the cached LocaleData and formats have been loaded.
513b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller     */
523b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static Locale sLocale;
533b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static LocaleData sLocaleData;
543b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static String sTimeOnlyFormat;
553b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static String sDateOnlyFormat;
563b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static String sDateTimeFormat;
573b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
583b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private final LocaleData localeData;
593b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private final String dateTimeFormat;
603b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private final String timeOnlyFormat;
613b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private final String dateOnlyFormat;
623b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
633b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private StringBuilder outputBuilder;
6416566656cd2f81f62c8693e64de68062f2680161Neil Fuller    private Formatter numberFormatter;
653b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
663b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    public TimeFormatter() {
673b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        synchronized (TimeFormatter.class) {
683b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            Locale locale = Locale.getDefault();
693b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
703b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            if (sLocale == null || !(locale.equals(sLocale))) {
713b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                sLocale = locale;
723b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                sLocaleData = LocaleData.get(locale);
733b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
743b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                Resources r = Resources.getSystem();
753b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
763b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                sDateOnlyFormat = r.getString(com.android.internal.R.string.month_day_year);
773b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time);
783b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            }
793b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
803b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            this.dateTimeFormat = sDateTimeFormat;
813b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            this.timeOnlyFormat = sTimeOnlyFormat;
823b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            this.dateOnlyFormat = sDateOnlyFormat;
833b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            localeData = sLocaleData;
843b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        }
853b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
863b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
873b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    /**
883b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller     * Format the specified {@code wallTime} using {@code pattern}. The output is returned.
893b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller     */
903b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    public String format(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
913b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        try {
923b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            StringBuilder stringBuilder = new StringBuilder();
933b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
943b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            outputBuilder = stringBuilder;
9516566656cd2f81f62c8693e64de68062f2680161Neil Fuller            // This uses the US locale because number localization is handled separately (see below)
9616566656cd2f81f62c8693e64de68062f2680161Neil Fuller            // and locale sensitive strings are output directly using outputBuilder.
9716566656cd2f81f62c8693e64de68062f2680161Neil Fuller            numberFormatter = new Formatter(stringBuilder, Locale.US);
983b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
993b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            formatInternal(pattern, wallTime, zoneInfo);
1003b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            String result = stringBuilder.toString();
1013b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            // This behavior is the source of a bug since some formats are defined as being
10216566656cd2f81f62c8693e64de68062f2680161Neil Fuller            // in ASCII and not localized.
1033b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            if (localeData.zeroDigit != '0') {
1043b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                result = localizeDigits(result);
1053b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            }
1063b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            return result;
1073b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        } finally {
1083b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            outputBuilder = null;
10916566656cd2f81f62c8693e64de68062f2680161Neil Fuller            numberFormatter = null;
1103b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        }
1113b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
1123b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
1133b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private String localizeDigits(String s) {
1143b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        int length = s.length();
1153b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        int offsetToLocalizedDigits = localeData.zeroDigit - '0';
1163b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        StringBuilder result = new StringBuilder(length);
1173b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        for (int i = 0; i < length; ++i) {
1183b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            char ch = s.charAt(i);
1193b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            if (ch >= '0' && ch <= '9') {
1203b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                ch += offsetToLocalizedDigits;
1213b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            }
1223b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            result.append(ch);
1233b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        }
1243b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        return result.toString();
1253b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
1263b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
1273b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    /**
1283b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller     * Format the specified {@code wallTime} using {@code pattern}. The output is written to
1293b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller     * {@link #outputBuilder}.
1303b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller     */
1313b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private void formatInternal(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
13216566656cd2f81f62c8693e64de68062f2680161Neil Fuller        CharBuffer formatBuffer = CharBuffer.wrap(pattern);
1333b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        while (formatBuffer.remaining() > 0) {
13416566656cd2f81f62c8693e64de68062f2680161Neil Fuller            boolean outputCurrentChar = true;
13516566656cd2f81f62c8693e64de68062f2680161Neil Fuller            char currentChar = formatBuffer.get(formatBuffer.position());
13616566656cd2f81f62c8693e64de68062f2680161Neil Fuller            if (currentChar == '%') {
13716566656cd2f81f62c8693e64de68062f2680161Neil Fuller                outputCurrentChar = handleToken(formatBuffer, wallTime, zoneInfo);
1383b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            }
13916566656cd2f81f62c8693e64de68062f2680161Neil Fuller            if (outputCurrentChar) {
14016566656cd2f81f62c8693e64de68062f2680161Neil Fuller                outputBuilder.append(formatBuffer.get(formatBuffer.position()));
1413b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            }
1423b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            formatBuffer.position(formatBuffer.position() + 1);
1433b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        }
1443b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
1453b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
14616566656cd2f81f62c8693e64de68062f2680161Neil Fuller    private boolean handleToken(CharBuffer formatBuffer, ZoneInfo.WallTime wallTime,
1473b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            ZoneInfo zoneInfo) {
1483b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
14916566656cd2f81f62c8693e64de68062f2680161Neil Fuller        // The char at formatBuffer.position() is expected to be '%' at this point.
1503b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        int modifier = 0;
1513b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        while (formatBuffer.remaining() > 1) {
15216566656cd2f81f62c8693e64de68062f2680161Neil Fuller            // Increment the position then get the new current char.
1533b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            formatBuffer.position(formatBuffer.position() + 1);
15416566656cd2f81f62c8693e64de68062f2680161Neil Fuller            char currentChar = formatBuffer.get(formatBuffer.position());
15516566656cd2f81f62c8693e64de68062f2680161Neil Fuller            switch (currentChar) {
1563b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'A':
1573b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    modifyAndAppend((wallTime.getWeekDay() < 0
1583b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                    || wallTime.getWeekDay() >= DAYSPERWEEK)
1593b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                    ? "?" : localeData.longWeekdayNames[wallTime.getWeekDay() + 1],
1603b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            modifier);
1613b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
1623b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'a':
1633b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    modifyAndAppend((wallTime.getWeekDay() < 0
1643b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                    || wallTime.getWeekDay() >= DAYSPERWEEK)
1653b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                    ? "?" : localeData.shortWeekdayNames[wallTime.getWeekDay() + 1],
1663b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            modifier);
1673b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
1683b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'B':
1693b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    if (modifier == '-') {
1703b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        modifyAndAppend((wallTime.getMonth() < 0
1713b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                        || wallTime.getMonth() >= MONSPERYEAR)
1723b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                        ? "?"
1733b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                        : localeData.longStandAloneMonthNames[wallTime.getMonth()],
1743b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                modifier);
1753b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    } else {
1763b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        modifyAndAppend((wallTime.getMonth() < 0
1773b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                        || wallTime.getMonth() >= MONSPERYEAR)
1783b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                        ? "?" : localeData.longMonthNames[wallTime.getMonth()],
1793b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                modifier);
1803b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    }
1813b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
1823b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'b':
1833b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'h':
1843b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    modifyAndAppend((wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
1853b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                    ? "?" : localeData.shortMonthNames[wallTime.getMonth()],
1863b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            modifier);
1873b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
1883b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'C':
1893b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    outputYear(wallTime.getYear(), true, false, modifier);
1903b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
1913b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'c':
1923b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    formatInternal(dateTimeFormat, wallTime, zoneInfo);
1933b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
1943b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'D':
1953b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    formatInternal("%m/%d/%y", wallTime, zoneInfo);
1963b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
1973b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'd':
19816566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
1993b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            wallTime.getMonthDay());
2003b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2013b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'E':
2023b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'O':
2033b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    // C99 locale modifiers are not supported.
2043b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    continue;
2053b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case '_':
2063b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case '-':
2073b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case '0':
2083b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case '^':
2093b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case '#':
21016566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    modifier = currentChar;
2113b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    continue;
2123b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'e':
21316566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
2143b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            wallTime.getMonthDay());
2153b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2163b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'F':
2173b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    formatInternal("%Y-%m-%d", wallTime, zoneInfo);
2183b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2193b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'H':
22016566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
2213b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            wallTime.getHour());
2223b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2233b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'I':
2243b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    int hour = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
22516566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour);
2263b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2273b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'j':
2283b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    int yearDay = wallTime.getYearDay() + 1;
22916566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"),
2303b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            yearDay);
2313b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2323b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'k':
23316566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
2343b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            wallTime.getHour());
2353b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2363b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'l':
2373b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    int n2 = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
23816566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2);
2393b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2403b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'M':
24116566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
2423b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            wallTime.getMinute());
2433b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2443b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'm':
24516566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
2463b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            wallTime.getMonth() + 1);
2473b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2483b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'n':
24916566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    outputBuilder.append('\n');
2503b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2513b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'p':
2523b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
2533b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            : localeData.amPm[0], modifier);
2543b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2553b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'P':
2563b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
2573b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            : localeData.amPm[0], FORCE_LOWER_CASE);
2583b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2593b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'R':
2603b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    formatInternal("%H:%M", wallTime, zoneInfo);
2613b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2623b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'r':
2633b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    formatInternal("%I:%M:%S %p", wallTime, zoneInfo);
2643b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2653b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'S':
26616566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
2673b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            wallTime.getSecond());
2683b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2693b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 's':
2703b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    int timeInSeconds = wallTime.mktime(zoneInfo);
27116566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    outputBuilder.append(Integer.toString(timeInSeconds));
2723b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2733b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'T':
2743b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    formatInternal("%H:%M:%S", wallTime, zoneInfo);
2753b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2763b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 't':
27716566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    outputBuilder.append('\t');
2783b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2793b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'U':
28016566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
2813b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            (wallTime.getYearDay() + DAYSPERWEEK - wallTime.getWeekDay())
2823b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                    / DAYSPERWEEK);
2833b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2843b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'u':
2853b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    int day = (wallTime.getWeekDay() == 0) ? DAYSPERWEEK : wallTime.getWeekDay();
28616566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format("%d", day);
2873b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
2883b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'V':   /* ISO 8601 week number */
2893b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'G':   /* ISO 8601 year (four digits) */
2903b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'g':   /* ISO 8601 year (two digits) */
2913b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                {
2923b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    int year = wallTime.getYear();
2933b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    int yday = wallTime.getYearDay();
2943b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    int wday = wallTime.getWeekDay();
2953b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    int w;
2963b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    while (true) {
2973b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        int len = isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
2983b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        // What yday (-3 ... 3) does the ISO year begin on?
2993b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        int bot = ((yday + 11 - wday) % DAYSPERWEEK) - 3;
3003b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        // What yday does the NEXT ISO year begin on?
3013b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        int top = bot - (len % DAYSPERWEEK);
3023b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        if (top < -3) {
3033b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            top += DAYSPERWEEK;
3043b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        }
3053b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        top += len;
3063b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        if (yday >= top) {
3073b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            ++year;
3083b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            w = 1;
3093b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            break;
3103b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        }
3113b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        if (yday >= bot) {
3123b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            w = 1 + ((yday - bot) / DAYSPERWEEK);
3133b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                            break;
3143b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        }
3153b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        --year;
3163b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        yday += isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
3173b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    }
31816566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    if (currentChar == 'V') {
31916566656cd2f81f62c8693e64de68062f2680161Neil Fuller                        numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w);
32016566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    } else if (currentChar == 'g') {
3213b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        outputYear(year, false, true, modifier);
3223b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    } else {
3233b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        outputYear(year, true, true, modifier);
3243b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    }
3253b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
3263b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                }
3273b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'v':
3283b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    formatInternal("%e-%b-%Y", wallTime, zoneInfo);
3293b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
3303b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'W':
3313b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    int n = (wallTime.getYearDay() + DAYSPERWEEK - (
3323b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                    wallTime.getWeekDay() != 0 ? (wallTime.getWeekDay() - 1)
3333b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                                            : (DAYSPERWEEK - 1))) / DAYSPERWEEK;
33416566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
3353b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
3363b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'w':
33716566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format("%d", wallTime.getWeekDay());
3383b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
3393b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'X':
3403b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    formatInternal(timeOnlyFormat, wallTime, zoneInfo);
3413b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
3423b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'x':
3433b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    formatInternal(dateOnlyFormat, wallTime, zoneInfo);
3443b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
3453b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'y':
3463b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    outputYear(wallTime.getYear(), false, true, modifier);
3473b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
3483b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'Y':
3493b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    outputYear(wallTime.getYear(), true, true, modifier);
3503b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
3513b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'Z':
3523b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    if (wallTime.getIsDst() < 0) {
3533b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        return false;
3543b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    }
3553b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    boolean isDst = wallTime.getIsDst() != 0;
3563b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    modifyAndAppend(zoneInfo.getDisplayName(isDst, TimeZone.SHORT), modifier);
3573b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
3583b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case 'z': {
3593b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    if (wallTime.getIsDst() < 0) {
3603b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        return false;
3613b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    }
3623b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    int diff = wallTime.getGmtOffset();
36316566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    char sign;
3643b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    if (diff < 0) {
36516566656cd2f81f62c8693e64de68062f2680161Neil Fuller                        sign = '-';
3663b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        diff = -diff;
3673b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    } else {
36816566656cd2f81f62c8693e64de68062f2680161Neil Fuller                        sign = '+';
3693b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    }
37016566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    outputBuilder.append(sign);
3713b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    diff /= SECSPERMIN;
3723b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR);
37316566656cd2f81f62c8693e64de68062f2680161Neil Fuller                    numberFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff);
3743b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
3753b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                }
3763b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case '+':
3773b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    formatInternal("%a %b %e %H:%M:%S %Z %Y", wallTime, zoneInfo);
3783b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return false;
3793b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                case '%':
3803b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    // If conversion char is undefined, behavior is undefined. Print out the
3813b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    // character itself.
3823b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                default:
3833b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    return true;
3843b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            }
3853b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        }
3863b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        return true;
3873b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
3883b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
3893b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private void modifyAndAppend(CharSequence str, int modifier) {
3903b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        switch (modifier) {
3913b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            case FORCE_LOWER_CASE:
3923b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                for (int i = 0; i < str.length(); i++) {
3933b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    outputBuilder.append(brokenToLower(str.charAt(i)));
3943b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                }
3953b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                break;
3963b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            case '^':
3973b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                for (int i = 0; i < str.length(); i++) {
3983b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    outputBuilder.append(brokenToUpper(str.charAt(i)));
3993b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                }
4003b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                break;
4013b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            case '#':
4023b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                for (int i = 0; i < str.length(); i++) {
4033b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    char c = str.charAt(i);
4043b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    if (brokenIsUpper(c)) {
4053b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        c = brokenToLower(c);
4063b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    } else if (brokenIsLower(c)) {
4073b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                        c = brokenToUpper(c);
4083b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    }
4093b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                    outputBuilder.append(c);
4103b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                }
4113b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                break;
4123b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            default:
4133b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                outputBuilder.append(str);
4143b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        }
4153b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
4163b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
4173b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private void outputYear(int value, boolean outputTop, boolean outputBottom, int modifier) {
4183b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        int lead;
4193b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        int trail;
4203b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
4213b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        final int DIVISOR = 100;
4223b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        trail = value % DIVISOR;
4233b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        lead = value / DIVISOR + trail / DIVISOR;
4243b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        trail %= DIVISOR;
4253b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        if (trail < 0 && lead > 0) {
4263b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            trail += DIVISOR;
4273b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            --lead;
4283b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        } else if (lead < 0 && trail > 0) {
4293b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            trail -= DIVISOR;
4303b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            ++lead;
4313b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        }
4323b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        if (outputTop) {
4333b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            if (lead == 0 && trail < 0) {
43416566656cd2f81f62c8693e64de68062f2680161Neil Fuller                outputBuilder.append("-0");
4353b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            } else {
43616566656cd2f81f62c8693e64de68062f2680161Neil Fuller                numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead);
4373b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            }
4383b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        }
4393b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        if (outputBottom) {
4403b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            int n = ((trail < 0) ? -trail : trail);
44116566656cd2f81f62c8693e64de68062f2680161Neil Fuller            numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
4423b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        }
4433b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
4443b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
4453b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static String getFormat(int modifier, String normal, String underscore, String dash,
4463b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            String zero) {
4473b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        switch (modifier) {
4483b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            case '_':
4493b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                return underscore;
4503b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            case '-':
4513b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                return dash;
4523b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            case '0':
4533b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller                return zero;
4543b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        }
4553b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        return normal;
4563b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
4573b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
4583b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static boolean isLeap(int year) {
4593b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
4603b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
4613b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
4623b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    /**
46316566656cd2f81f62c8693e64de68062f2680161Neil Fuller     * A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII codes in
46416566656cd2f81f62c8693e64de68062f2680161Neil Fuller     * order to be compatible with the old native implementation.
4653b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller     */
4663b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static boolean brokenIsUpper(char toCheck) {
4673b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        return toCheck >= 'A' && toCheck <= 'Z';
4683b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
4693b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
4703b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    /**
47116566656cd2f81f62c8693e64de68062f2680161Neil Fuller     * A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII codes in
47216566656cd2f81f62c8693e64de68062f2680161Neil Fuller     * order to be compatible with the old native implementation.
4733b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller     */
4743b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static boolean brokenIsLower(char toCheck) {
4753b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        return toCheck >= 'a' && toCheck <= 'z';
4763b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
4773b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
4783b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    /**
47916566656cd2f81f62c8693e64de68062f2680161Neil Fuller     * A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII codes in
48016566656cd2f81f62c8693e64de68062f2680161Neil Fuller     * order to be compatible with the old native implementation.
4813b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller     */
4823b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static char brokenToLower(char input) {
4833b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        if (input >= 'A' && input <= 'Z') {
4843b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            return (char) (input - 'A' + 'a');
4853b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        }
4863b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        return input;
4873b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
4883b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
4893b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    /**
49016566656cd2f81f62c8693e64de68062f2680161Neil Fuller     * A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII codes in
49116566656cd2f81f62c8693e64de68062f2680161Neil Fuller     * order to be compatible with the old native implementation.
4923b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller     */
4933b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    private static char brokenToUpper(char input) {
4943b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        if (input >= 'a' && input <= 'z') {
4953b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller            return (char) (input - 'a' + 'A');
4963b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        }
4973b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller        return input;
4983b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller    }
4993b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller
5003b852e3489e995600fce19dfdbf3a5d769374d74Neil Fuller}
501