Search in sources :

Example 6 with Output

use of android.icu.util.Output in project j2objc by google.

the class TimeZoneFormat method parse.

/**
 * Returns a <code>TimeZone</code> by parsing the time zone string according to
 * the parse position, the style and the parse options.
 *
 * @param text the text contains a time zone string at the position.
 * @param style the format style.
 * @param pos the position.
 * @param options the parse options.
 * @param timeType The output argument for receiving the time type (standard/daylight/unknown),
 * or specify null if the information is not necessary.
 * @return A <code>TimeZone</code>, or null if the input could not be parsed.
 * @see Style
 * @see #format(Style, TimeZone, long, Output)
 */
public TimeZone parse(Style style, String text, ParsePosition pos, EnumSet<ParseOption> options, Output<TimeType> timeType) {
    if (timeType == null) {
        timeType = new Output<TimeType>(TimeType.UNKNOWN);
    } else {
        timeType.value = TimeType.UNKNOWN;
    }
    int startIdx = pos.getIndex();
    int maxPos = text.length();
    int offset;
    // Styles using localized GMT format as fallback
    boolean fallbackLocalizedGMT = (style == Style.SPECIFIC_LONG || style == Style.GENERIC_LONG || style == Style.GENERIC_LOCATION);
    boolean fallbackShortLocalizedGMT = (style == Style.SPECIFIC_SHORT || style == Style.GENERIC_SHORT);
    // bit flags representing already evaluated styles
    int evaluated = 0;
    ParsePosition tmpPos = new ParsePosition(startIdx);
    // stores successfully parsed offset for later use
    int parsedOffset = UNKNOWN_OFFSET;
    // stores successfully parsed offset position for later use
    int parsedPos = -1;
    // Try localized GMT format first if necessary
    if (fallbackLocalizedGMT || fallbackShortLocalizedGMT) {
        Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
        offset = parseOffsetLocalizedGMT(text, tmpPos, fallbackShortLocalizedGMT, hasDigitOffset);
        if (tmpPos.getErrorIndex() == -1) {
            // 2) The input text was not completely processed
            if (tmpPos.getIndex() == maxPos || hasDigitOffset.value) {
                pos.setIndex(tmpPos.getIndex());
                return getTimeZoneForOffset(offset);
            }
            parsedOffset = offset;
            parsedPos = tmpPos.getIndex();
        }
        // Note: For now, no distinction between long/short localized GMT format in the parser.
        // This might be changed in future.
        // evaluated |= (fallbackLocalizedGMT ? Style.LOCALIZED_GMT.flag : Style.LOCALIZED_GMT_SHORT.flag);
        evaluated |= (Style.LOCALIZED_GMT.flag | Style.LOCALIZED_GMT_SHORT.flag);
    }
    boolean parseTZDBAbbrev = (options == null) ? getDefaultParseOptions().contains(ParseOption.TZ_DATABASE_ABBREVIATIONS) : options.contains(ParseOption.TZ_DATABASE_ABBREVIATIONS);
    // Try the specified style
    switch(style) {
        case LOCALIZED_GMT:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                offset = parseOffsetLocalizedGMT(text, tmpPos);
                if (tmpPos.getErrorIndex() == -1) {
                    pos.setIndex(tmpPos.getIndex());
                    return getTimeZoneForOffset(offset);
                }
                // Note: For now, no distinction between long/short localized GMT format in the parser.
                // This might be changed in future.
                evaluated |= Style.LOCALIZED_GMT_SHORT.flag;
                break;
            }
        case LOCALIZED_GMT_SHORT:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                offset = parseOffsetShortLocalizedGMT(text, tmpPos);
                if (tmpPos.getErrorIndex() == -1) {
                    pos.setIndex(tmpPos.getIndex());
                    return getTimeZoneForOffset(offset);
                }
                // Note: For now, no distinction between long/short localized GMT format in the parser.
                // This might be changed in future.
                evaluated |= Style.LOCALIZED_GMT.flag;
                break;
            }
        case ISO_BASIC_SHORT:
        case ISO_BASIC_FIXED:
        case ISO_BASIC_FULL:
        case ISO_EXTENDED_FIXED:
        case ISO_EXTENDED_FULL:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                offset = parseOffsetISO8601(text, tmpPos);
                if (tmpPos.getErrorIndex() == -1) {
                    pos.setIndex(tmpPos.getIndex());
                    return getTimeZoneForOffset(offset);
                }
                break;
            }
        case ISO_BASIC_LOCAL_SHORT:
        case ISO_BASIC_LOCAL_FIXED:
        case ISO_BASIC_LOCAL_FULL:
        case ISO_EXTENDED_LOCAL_FIXED:
        case ISO_EXTENDED_LOCAL_FULL:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                // Exclude the case of UTC Indicator "Z" here
                Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
                offset = parseOffsetISO8601(text, tmpPos, false, hasDigitOffset);
                if (tmpPos.getErrorIndex() == -1 && hasDigitOffset.value) {
                    pos.setIndex(tmpPos.getIndex());
                    return getTimeZoneForOffset(offset);
                }
                break;
            }
        case SPECIFIC_LONG:
        case SPECIFIC_SHORT:
            {
                // Specific styles
                EnumSet<NameType> nameTypes = null;
                if (style == Style.SPECIFIC_LONG) {
                    nameTypes = EnumSet.of(NameType.LONG_STANDARD, NameType.LONG_DAYLIGHT);
                } else {
                    assert style == Style.SPECIFIC_SHORT;
                    nameTypes = EnumSet.of(NameType.SHORT_STANDARD, NameType.SHORT_DAYLIGHT);
                }
                Collection<MatchInfo> specificMatches = _tznames.find(text, startIdx, nameTypes);
                if (specificMatches != null) {
                    MatchInfo specificMatch = null;
                    for (MatchInfo match : specificMatches) {
                        if (startIdx + match.matchLength() > parsedPos) {
                            specificMatch = match;
                            parsedPos = startIdx + match.matchLength();
                        }
                    }
                    if (specificMatch != null) {
                        timeType.value = getTimeType(specificMatch.nameType());
                        pos.setIndex(parsedPos);
                        return TimeZone.getTimeZone(getTimeZoneID(specificMatch.tzID(), specificMatch.mzID()));
                    }
                }
                if (parseTZDBAbbrev && style == Style.SPECIFIC_SHORT) {
                    assert nameTypes.contains(NameType.SHORT_STANDARD);
                    assert nameTypes.contains(NameType.SHORT_DAYLIGHT);
                    Collection<MatchInfo> tzdbNameMatches = getTZDBTimeZoneNames().find(text, startIdx, nameTypes);
                    if (tzdbNameMatches != null) {
                        MatchInfo tzdbNameMatch = null;
                        for (MatchInfo match : tzdbNameMatches) {
                            if (startIdx + match.matchLength() > parsedPos) {
                                tzdbNameMatch = match;
                                parsedPos = startIdx + match.matchLength();
                            }
                        }
                        if (tzdbNameMatch != null) {
                            timeType.value = getTimeType(tzdbNameMatch.nameType());
                            pos.setIndex(parsedPos);
                            return TimeZone.getTimeZone(getTimeZoneID(tzdbNameMatch.tzID(), tzdbNameMatch.mzID()));
                        }
                    }
                }
                break;
            }
        case GENERIC_LONG:
        case GENERIC_SHORT:
        case GENERIC_LOCATION:
            {
                EnumSet<GenericNameType> genericNameTypes = null;
                switch(style) {
                    case GENERIC_LOCATION:
                        genericNameTypes = EnumSet.of(GenericNameType.LOCATION);
                        break;
                    case GENERIC_LONG:
                        genericNameTypes = EnumSet.of(GenericNameType.LONG, GenericNameType.LOCATION);
                        break;
                    case GENERIC_SHORT:
                        genericNameTypes = EnumSet.of(GenericNameType.SHORT, GenericNameType.LOCATION);
                        break;
                    default:
                        // style cannot be other than above cases
                        assert false;
                        break;
                }
                GenericMatchInfo bestGeneric = getTimeZoneGenericNames().findBestMatch(text, startIdx, genericNameTypes);
                if (bestGeneric != null && (startIdx + bestGeneric.matchLength() > parsedPos)) {
                    timeType.value = bestGeneric.timeType();
                    pos.setIndex(startIdx + bestGeneric.matchLength());
                    return TimeZone.getTimeZone(bestGeneric.tzID());
                }
                break;
            }
        case ZONE_ID:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                String id = parseZoneID(text, tmpPos);
                if (tmpPos.getErrorIndex() == -1) {
                    pos.setIndex(tmpPos.getIndex());
                    return TimeZone.getTimeZone(id);
                }
                break;
            }
        case ZONE_ID_SHORT:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                String id = parseShortZoneID(text, tmpPos);
                if (tmpPos.getErrorIndex() == -1) {
                    pos.setIndex(tmpPos.getIndex());
                    return TimeZone.getTimeZone(id);
                }
                break;
            }
        case EXEMPLAR_LOCATION:
            {
                tmpPos.setIndex(startIdx);
                tmpPos.setErrorIndex(-1);
                String id = parseExemplarLocation(text, tmpPos);
                if (tmpPos.getErrorIndex() == -1) {
                    pos.setIndex(tmpPos.getIndex());
                    return TimeZone.getTimeZone(id);
                }
                break;
            }
    }
    evaluated |= style.flag;
    if (parsedPos > startIdx) {
        // GMT format.
        assert parsedOffset != UNKNOWN_OFFSET;
        pos.setIndex(parsedPos);
        return getTimeZoneForOffset(parsedOffset);
    }
    // Failed to parse the input text as the time zone format in the specified style.
    // Check the longest match among other styles below.
    // stores successfully parsed zone ID for later use
    String parsedID = null;
    // stores successfully parsed time type for later use
    TimeType parsedTimeType = TimeType.UNKNOWN;
    assert parsedPos < 0;
    assert parsedOffset == UNKNOWN_OFFSET;
    // ISO 8601
    if (parsedPos < maxPos && ((evaluated & ISO_Z_STYLE_FLAG) == 0 || (evaluated & ISO_LOCAL_STYLE_FLAG) == 0)) {
        tmpPos.setIndex(startIdx);
        tmpPos.setErrorIndex(-1);
        Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
        offset = parseOffsetISO8601(text, tmpPos, false, hasDigitOffset);
        if (tmpPos.getErrorIndex() == -1) {
            if (tmpPos.getIndex() == maxPos || hasDigitOffset.value) {
                pos.setIndex(tmpPos.getIndex());
                return getTimeZoneForOffset(offset);
            }
            // may collide with other names. In this case, we need to evaluate other names.
            if (parsedPos < tmpPos.getIndex()) {
                parsedOffset = offset;
                parsedID = null;
                parsedTimeType = TimeType.UNKNOWN;
                parsedPos = tmpPos.getIndex();
                // only when "Z" is used
                assert parsedPos == startIdx + 1;
            }
        }
    }
    // Localized GMT format
    if (parsedPos < maxPos && (evaluated & Style.LOCALIZED_GMT.flag) == 0) {
        tmpPos.setIndex(startIdx);
        tmpPos.setErrorIndex(-1);
        Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
        offset = parseOffsetLocalizedGMT(text, tmpPos, false, hasDigitOffset);
        if (tmpPos.getErrorIndex() == -1) {
            if (tmpPos.getIndex() == maxPos || hasDigitOffset.value) {
                pos.setIndex(tmpPos.getIndex());
                return getTimeZoneForOffset(offset);
            }
            // Evaluate other names - see the comment earlier in this method.
            if (parsedPos < tmpPos.getIndex()) {
                parsedOffset = offset;
                parsedID = null;
                parsedTimeType = TimeType.UNKNOWN;
                parsedPos = tmpPos.getIndex();
            }
        }
    }
    if (parsedPos < maxPos && (evaluated & Style.LOCALIZED_GMT_SHORT.flag) == 0) {
        tmpPos.setIndex(startIdx);
        tmpPos.setErrorIndex(-1);
        Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
        offset = parseOffsetLocalizedGMT(text, tmpPos, true, hasDigitOffset);
        if (tmpPos.getErrorIndex() == -1) {
            if (tmpPos.getIndex() == maxPos || hasDigitOffset.value) {
                pos.setIndex(tmpPos.getIndex());
                return getTimeZoneForOffset(offset);
            }
            // Evaluate other names - see the comment earlier in this method.
            if (parsedPos < tmpPos.getIndex()) {
                parsedOffset = offset;
                parsedID = null;
                parsedTimeType = TimeType.UNKNOWN;
                parsedPos = tmpPos.getIndex();
            }
        }
    }
    // When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
    // For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
    // used for America/New_York. With parseAllStyles true, this code parses "EST"
    // as America/New_York.
    // Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
    // which we want to avoid normally (note that we cache the trie, so this is applicable to the
    // first time only as long as the cache does not expire).
    boolean parseAllStyles = (options == null) ? getDefaultParseOptions().contains(ParseOption.ALL_STYLES) : options.contains(ParseOption.ALL_STYLES);
    if (parseAllStyles) {
        // Try all specific names and exemplar location names
        if (parsedPos < maxPos) {
            Collection<MatchInfo> specificMatches = _tznames.find(text, startIdx, ALL_SIMPLE_NAME_TYPES);
            MatchInfo specificMatch = null;
            int matchPos = -1;
            if (specificMatches != null) {
                for (MatchInfo match : specificMatches) {
                    if (startIdx + match.matchLength() > matchPos) {
                        specificMatch = match;
                        matchPos = startIdx + match.matchLength();
                    }
                }
            }
            if (parsedPos < matchPos) {
                parsedPos = matchPos;
                parsedID = getTimeZoneID(specificMatch.tzID(), specificMatch.mzID());
                parsedTimeType = getTimeType(specificMatch.nameType());
                parsedOffset = UNKNOWN_OFFSET;
            }
        }
        if (parseTZDBAbbrev && parsedPos < maxPos && (evaluated & Style.SPECIFIC_SHORT.flag) == 0) {
            Collection<MatchInfo> tzdbNameMatches = getTZDBTimeZoneNames().find(text, startIdx, ALL_SIMPLE_NAME_TYPES);
            MatchInfo tzdbNameMatch = null;
            int matchPos = -1;
            if (tzdbNameMatches != null) {
                for (MatchInfo match : tzdbNameMatches) {
                    if (startIdx + match.matchLength() > matchPos) {
                        tzdbNameMatch = match;
                        matchPos = startIdx + match.matchLength();
                    }
                }
                if (parsedPos < matchPos) {
                    parsedPos = matchPos;
                    parsedID = getTimeZoneID(tzdbNameMatch.tzID(), tzdbNameMatch.mzID());
                    parsedTimeType = getTimeType(tzdbNameMatch.nameType());
                    parsedOffset = UNKNOWN_OFFSET;
                }
            }
        }
        // Try generic names
        if (parsedPos < maxPos) {
            GenericMatchInfo genericMatch = getTimeZoneGenericNames().findBestMatch(text, startIdx, ALL_GENERIC_NAME_TYPES);
            if (genericMatch != null && parsedPos < startIdx + genericMatch.matchLength()) {
                parsedPos = startIdx + genericMatch.matchLength();
                parsedID = genericMatch.tzID();
                parsedTimeType = genericMatch.timeType();
                parsedOffset = UNKNOWN_OFFSET;
            }
        }
        // Try time zone ID
        if (parsedPos < maxPos && (evaluated & Style.ZONE_ID.flag) == 0) {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);
            String id = parseZoneID(text, tmpPos);
            if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
                parsedPos = tmpPos.getIndex();
                parsedID = id;
                parsedTimeType = TimeType.UNKNOWN;
                parsedOffset = UNKNOWN_OFFSET;
            }
        }
        // Try short time zone ID
        if (parsedPos < maxPos && (evaluated & Style.ZONE_ID_SHORT.flag) == 0) {
            tmpPos.setIndex(startIdx);
            tmpPos.setErrorIndex(-1);
            String id = parseShortZoneID(text, tmpPos);
            if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
                parsedPos = tmpPos.getIndex();
                parsedID = id;
                parsedTimeType = TimeType.UNKNOWN;
                parsedOffset = UNKNOWN_OFFSET;
            }
        }
    }
    if (parsedPos > startIdx) {
        // Parsed successfully
        TimeZone parsedTZ = null;
        if (parsedID != null) {
            parsedTZ = TimeZone.getTimeZone(parsedID);
        } else {
            assert parsedOffset != UNKNOWN_OFFSET;
            parsedTZ = getTimeZoneForOffset(parsedOffset);
        }
        timeType.value = parsedTimeType;
        pos.setIndex(parsedPos);
        return parsedTZ;
    }
    pos.setErrorIndex(startIdx);
    return null;
}
Also used : GenericMatchInfo(android.icu.impl.TimeZoneGenericNames.GenericMatchInfo) EnumSet(java.util.EnumSet) AttributedString(java.text.AttributedString) TimeZone(android.icu.util.TimeZone) GenericMatchInfo(android.icu.impl.TimeZoneGenericNames.GenericMatchInfo) MatchInfo(android.icu.text.TimeZoneNames.MatchInfo) Output(android.icu.util.Output) Collection(java.util.Collection) ParsePosition(java.text.ParsePosition)

Example 7 with Output

use of android.icu.util.Output 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)

Example 8 with Output

use of android.icu.util.Output in project j2objc by google.

the class CompactDecimalFormat method format.

/* INTERNALS */
private StringBuffer format(double number, Currency curr, StringBuffer toAppendTo, FieldPosition pos) {
    if (curr != null && style == CompactStyle.LONG) {
        throw new UnsupportedOperationException("CompactDecimalFormat does not support LONG style for currency.");
    }
    // Compute the scaled amount, prefix, and suffix appropriate for the number's magnitude.
    Output<Unit> currencyUnit = new Output<Unit>();
    Amount amount = toAmount(number, curr, currencyUnit);
    Unit unit = amount.getUnit();
    // Note that currencyUnit is a remnant.  In almost all cases, it will be null.
    StringBuffer prefix = new StringBuffer();
    StringBuffer suffix = new StringBuffer();
    if (currencyUnit.value != null) {
        currencyUnit.value.writePrefix(prefix);
    }
    unit.writePrefix(prefix);
    unit.writeSuffix(suffix);
    if (currencyUnit.value != null) {
        currencyUnit.value.writeSuffix(suffix);
    }
    if (curr == null) {
        // Prevent locking when not formatting a currency number.
        toAppendTo.append(escape(prefix.toString()));
        super.format(amount.getQty(), toAppendTo, pos);
        toAppendTo.append(escape(suffix.toString()));
    } else {
        // This has to be synchronized since this information is held in the state of the DecimalFormat object.
        synchronized (this) {
            String originalPattern = this.toPattern();
            Currency originalCurrency = this.getCurrency();
            StringBuffer newPattern = new StringBuffer();
            // Write prefixes and suffixes to the pattern.  Note that we have to apply it to both halves of a
            // positive/negative format (separated by ';')
            int semicolonPos = originalPattern.indexOf(';');
            newPattern.append(prefix);
            if (semicolonPos != -1) {
                newPattern.append(originalPattern, 0, semicolonPos);
                newPattern.append(suffix);
                newPattern.append(';');
                newPattern.append(prefix);
            }
            newPattern.append(originalPattern, semicolonPos + 1, originalPattern.length());
            newPattern.append(suffix);
            // Overwrite the pattern and currency.
            setCurrency(curr);
            applyPattern(newPattern.toString());
            // Actually perform the formatting.
            super.format(amount.getQty(), toAppendTo, pos);
            // Reset the pattern and currency.
            setCurrency(originalCurrency);
            applyPattern(originalPattern);
        }
    }
    return toAppendTo;
}
Also used : Output(android.icu.util.Output) CurrencyAmount(android.icu.util.CurrencyAmount) Currency(android.icu.util.Currency)

Example 9 with Output

use of android.icu.util.Output in project j2objc by google.

the class PluralRulesTest method TestSerialization.

@Test
public void TestSerialization() {
    Output<Integer> size = new Output<Integer>();
    int max = 0;
    for (ULocale locale : PluralRules.getAvailableULocales()) {
        PluralRules item = PluralRules.forLocale(locale);
        PluralRules item2 = serializeAndDeserialize(item, size);
        logln(locale + "\tsize:\t" + size.value);
        max = Math.max(max, size.value);
        if (!assertEquals(locale + "\tPlural rules before and after serialization", item, item2)) {
            // for debugging
            PluralRules item3 = serializeAndDeserialize(item, size);
            item.equals(item3);
        }
    }
    logln("max \tsize:\t" + max);
}
Also used : PluralRules(android.icu.text.PluralRules) ULocale(android.icu.util.ULocale) Output(android.icu.util.Output) Test(org.junit.Test)

Example 10 with Output

use of android.icu.util.Output in project j2objc by google.

the class PluralRulesTest method TestKeywords.

@Test
public void TestKeywords() {
    Set<String> possibleKeywords = new LinkedHashSet(Arrays.asList("zero", "one", "two", "few", "many", "other"));
    Object[][][] tests = { // format is locale, explicits, then triples of keyword, status, unique value.
    { { "en", null }, { "one", KeywordStatus.UNIQUE, 1.0d }, { "other", KeywordStatus.UNBOUNDED, null } }, { { "pl", null }, { "one", KeywordStatus.UNIQUE, 1.0d }, { "few", KeywordStatus.UNBOUNDED, null }, { "many", KeywordStatus.UNBOUNDED, null }, // note that it is
    { "other", KeywordStatus.SUPPRESSED, null, KeywordStatus.UNBOUNDED, null } // suppressed in
    // INTEGER but not
    // DECIMAL
    }, { // check that 1 is suppressed
    { "en", new HashSet<Double>(Arrays.asList(1.0d)) }, { "one", KeywordStatus.SUPPRESSED, null }, { "other", KeywordStatus.UNBOUNDED, null } } };
    Output<Double> uniqueValue = new Output<Double>();
    for (Object[][] test : tests) {
        ULocale locale = new ULocale((String) test[0][0]);
        // NumberType numberType = (NumberType) test[1];
        Set<Double> explicits = (Set<Double>) test[0][1];
        PluralRules pluralRules = factory.forLocale(locale);
        LinkedHashSet<String> remaining = new LinkedHashSet(possibleKeywords);
        for (int i = 1; i < test.length; ++i) {
            Object[] row = test[i];
            String keyword = (String) row[0];
            KeywordStatus statusExpected = (KeywordStatus) row[1];
            Double uniqueExpected = (Double) row[2];
            remaining.remove(keyword);
            KeywordStatus status = pluralRules.getKeywordStatus(keyword, 0, explicits, uniqueValue);
            assertEquals(getAssertMessage("Unique Value", locale, pluralRules, keyword), uniqueExpected, uniqueValue.value);
            assertEquals(getAssertMessage("Keyword Status", locale, pluralRules, keyword), statusExpected, status);
            if (row.length > 3) {
                statusExpected = (KeywordStatus) row[3];
                uniqueExpected = (Double) row[4];
                status = pluralRules.getKeywordStatus(keyword, 0, explicits, uniqueValue, SampleType.DECIMAL);
                assertEquals(getAssertMessage("Unique Value - decimal", locale, pluralRules, keyword), uniqueExpected, uniqueValue.value);
                assertEquals(getAssertMessage("Keyword Status - decimal", locale, pluralRules, keyword), statusExpected, status);
            }
        }
        for (String keyword : remaining) {
            KeywordStatus status = pluralRules.getKeywordStatus(keyword, 0, null, uniqueValue);
            assertEquals("Invalid keyword " + keyword, status, KeywordStatus.INVALID);
            assertNull("Invalid keyword " + keyword, uniqueValue.value);
        }
    }
}
Also used : LinkedHashSet(java.util.LinkedHashSet) ULocale(android.icu.util.ULocale) TreeSet(java.util.TreeSet) HashSet(java.util.HashSet) EnumSet(java.util.EnumSet) LinkedHashSet(java.util.LinkedHashSet) Set(java.util.Set) PluralRules(android.icu.text.PluralRules) KeywordStatus(android.icu.text.PluralRules.KeywordStatus) Output(android.icu.util.Output) Test(org.junit.Test)

Aggregations

Output (android.icu.util.Output)12 ULocale (android.icu.util.ULocale)6 Test (org.junit.Test)6 TimeZone (android.icu.util.TimeZone)5 TimeType (android.icu.text.TimeZoneFormat.TimeType)4 BasicTimeZone (android.icu.util.BasicTimeZone)4 TimeZoneFormat (android.icu.text.TimeZoneFormat)3 SimpleTimeZone (android.icu.util.SimpleTimeZone)3 ParsePosition (java.text.ParsePosition)3 Date (java.util.Date)3 EnumSet (java.util.EnumSet)3 PluralRules (android.icu.text.PluralRules)2 Style (android.icu.text.TimeZoneFormat.Style)2 Calendar (android.icu.util.Calendar)2 AttributedString (java.text.AttributedString)2 HashSet (java.util.HashSet)2 DayPeriodRules (android.icu.impl.DayPeriodRules)1 TZDBTimeZoneNames (android.icu.impl.TZDBTimeZoneNames)1 GenericMatchInfo (android.icu.impl.TimeZoneGenericNames.GenericMatchInfo)1 CollationTailoring (android.icu.impl.coll.CollationTailoring)1