TimeFormatterpublic 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 | sLocaleThe 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 boolean | brokenIsLower(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 boolean | brokenIsUpper(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 char | brokenToLower(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 char | brokenToUpper(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.String | format(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 void | formatInternal(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.String | getFormat(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 boolean | handleToken(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 boolean | isLeap(int year)
return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
| private java.lang.String | localizeDigits(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 void | modifyAndAppend(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 void | outputYear(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);
}
|
|