Search in sources :

Example 1 with DayPeriodRules

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);
        }
    }
}
Also used : DayPeriodRules(android.icu.impl.DayPeriodRules) AttributedString(java.text.AttributedString) FieldPosition(java.text.FieldPosition) TimeZone(android.icu.util.TimeZone) BasicTimeZone(android.icu.util.BasicTimeZone) DateNumberFormat(android.icu.impl.DateNumberFormat)

Example 2 with DayPeriodRules

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);
    }
}
Also used : DayPeriodRules(android.icu.impl.DayPeriodRules) HebrewCalendar(android.icu.util.HebrewCalendar) Calendar(android.icu.util.Calendar) AttributedString(java.text.AttributedString) Date(java.util.Date) TimeType(android.icu.text.TimeZoneFormat.TimeType) TimeZone(android.icu.util.TimeZone) BasicTimeZone(android.icu.util.BasicTimeZone) BasicTimeZone(android.icu.util.BasicTimeZone) TimeZoneTransition(android.icu.util.TimeZoneTransition) Output(android.icu.util.Output)

Aggregations

DayPeriodRules (android.icu.impl.DayPeriodRules)2 BasicTimeZone (android.icu.util.BasicTimeZone)2 TimeZone (android.icu.util.TimeZone)2 AttributedString (java.text.AttributedString)2 DateNumberFormat (android.icu.impl.DateNumberFormat)1 TimeType (android.icu.text.TimeZoneFormat.TimeType)1 Calendar (android.icu.util.Calendar)1 HebrewCalendar (android.icu.util.HebrewCalendar)1 Output (android.icu.util.Output)1 TimeZoneTransition (android.icu.util.TimeZoneTransition)1 FieldPosition (java.text.FieldPosition)1 Date (java.util.Date)1