FileDocCategorySizeDatePackage
TimeFormatter.javaAPI DocAndroid 5.1 API20802Thu Mar 12 22:22:10 GMT 2015android.text.format

TimeFormatter

public class TimeFormatter extends Object
Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java.

This class is not thread safe.

Fields Summary
private static final int
FORCE_LOWER_CASE
private static final int
SECSPERMIN
private static final int
MINSPERHOUR
private static final int
DAYSPERWEEK
private static final int
MONSPERYEAR
private static final int
HOURSPERDAY
private static final int
DAYSPERLYEAR
private static final int
DAYSPERNYEAR
private static Locale
sLocale
The Locale for which the cached LocaleData and formats have been loaded.
private static libcore.icu.LocaleData
sLocaleData
private static String
sTimeOnlyFormat
private static String
sDateOnlyFormat
private static String
sDateTimeFormat
private final libcore.icu.LocaleData
localeData
private final String
dateTimeFormat
private final String
timeOnlyFormat
private final String
dateOnlyFormat
private StringBuilder
outputBuilder
private Formatter
numberFormatter
Constructors Summary
public TimeFormatter()


      
        synchronized (TimeFormatter.class) {
            Locale locale = Locale.getDefault();

            if (sLocale == null || !(locale.equals(sLocale))) {
                sLocale = locale;
                sLocaleData = LocaleData.get(locale);

                Resources r = Resources.getSystem();
                sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
                sDateOnlyFormat = r.getString(com.android.internal.R.string.month_day_year);
                sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time);
            }

            this.dateTimeFormat = sDateTimeFormat;
            this.timeOnlyFormat = sTimeOnlyFormat;
            this.dateOnlyFormat = sDateOnlyFormat;
            localeData = sLocaleData;
        }
    
Methods Summary
private static booleanbrokenIsLower(char toCheck)
A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII codes in order to be compatible with the old native implementation.

        return toCheck >= 'a" && toCheck <= 'z";
    
private static booleanbrokenIsUpper(char toCheck)
A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII codes in order to be compatible with the old native implementation.

        return toCheck >= 'A" && toCheck <= 'Z";
    
private static charbrokenToLower(char input)
A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII codes in order to be compatible with the old native implementation.

        if (input >= 'A" && input <= 'Z") {
            return (char) (input - 'A" + 'a");
        }
        return input;
    
private static charbrokenToUpper(char input)
A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII codes in order to be compatible with the old native implementation.

        if (input >= 'a" && input <= 'z") {
            return (char) (input - 'a" + 'A");
        }
        return input;
    
public java.lang.Stringformat(java.lang.String pattern, ZoneInfo.WallTime wallTime, libcore.util.ZoneInfo zoneInfo)
Format the specified {@code wallTime} using {@code pattern}. The output is returned.

        try {
            StringBuilder stringBuilder = new StringBuilder();

            outputBuilder = stringBuilder;
            // This uses the US locale because number localization is handled separately (see below)
            // and locale sensitive strings are output directly using outputBuilder.
            numberFormatter = new Formatter(stringBuilder, Locale.US);

            formatInternal(pattern, wallTime, zoneInfo);
            String result = stringBuilder.toString();
            // This behavior is the source of a bug since some formats are defined as being
            // in ASCII and not localized.
            if (localeData.zeroDigit != '0") {
                result = localizeDigits(result);
            }
            return result;
        } finally {
            outputBuilder = null;
            numberFormatter = null;
        }
    
private voidformatInternal(java.lang.String pattern, ZoneInfo.WallTime wallTime, libcore.util.ZoneInfo zoneInfo)
Format the specified {@code wallTime} using {@code pattern}. The output is written to {@link #outputBuilder}.

        CharBuffer formatBuffer = CharBuffer.wrap(pattern);
        while (formatBuffer.remaining() > 0) {
            boolean outputCurrentChar = true;
            char currentChar = formatBuffer.get(formatBuffer.position());
            if (currentChar == '%") {
                outputCurrentChar = handleToken(formatBuffer, wallTime, zoneInfo);
            }
            if (outputCurrentChar) {
                outputBuilder.append(formatBuffer.get(formatBuffer.position()));
            }
            formatBuffer.position(formatBuffer.position() + 1);
        }
    
private static java.lang.StringgetFormat(int modifier, java.lang.String normal, java.lang.String underscore, java.lang.String dash, java.lang.String zero)

        switch (modifier) {
            case '_":
                return underscore;
            case '-":
                return dash;
            case '0":
                return zero;
        }
        return normal;
    
private booleanhandleToken(java.nio.CharBuffer formatBuffer, ZoneInfo.WallTime wallTime, libcore.util.ZoneInfo zoneInfo)


        // The char at formatBuffer.position() is expected to be '%' at this point.
        int modifier = 0;
        while (formatBuffer.remaining() > 1) {
            // Increment the position then get the new current char.
            formatBuffer.position(formatBuffer.position() + 1);
            char currentChar = formatBuffer.get(formatBuffer.position());
            switch (currentChar) {
                case 'A":
                    modifyAndAppend((wallTime.getWeekDay() < 0
                                    || wallTime.getWeekDay() >= DAYSPERWEEK)
                                    ? "?" : localeData.longWeekdayNames[wallTime.getWeekDay() + 1],
                            modifier);
                    return false;
                case 'a":
                    modifyAndAppend((wallTime.getWeekDay() < 0
                                    || wallTime.getWeekDay() >= DAYSPERWEEK)
                                    ? "?" : localeData.shortWeekdayNames[wallTime.getWeekDay() + 1],
                            modifier);
                    return false;
                case 'B":
                    if (modifier == '-") {
                        modifyAndAppend((wallTime.getMonth() < 0
                                        || wallTime.getMonth() >= MONSPERYEAR)
                                        ? "?"
                                        : localeData.longStandAloneMonthNames[wallTime.getMonth()],
                                modifier);
                    } else {
                        modifyAndAppend((wallTime.getMonth() < 0
                                        || wallTime.getMonth() >= MONSPERYEAR)
                                        ? "?" : localeData.longMonthNames[wallTime.getMonth()],
                                modifier);
                    }
                    return false;
                case 'b":
                case 'h":
                    modifyAndAppend((wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
                                    ? "?" : localeData.shortMonthNames[wallTime.getMonth()],
                            modifier);
                    return false;
                case 'C":
                    outputYear(wallTime.getYear(), true, false, modifier);
                    return false;
                case 'c":
                    formatInternal(dateTimeFormat, wallTime, zoneInfo);
                    return false;
                case 'D":
                    formatInternal("%m/%d/%y", wallTime, zoneInfo);
                    return false;
                case 'd":
                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
                            wallTime.getMonthDay());
                    return false;
                case 'E":
                case 'O":
                    // C99 locale modifiers are not supported.
                    continue;
                case '_":
                case '-":
                case '0":
                case '^":
                case '#":
                    modifier = currentChar;
                    continue;
                case 'e":
                    numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
                            wallTime.getMonthDay());
                    return false;
                case 'F":
                    formatInternal("%Y-%m-%d", wallTime, zoneInfo);
                    return false;
                case 'H":
                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
                            wallTime.getHour());
                    return false;
                case 'I":
                    int hour = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour);
                    return false;
                case 'j":
                    int yearDay = wallTime.getYearDay() + 1;
                    numberFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"),
                            yearDay);
                    return false;
                case 'k":
                    numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
                            wallTime.getHour());
                    return false;
                case 'l":
                    int n2 = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
                    numberFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2);
                    return false;
                case 'M":
                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
                            wallTime.getMinute());
                    return false;
                case 'm":
                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
                            wallTime.getMonth() + 1);
                    return false;
                case 'n":
                    outputBuilder.append('\n");
                    return false;
                case 'p":
                    modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
                            : localeData.amPm[0], modifier);
                    return false;
                case 'P":
                    modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
                            : localeData.amPm[0], FORCE_LOWER_CASE);
                    return false;
                case 'R":
                    formatInternal("%H:%M", wallTime, zoneInfo);
                    return false;
                case 'r":
                    formatInternal("%I:%M:%S %p", wallTime, zoneInfo);
                    return false;
                case 'S":
                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
                            wallTime.getSecond());
                    return false;
                case 's":
                    int timeInSeconds = wallTime.mktime(zoneInfo);
                    outputBuilder.append(Integer.toString(timeInSeconds));
                    return false;
                case 'T":
                    formatInternal("%H:%M:%S", wallTime, zoneInfo);
                    return false;
                case 't":
                    outputBuilder.append('\t");
                    return false;
                case 'U":
                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
                            (wallTime.getYearDay() + DAYSPERWEEK - wallTime.getWeekDay())
                                    / DAYSPERWEEK);
                    return false;
                case 'u":
                    int day = (wallTime.getWeekDay() == 0) ? DAYSPERWEEK : wallTime.getWeekDay();
                    numberFormatter.format("%d", day);
                    return false;
                case 'V":   /* ISO 8601 week number */
                case 'G":   /* ISO 8601 year (four digits) */
                case 'g":   /* ISO 8601 year (two digits) */
                {
                    int year = wallTime.getYear();
                    int yday = wallTime.getYearDay();
                    int wday = wallTime.getWeekDay();
                    int w;
                    while (true) {
                        int len = isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
                        // What yday (-3 ... 3) does the ISO year begin on?
                        int bot = ((yday + 11 - wday) % DAYSPERWEEK) - 3;
                        // What yday does the NEXT ISO year begin on?
                        int top = bot - (len % DAYSPERWEEK);
                        if (top < -3) {
                            top += DAYSPERWEEK;
                        }
                        top += len;
                        if (yday >= top) {
                            ++year;
                            w = 1;
                            break;
                        }
                        if (yday >= bot) {
                            w = 1 + ((yday - bot) / DAYSPERWEEK);
                            break;
                        }
                        --year;
                        yday += isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
                    }
                    if (currentChar == 'V") {
                        numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w);
                    } else if (currentChar == 'g") {
                        outputYear(year, false, true, modifier);
                    } else {
                        outputYear(year, true, true, modifier);
                    }
                    return false;
                }
                case 'v":
                    formatInternal("%e-%b-%Y", wallTime, zoneInfo);
                    return false;
                case 'W":
                    int n = (wallTime.getYearDay() + DAYSPERWEEK - (
                                    wallTime.getWeekDay() != 0 ? (wallTime.getWeekDay() - 1)
                                            : (DAYSPERWEEK - 1))) / DAYSPERWEEK;
                    numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
                    return false;
                case 'w":
                    numberFormatter.format("%d", wallTime.getWeekDay());
                    return false;
                case 'X":
                    formatInternal(timeOnlyFormat, wallTime, zoneInfo);
                    return false;
                case 'x":
                    formatInternal(dateOnlyFormat, wallTime, zoneInfo);
                    return false;
                case 'y":
                    outputYear(wallTime.getYear(), false, true, modifier);
                    return false;
                case 'Y":
                    outputYear(wallTime.getYear(), true, true, modifier);
                    return false;
                case 'Z":
                    if (wallTime.getIsDst() < 0) {
                        return false;
                    }
                    boolean isDst = wallTime.getIsDst() != 0;
                    modifyAndAppend(zoneInfo.getDisplayName(isDst, TimeZone.SHORT), modifier);
                    return false;
                case 'z": {
                    if (wallTime.getIsDst() < 0) {
                        return false;
                    }
                    int diff = wallTime.getGmtOffset();
                    char sign;
                    if (diff < 0) {
                        sign = '-";
                        diff = -diff;
                    } else {
                        sign = '+";
                    }
                    outputBuilder.append(sign);
                    diff /= SECSPERMIN;
                    diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR);
                    numberFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff);
                    return false;
                }
                case '+":
                    formatInternal("%a %b %e %H:%M:%S %Z %Y", wallTime, zoneInfo);
                    return false;
                case '%":
                    // If conversion char is undefined, behavior is undefined. Print out the
                    // character itself.
                default:
                    return true;
            }
        }
        return true;
    
private static booleanisLeap(int year)

        return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
    
private java.lang.StringlocalizeDigits(java.lang.String s)

        int length = s.length();
        int offsetToLocalizedDigits = localeData.zeroDigit - '0";
        StringBuilder result = new StringBuilder(length);
        for (int i = 0; i < length; ++i) {
            char ch = s.charAt(i);
            if (ch >= '0" && ch <= '9") {
                ch += offsetToLocalizedDigits;
            }
            result.append(ch);
        }
        return result.toString();
    
private voidmodifyAndAppend(java.lang.CharSequence str, int modifier)

        switch (modifier) {
            case FORCE_LOWER_CASE:
                for (int i = 0; i < str.length(); i++) {
                    outputBuilder.append(brokenToLower(str.charAt(i)));
                }
                break;
            case '^":
                for (int i = 0; i < str.length(); i++) {
                    outputBuilder.append(brokenToUpper(str.charAt(i)));
                }
                break;
            case '#":
                for (int i = 0; i < str.length(); i++) {
                    char c = str.charAt(i);
                    if (brokenIsUpper(c)) {
                        c = brokenToLower(c);
                    } else if (brokenIsLower(c)) {
                        c = brokenToUpper(c);
                    }
                    outputBuilder.append(c);
                }
                break;
            default:
                outputBuilder.append(str);
        }
    
private voidoutputYear(int value, boolean outputTop, boolean outputBottom, int modifier)

        int lead;
        int trail;

        final int DIVISOR = 100;
        trail = value % DIVISOR;
        lead = value / DIVISOR + trail / DIVISOR;
        trail %= DIVISOR;
        if (trail < 0 && lead > 0) {
            trail += DIVISOR;
            --lead;
        } else if (lead < 0 && trail > 0) {
            trail -= DIVISOR;
            ++lead;
        }
        if (outputTop) {
            if (lead == 0 && trail < 0) {
                outputBuilder.append("-0");
            } else {
                numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead);
            }
        }
        if (outputBottom) {
            int n = ((trail < 0) ? -trail : trail);
            numberFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
        }