use of android.icu.impl.DayPeriodRules in project j2objc by google.
the class SimpleDateFormat method subFormat.
/**
* Formats a single field; useFastFormat variant. Reuses a
* StringBuffer for results instead of creating a String on the
* heap for each call.
*
* NOTE We don't really need the beginOffset parameter, EXCEPT for
* the need to support the slow subFormat variant (above) which
* has to pass it in to us.
*
* @deprecated This API is ICU internal only.
* @hide original deprecated declaration
* @hide draft / provisional / internal are hidden on Android
*/
@Deprecated
@SuppressWarnings("fallthrough")
protected void subFormat(StringBuffer buf, char ch, int count, int beginOffset, int fieldNum, DisplayContext capitalizationContext, FieldPosition pos, Calendar cal) {
final int maxIntCount = Integer.MAX_VALUE;
final int bufstart = buf.length();
TimeZone tz = cal.getTimeZone();
long date = cal.getTimeInMillis();
String result = null;
int patternCharIndex = getIndexFromChar(ch);
if (patternCharIndex == -1) {
if (ch == 'l') {
// (SMALL LETTER L) deprecated placeholder for leap month marker, ignore
return;
} else {
throw new IllegalArgumentException("Illegal pattern character " + "'" + ch + "' in \"" + pattern + '"');
}
}
final int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
int value = 0;
// Don't get value unless it is useful
if (field >= 0) {
value = (patternCharIndex != DateFormat.RELATED_YEAR) ? cal.get(field) : cal.getRelatedYear();
}
NumberFormat currentNumberFormat = getNumberFormat(ch);
DateFormatSymbols.CapitalizationContextUsage capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.OTHER;
switch(patternCharIndex) {
case // 'G' - ERA
0:
if (cal.getType().equals("chinese") || cal.getType().equals("dangi")) {
// moved from ChineseDateFormat
zeroPaddingNumber(currentNumberFormat, buf, value, 1, 9);
} else {
if (count == 5) {
safeAppend(formatData.narrowEras, value, buf);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.ERA_NARROW;
} else if (count == 4) {
safeAppend(formatData.eraNames, value, buf);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.ERA_WIDE;
} else {
safeAppend(formatData.eras, value, buf);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.ERA_ABBREV;
}
}
break;
case // 'U' - YEAR_NAME_FIELD
30:
if (formatData.shortYearNames != null && value <= formatData.shortYearNames.length) {
safeAppend(formatData.shortYearNames, value - 1, buf);
break;
}
// 'y' - YEAR
case 1:
case // 'Y' - YEAR_WOY
18:
if (override != null && (override.compareTo("hebr") == 0 || override.indexOf("y=hebr") >= 0) && value > HEBREW_CAL_CUR_MILLENIUM_START_YEAR && value < HEBREW_CAL_CUR_MILLENIUM_END_YEAR) {
value -= HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
}
/* According to the specification, if the number of pattern letters ('y') is 2,
* the year is truncated to 2 digits; otherwise it is interpreted as a number.
* But the original code process 'y', 'yy', 'yyy' in the same way. and process
* patterns with 4 or more than 4 'y' characters in the same way.
* So I change the codes to meet the specification. [Richard/GCl]
*/
if (count == 2) {
// clip 1996 to 96
zeroPaddingNumber(currentNumberFormat, buf, value, 2, 2);
} else {
// count = 1 or count > 2
zeroPaddingNumber(currentNumberFormat, buf, value, count, maxIntCount);
}
break;
// 'M' - MONTH
case 2:
case // 'L' - STANDALONE MONTH
26:
if (cal.getType().equals("hebrew")) {
boolean isLeap = HebrewCalendar.isLeapYear(cal.get(Calendar.YEAR));
if (isLeap && value == 6 && count >= 3) {
// Show alternate form for Adar II in leap years in Hebrew calendar.
value = 13;
}
if (!isLeap && value >= 6 && count < 3) {
// Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
value--;
}
}
int isLeapMonth = (formatData.leapMonthPatterns != null && formatData.leapMonthPatterns.length >= DateFormatSymbols.DT_MONTH_PATTERN_COUNT) ? cal.get(Calendar.IS_LEAP_MONTH) : 0;
// should consolidate the next section by using arrays of pointers & counts for the right symbols...
if (count == 5) {
if (patternCharIndex == 2) {
safeAppendWithMonthPattern(formatData.narrowMonths, value, buf, (isLeapMonth != 0) ? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_FORMAT_NARROW] : null);
} else {
safeAppendWithMonthPattern(formatData.standaloneNarrowMonths, value, buf, (isLeapMonth != 0) ? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_STANDALONE_NARROW] : null);
}
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.MONTH_NARROW;
} else if (count == 4) {
if (patternCharIndex == 2) {
safeAppendWithMonthPattern(formatData.months, value, buf, (isLeapMonth != 0) ? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_FORMAT_WIDE] : null);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.MONTH_FORMAT;
} else {
safeAppendWithMonthPattern(formatData.standaloneMonths, value, buf, (isLeapMonth != 0) ? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_STANDALONE_WIDE] : null);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.MONTH_STANDALONE;
}
} else if (count == 3) {
if (patternCharIndex == 2) {
safeAppendWithMonthPattern(formatData.shortMonths, value, buf, (isLeapMonth != 0) ? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV] : null);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.MONTH_FORMAT;
} else {
safeAppendWithMonthPattern(formatData.standaloneShortMonths, value, buf, (isLeapMonth != 0) ? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_STANDALONE_ABBREV] : null);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.MONTH_STANDALONE;
}
} else {
StringBuffer monthNumber = new StringBuffer();
zeroPaddingNumber(currentNumberFormat, monthNumber, value + 1, count, maxIntCount);
String[] monthNumberStrings = new String[1];
monthNumberStrings[0] = monthNumber.toString();
safeAppendWithMonthPattern(monthNumberStrings, 0, buf, (isLeapMonth != 0) ? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_NUMERIC] : null);
}
break;
case // 'k' - HOUR_OF_DAY (1..24)
4:
if (value == 0) {
zeroPaddingNumber(currentNumberFormat, buf, cal.getMaximum(Calendar.HOUR_OF_DAY) + 1, count, maxIntCount);
} else {
zeroPaddingNumber(currentNumberFormat, buf, value, count, maxIntCount);
}
break;
case // 'S' - FRACTIONAL_SECOND
8:
// Fractional seconds left-justify
{
numberFormat.setMinimumIntegerDigits(Math.min(3, count));
numberFormat.setMaximumIntegerDigits(maxIntCount);
if (count == 1) {
value /= 100;
} else if (count == 2) {
value /= 10;
}
FieldPosition p = new FieldPosition(-1);
numberFormat.format(value, buf, p);
if (count > 3) {
numberFormat.setMinimumIntegerDigits(count - 3);
numberFormat.format(0L, buf, p);
}
}
break;
case // 'e' - DOW_LOCAL (use DOW_LOCAL for numeric, DAY_OF_WEEK for format names)
19:
if (count < 3) {
zeroPaddingNumber(currentNumberFormat, buf, value, count, maxIntCount);
break;
}
// For alpha day-of-week, we don't want DOW_LOCAL,
// we need the standard DAY_OF_WEEK.
value = cal.get(Calendar.DAY_OF_WEEK);
// fall through, do not break here
case // 'E' - DAY_OF_WEEK
9:
if (count == 5) {
safeAppend(formatData.narrowWeekdays, value, buf);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_NARROW;
} else if (count == 4) {
safeAppend(formatData.weekdays, value, buf);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_FORMAT;
} else if (count == 6 && formatData.shorterWeekdays != null) {
safeAppend(formatData.shorterWeekdays, value, buf);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_FORMAT;
} else {
// count <= 3, use abbreviated form if exists
safeAppend(formatData.shortWeekdays, value, buf);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_FORMAT;
}
break;
case // 'a' - AM_PM
14:
// formatData.ampmsNarrow may be null when deserializing DateFormatSymbolsfrom old version
if (count < 5 || formatData.ampmsNarrow == null) {
safeAppend(formatData.ampms, value, buf);
} else {
safeAppend(formatData.ampmsNarrow, value, buf);
}
break;
case // 'h' - HOUR (1..12)
15:
if (value == 0) {
zeroPaddingNumber(currentNumberFormat, buf, cal.getLeastMaximum(Calendar.HOUR) + 1, count, maxIntCount);
} else {
zeroPaddingNumber(currentNumberFormat, buf, value, count, maxIntCount);
}
break;
case // 'z' - TIMEZONE_FIELD
17:
if (count < 4) {
// "z", "zz", "zzz"
result = tzFormat().format(Style.SPECIFIC_SHORT, tz, date);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.METAZONE_SHORT;
} else {
result = tzFormat().format(Style.SPECIFIC_LONG, tz, date);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.METAZONE_LONG;
}
buf.append(result);
break;
case // 'Z' - TIMEZONE_RFC_FIELD
23:
if (count < 4) {
// RFC822 format - equivalent to ISO 8601 local offset fixed width format
result = tzFormat().format(Style.ISO_BASIC_LOCAL_FULL, tz, date);
} else if (count == 5) {
// ISO 8601 extended format
result = tzFormat().format(Style.ISO_EXTENDED_FULL, tz, date);
} else {
// long form, localized GMT pattern
result = tzFormat().format(Style.LOCALIZED_GMT, tz, date);
}
buf.append(result);
break;
case // 'v' - TIMEZONE_GENERIC_FIELD
24:
if (count == 1) {
// "v"
result = tzFormat().format(Style.GENERIC_SHORT, tz, date);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.METAZONE_SHORT;
} else if (count == 4) {
// "vvvv"
result = tzFormat().format(Style.GENERIC_LONG, tz, date);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.METAZONE_LONG;
}
buf.append(result);
break;
case // 'V' - TIMEZONE_SPECIAL_FIELD
29:
if (count == 1) {
// "V"
result = tzFormat().format(Style.ZONE_ID_SHORT, tz, date);
} else if (count == 2) {
// "VV"
result = tzFormat().format(Style.ZONE_ID, tz, date);
} else if (count == 3) {
// "VVV"
result = tzFormat().format(Style.EXEMPLAR_LOCATION, tz, date);
} else if (count == 4) {
// "VVVV"
result = tzFormat().format(Style.GENERIC_LOCATION, tz, date);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.ZONE_LONG;
}
buf.append(result);
break;
case // 'O' - TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
31:
if (count == 1) {
// "O" - Short Localized GMT format
result = tzFormat().format(Style.LOCALIZED_GMT_SHORT, tz, date);
} else if (count == 4) {
// "OOOO" - Localized GMT format
result = tzFormat().format(Style.LOCALIZED_GMT, tz, date);
}
buf.append(result);
break;
case // 'X' - TIMEZONE_ISO_FIELD
32:
if (count == 1) {
// "X" - ISO Basic/Short
result = tzFormat().format(Style.ISO_BASIC_SHORT, tz, date);
} else if (count == 2) {
// "XX" - ISO Basic/Fixed
result = tzFormat().format(Style.ISO_BASIC_FIXED, tz, date);
} else if (count == 3) {
// "XXX" - ISO Extended/Fixed
result = tzFormat().format(Style.ISO_EXTENDED_FIXED, tz, date);
} else if (count == 4) {
// "XXXX" - ISO Basic/Optional second field
result = tzFormat().format(Style.ISO_BASIC_FULL, tz, date);
} else if (count == 5) {
// "XXXXX" - ISO Extended/Optional second field
result = tzFormat().format(Style.ISO_EXTENDED_FULL, tz, date);
}
buf.append(result);
break;
case // 'x' - TIMEZONE_ISO_LOCAL_FIELD
33:
if (count == 1) {
// "x" - ISO Local Basic/Short
result = tzFormat().format(Style.ISO_BASIC_LOCAL_SHORT, tz, date);
} else if (count == 2) {
// "x" - ISO Local Basic/Fixed
result = tzFormat().format(Style.ISO_BASIC_LOCAL_FIXED, tz, date);
} else if (count == 3) {
// "xxx" - ISO Local Extended/Fixed
result = tzFormat().format(Style.ISO_EXTENDED_LOCAL_FIXED, tz, date);
} else if (count == 4) {
// "xxxx" - ISO Local Basic/Optional second field
result = tzFormat().format(Style.ISO_BASIC_LOCAL_FULL, tz, date);
} else if (count == 5) {
// "xxxxx" - ISO Local Extended/Optional second field
result = tzFormat().format(Style.ISO_EXTENDED_LOCAL_FULL, tz, date);
}
buf.append(result);
break;
case // 'c' - STANDALONE DAY (use DOW_LOCAL for numeric, DAY_OF_WEEK for standalone)
25:
if (count < 3) {
zeroPaddingNumber(currentNumberFormat, buf, value, 1, maxIntCount);
break;
}
// For alpha day-of-week, we don't want DOW_LOCAL,
// we need the standard DAY_OF_WEEK.
value = cal.get(Calendar.DAY_OF_WEEK);
if (count == 5) {
safeAppend(formatData.standaloneNarrowWeekdays, value, buf);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_NARROW;
} else if (count == 4) {
safeAppend(formatData.standaloneWeekdays, value, buf);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_STANDALONE;
} else if (count == 6 && formatData.standaloneShorterWeekdays != null) {
safeAppend(formatData.standaloneShorterWeekdays, value, buf);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_STANDALONE;
} else {
// count == 3
safeAppend(formatData.standaloneShortWeekdays, value, buf);
capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_STANDALONE;
}
break;
case // 'Q' - QUARTER
27:
if (count >= 4) {
safeAppend(formatData.quarters, value / 3, buf);
} else if (count == 3) {
safeAppend(formatData.shortQuarters, value / 3, buf);
} else {
zeroPaddingNumber(currentNumberFormat, buf, (value / 3) + 1, count, maxIntCount);
}
break;
case // 'q' - STANDALONE QUARTER
28:
if (count >= 4) {
safeAppend(formatData.standaloneQuarters, value / 3, buf);
} else if (count == 3) {
safeAppend(formatData.standaloneShortQuarters, value / 3, buf);
} else {
zeroPaddingNumber(currentNumberFormat, buf, (value / 3) + 1, count, maxIntCount);
}
break;
case // 'b' - am/pm/noon/midnight
35:
{
// Note: "midnight" can be ambiguous as to whether it refers to beginning of day or end of day.
// For ICU 57 output of "midnight" is temporarily suppressed.
int hour = cal.get(Calendar.HOUR_OF_DAY);
String toAppend = null;
// This means minutes and seconds, if present, must be zero.
if ((/*hour == 0 ||*/
hour == 12) && (!hasMinute || cal.get(Calendar.MINUTE) == 0) && (!hasSecond || cal.get(Calendar.SECOND) == 0)) {
// Stealing am/pm value to use as our array index.
// It works out: am/midnight are both 0, pm/noon are both 1,
// 12 am is 12 midnight, and 12 pm is 12 noon.
value = cal.get(Calendar.AM_PM);
if (count == 3) {
toAppend = formatData.abbreviatedDayPeriods[value];
} else if (count == 4 || count > 5) {
toAppend = formatData.wideDayPeriods[value];
} else {
// count == 5
toAppend = formatData.narrowDayPeriods[value];
}
}
if (toAppend == null) {
// Time isn't exactly midnight or noon (as displayed) or localized string doesn't
// exist for requested period. Fall back to am/pm instead.
subFormat(buf, 'a', count, beginOffset, fieldNum, capitalizationContext, pos, cal);
} else {
buf.append(toAppend);
}
break;
}
case // 'B' - flexible day period
36:
{
// TODO: Maybe fetch the DayperiodRules during initialization (instead of at the first
// loading of an instance) if a relevant pattern character (b or B) is used.
DayPeriodRules ruleSet = DayPeriodRules.getInstance(getLocale());
if (ruleSet == null) {
// Data doesn't exist for the locale we're looking for.
// Fall back to am/pm.
subFormat(buf, 'a', count, beginOffset, fieldNum, capitalizationContext, pos, cal);
break;
}
// Get current display time.
int hour = cal.get(Calendar.HOUR_OF_DAY);
int minute = 0;
int second = 0;
if (hasMinute) {
minute = cal.get(Calendar.MINUTE);
}
if (hasSecond) {
second = cal.get(Calendar.SECOND);
}
// Determine day period.
DayPeriodRules.DayPeriod periodType;
if (hour == 0 && minute == 0 && second == 0 && ruleSet.hasMidnight()) {
periodType = DayPeriodRules.DayPeriod.MIDNIGHT;
} else if (hour == 12 && minute == 0 && second == 0 && ruleSet.hasNoon()) {
periodType = DayPeriodRules.DayPeriod.NOON;
} else {
periodType = ruleSet.getDayPeriodForHour(hour);
}
// Get localized string.
assert (periodType != null);
String toAppend = null;
int index;
if (periodType != DayPeriodRules.DayPeriod.AM && periodType != DayPeriodRules.DayPeriod.PM && periodType != DayPeriodRules.DayPeriod.MIDNIGHT) {
index = periodType.ordinal();
if (count <= 3) {
// i.e. short
toAppend = formatData.abbreviatedDayPeriods[index];
} else if (count == 4 || count > 5) {
toAppend = formatData.wideDayPeriods[index];
} else {
// count == 5
toAppend = formatData.narrowDayPeriods[index];
}
}
// Midnight/Noon -> General Periods.
if (toAppend == null && (periodType == DayPeriodRules.DayPeriod.MIDNIGHT || periodType == DayPeriodRules.DayPeriod.NOON)) {
periodType = ruleSet.getDayPeriodForHour(hour);
index = periodType.ordinal();
if (count <= 3) {
// i.e. short
toAppend = formatData.abbreviatedDayPeriods[index];
} else if (count == 4 || count > 5) {
toAppend = formatData.wideDayPeriods[index];
} else {
// count == 5
toAppend = formatData.narrowDayPeriods[index];
}
}
// General Periods -> AM/PM.
if (periodType == DayPeriodRules.DayPeriod.AM || periodType == DayPeriodRules.DayPeriod.PM || toAppend == null) {
subFormat(buf, 'a', count, beginOffset, fieldNum, capitalizationContext, pos, cal);
} else {
buf.append(toAppend);
}
break;
}
case // TIME SEPARATOR (no pattern character currently defined, we should
37:
// not get here but leave support in for future definition.
buf.append(formatData.getTimeSeparatorString());
break;
default:
// case 3: // 'd' - DATE
// case 5: // 'H' - HOUR_OF_DAY (0..23)
// case 6: // 'm' - MINUTE
// case 7: // 's' - SECOND
// case 10: // 'D' - DAY_OF_YEAR
// case 11: // 'F' - DAY_OF_WEEK_IN_MONTH
// case 12: // 'w' - WEEK_OF_YEAR
// case 13: // 'W' - WEEK_OF_MONTH
// case 16: // 'K' - HOUR (0..11)
// case 20: // 'u' - EXTENDED_YEAR
// case 21: // 'g' - JULIAN_DAY
// case 22: // 'A' - MILLISECONDS_IN_DAY
zeroPaddingNumber(currentNumberFormat, buf, value, count, maxIntCount);
break;
}
if (fieldNum == 0 && capitalizationContext != null && UCharacter.isLowerCase(buf.codePointAt(bufstart))) {
boolean titlecase = false;
switch(capitalizationContext) {
case CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE:
titlecase = true;
break;
case CAPITALIZATION_FOR_UI_LIST_OR_MENU:
case CAPITALIZATION_FOR_STANDALONE:
if (formatData.capitalization != null) {
boolean[] transforms = formatData.capitalization.get(capContextUsageType);
titlecase = (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU) ? transforms[0] : transforms[1];
}
break;
default:
break;
}
if (titlecase) {
if (capitalizationBrkIter == null) {
// should only happen when deserializing, etc.
capitalizationBrkIter = BreakIterator.getSentenceInstance(locale);
}
// bufstart or beginOffset, should be the same
String firstField = buf.substring(bufstart);
String firstFieldTitleCase = UCharacter.toTitleCase(locale, firstField, capitalizationBrkIter, UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
buf.replace(bufstart, buf.length(), firstFieldTitleCase);
}
}
// Set the FieldPosition (for the first occurrence only)
if (pos.getBeginIndex() == pos.getEndIndex()) {
if (pos.getField() == PATTERN_INDEX_TO_DATE_FORMAT_FIELD[patternCharIndex]) {
pos.setBeginIndex(beginOffset);
pos.setEndIndex(beginOffset + buf.length() - bufstart);
} else if (pos.getFieldAttribute() == PATTERN_INDEX_TO_DATE_FORMAT_ATTRIBUTE[patternCharIndex]) {
pos.setBeginIndex(beginOffset);
pos.setEndIndex(beginOffset + buf.length() - bufstart);
}
}
}
use of android.icu.impl.DayPeriodRules in project j2objc by google.
the class SimpleDateFormat method parse.
/**
* Overrides DateFormat
* @see DateFormat
*/
@Override
public void parse(String text, Calendar cal, ParsePosition parsePos) {
TimeZone backupTZ = null;
Calendar resultCal = null;
if (cal != calendar && !cal.getType().equals(calendar.getType())) {
// Different calendar type
// We use the time/zone from the input calendar, but
// do not use the input calendar for field calculation.
calendar.setTimeInMillis(cal.getTimeInMillis());
backupTZ = calendar.getTimeZone();
calendar.setTimeZone(cal.getTimeZone());
resultCal = cal;
cal = calendar;
}
int pos = parsePos.getIndex();
if (pos < 0) {
parsePos.setErrorIndex(0);
return;
}
int start = pos;
// Hold the day period until everything else is parsed, because we need
// the hour to interpret time correctly.
// Using an one-element array for output parameter.
Output<DayPeriodRules.DayPeriod> dayPeriod = new Output<DayPeriodRules.DayPeriod>(null);
Output<TimeType> tzTimeType = new Output<TimeType>(TimeType.UNKNOWN);
boolean[] ambiguousYear = { false };
// item index for the first numeric field within a contiguous numeric run
int numericFieldStart = -1;
// item length for the first numeric field within a contiguous numeric run
int numericFieldLength = 0;
// start index of numeric text run in the input text
int numericStartPos = 0;
MessageFormat numericLeapMonthFormatter = null;
if (formatData.leapMonthPatterns != null && formatData.leapMonthPatterns.length >= DateFormatSymbols.DT_MONTH_PATTERN_COUNT) {
numericLeapMonthFormatter = new MessageFormat(formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_NUMERIC], locale);
}
Object[] items = getPatternItems();
int i = 0;
while (i < items.length) {
if (items[i] instanceof PatternItem) {
// Handle pattern field
PatternItem field = (PatternItem) items[i];
if (field.isNumeric) {
// try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
if (numericFieldStart == -1) {
// check if this field is followed by abutting another numeric field
if ((i + 1) < items.length && (items[i + 1] instanceof PatternItem) && ((PatternItem) items[i + 1]).isNumeric) {
// record the first numeric field within a numeric text run
numericFieldStart = i;
numericFieldLength = field.length;
numericStartPos = pos;
}
}
}
if (numericFieldStart != -1) {
// Handle a numeric field within abutting numeric fields
int len = field.length;
if (numericFieldStart == i) {
len = numericFieldLength;
}
// Parse a numeric field
pos = subParse(text, pos, field.type, len, true, false, ambiguousYear, cal, numericLeapMonthFormatter, tzTimeType);
if (pos < 0) {
// If the parse fails anywhere in the numeric run, back up to the
// start of the run and use shorter pattern length for the first
// numeric field.
--numericFieldLength;
if (numericFieldLength == 0) {
// can not make shorter any more
parsePos.setIndex(start);
parsePos.setErrorIndex(pos);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
i = numericFieldStart;
pos = numericStartPos;
continue;
}
} else if (field.type != 'l') {
// (SMALL LETTER L) obsolete pattern char just gets ignored
// Handle a non-numeric field or a non-abutting numeric field
numericFieldStart = -1;
int s = pos;
pos = subParse(text, pos, field.type, field.length, false, true, ambiguousYear, cal, numericLeapMonthFormatter, tzTimeType, dayPeriod);
if (pos < 0) {
if (pos == ISOSpecialEra) {
// era not present, in special cases allow this to continue
pos = s;
if (i + 1 < items.length) {
String patl = null;
// if it will cause a class cast exception to String, we can't use it
try {
patl = (String) items[i + 1];
} catch (ClassCastException cce) {
parsePos.setIndex(start);
parsePos.setErrorIndex(s);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
// get next item in pattern
if (patl == null)
patl = (String) items[i + 1];
int plen = patl.length();
int idx = 0;
// Skip contiguous white spaces.
while (idx < plen) {
char pch = patl.charAt(idx);
if (PatternProps.isWhiteSpace(pch))
idx++;
else
break;
}
// if next item in pattern is all whitespace, skip it
if (idx == plen) {
i++;
}
}
} else {
parsePos.setIndex(start);
parsePos.setErrorIndex(s);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
}
}
} else {
// Handle literal pattern text literal
numericFieldStart = -1;
boolean[] complete = new boolean[1];
pos = matchLiteral(text, pos, items, i, complete);
if (!complete[0]) {
// Set the position of mismatch
parsePos.setIndex(start);
parsePos.setErrorIndex(pos);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
}
++i;
}
// Special hack for trailing "." after non-numeric field.
if (pos < text.length()) {
char extra = text.charAt(pos);
if (extra == '.' && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_WHITESPACE) && items.length != 0) {
// only do if the last field is not numeric
Object lastItem = items[items.length - 1];
if (lastItem instanceof PatternItem && !((PatternItem) lastItem).isNumeric) {
// skip the extra "."
pos++;
}
}
}
// If dayPeriod is set, use it in conjunction with hour-of-day to determine am/pm.
if (dayPeriod.value != null) {
DayPeriodRules ruleSet = DayPeriodRules.getInstance(getLocale());
if (!cal.isSet(Calendar.HOUR) && !cal.isSet(Calendar.HOUR_OF_DAY)) {
// If hour is not set, set time to the midpoint of current day period, overwriting
// minutes if it's set.
double midPoint = ruleSet.getMidPointForDayPeriod(dayPeriod.value);
// Truncate midPoint toward zero to get the hour.
// Any leftover means it was a half-hour.
int midPointHour = (int) midPoint;
int midPointMinute = (midPoint - midPointHour) > 0 ? 30 : 0;
// No need to set am/pm because hour-of-day is set last therefore takes precedence.
cal.set(Calendar.HOUR_OF_DAY, midPointHour);
cal.set(Calendar.MINUTE, midPointMinute);
} else {
int hourOfDay;
if (cal.isSet(Calendar.HOUR_OF_DAY)) {
// Hour is parsed in 24-hour format.
hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
} else {
// Hour is parsed in 12-hour format.
hourOfDay = cal.get(Calendar.HOUR);
// so 0 unambiguously means a 24-hour time from above.
if (hourOfDay == 0) {
hourOfDay = 12;
}
}
assert (0 <= hourOfDay && hourOfDay <= 23);
// If hour-of-day is 0 or 13 thru 23 then input time in unambiguously in 24-hour format.
if (hourOfDay == 0 || (13 <= hourOfDay && hourOfDay <= 23)) {
// Make hour-of-day take precedence over (hour + am/pm) by setting it again.
cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
} else {
// - cal.get(MINUTE) will return 0 if MINUTE is unset, which works.
if (hourOfDay == 12) {
hourOfDay = 0;
}
double currentHour = hourOfDay + cal.get(Calendar.MINUTE) / 60.0;
double midPointHour = ruleSet.getMidPointForDayPeriod(dayPeriod.value);
double hoursAheadMidPoint = currentHour - midPointHour;
// Assume current time is in the AM.
if (-6 <= hoursAheadMidPoint && hoursAheadMidPoint < 6) {
// Assumption holds; set time as such.
cal.set(Calendar.AM_PM, 0);
} else {
cal.set(Calendar.AM_PM, 1);
}
}
}
}
// At this point the fields of Calendar have been set. Calendar
// will fill in default values for missing fields when the time
// is computed.
parsePos.setIndex(pos);
// the year correctly to start with in other cases -- see subParse().
try {
TimeType tztype = tzTimeType.value;
if (ambiguousYear[0] || tztype != TimeType.UNKNOWN) {
// We need a copy of the fields, and we need to avoid triggering a call to
// complete(), which will recalculate the fields. Since we can't access
// the fields[] array in Calendar, we clone the entire object. This will
// stop working if Calendar.clone() is ever rewritten to call complete().
Calendar copy;
if (ambiguousYear[0]) {
// the two-digit year == the default start year
copy = (Calendar) cal.clone();
Date parsedDate = copy.getTime();
if (parsedDate.before(getDefaultCenturyStart())) {
// We can't use add here because that does a complete() first.
cal.set(Calendar.YEAR, getDefaultCenturyStartYear() + 100);
}
}
if (tztype != TimeType.UNKNOWN) {
copy = (Calendar) cal.clone();
TimeZone tz = copy.getTimeZone();
BasicTimeZone btz = null;
if (tz instanceof BasicTimeZone) {
btz = (BasicTimeZone) tz;
}
// Get local millis
copy.set(Calendar.ZONE_OFFSET, 0);
copy.set(Calendar.DST_OFFSET, 0);
long localMillis = copy.getTimeInMillis();
// Make sure parsed time zone type (Standard or Daylight)
// matches the rule used by the parsed time zone.
int[] offsets = new int[2];
if (btz != null) {
if (tztype == TimeType.STANDARD) {
btz.getOffsetFromLocal(localMillis, BasicTimeZone.LOCAL_STD, BasicTimeZone.LOCAL_STD, offsets);
} else {
btz.getOffsetFromLocal(localMillis, BasicTimeZone.LOCAL_DST, BasicTimeZone.LOCAL_DST, offsets);
}
} else {
// No good way to resolve ambiguous time at transition,
// but following code work in most case.
tz.getOffset(localMillis, true, offsets);
if (tztype == TimeType.STANDARD && offsets[1] != 0 || tztype == TimeType.DAYLIGHT && offsets[1] == 0) {
// Roll back one day and try it again.
// Note: This code assumes 1. timezone transition only happens
// once within 24 hours at max
// 2. the difference of local offsets at the transition is
// less than 24 hours.
tz.getOffset(localMillis - (24 * 60 * 60 * 1000), true, offsets);
}
}
// Now, compare the results with parsed type, either standard or
// daylight saving time
int resolvedSavings = offsets[1];
if (tztype == TimeType.STANDARD) {
if (offsets[1] != 0) {
// Override DST_OFFSET = 0 in the result calendar
resolvedSavings = 0;
}
} else {
// tztype == TZTYPE_DST
if (offsets[1] == 0) {
if (btz != null) {
long time = localMillis + offsets[0];
// We use the nearest daylight saving time rule.
TimeZoneTransition beforeTrs, afterTrs;
long beforeT = time, afterT = time;
int beforeSav = 0, afterSav = 0;
// Search for DST rule before or on the time
while (true) {
beforeTrs = btz.getPreviousTransition(beforeT, true);
if (beforeTrs == null) {
break;
}
beforeT = beforeTrs.getTime() - 1;
beforeSav = beforeTrs.getFrom().getDSTSavings();
if (beforeSav != 0) {
break;
}
}
// Search for DST rule after the time
while (true) {
afterTrs = btz.getNextTransition(afterT, false);
if (afterTrs == null) {
break;
}
afterT = afterTrs.getTime();
afterSav = afterTrs.getTo().getDSTSavings();
if (afterSav != 0) {
break;
}
}
if (beforeTrs != null && afterTrs != null) {
if (time - beforeT > afterT - time) {
resolvedSavings = afterSav;
} else {
resolvedSavings = beforeSav;
}
} else if (beforeTrs != null && beforeSav != 0) {
resolvedSavings = beforeSav;
} else if (afterTrs != null && afterSav != 0) {
resolvedSavings = afterSav;
} else {
resolvedSavings = btz.getDSTSavings();
}
} else {
resolvedSavings = tz.getDSTSavings();
}
if (resolvedSavings == 0) {
// Final fallback
resolvedSavings = millisPerHour;
}
}
}
cal.set(Calendar.ZONE_OFFSET, offsets[0]);
cal.set(Calendar.DST_OFFSET, resolvedSavings);
}
}
}// if any fields are out of range, e.g., MONTH == 17.
catch (IllegalArgumentException e) {
parsePos.setErrorIndex(pos);
parsePos.setIndex(start);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
// instead of the input calendar
if (resultCal != null) {
resultCal.setTimeZone(cal.getTimeZone());
resultCal.setTimeInMillis(cal.getTimeInMillis());
}
// Restore the original time zone if required
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
}
Aggregations