Search in sources :

Example 66 with TimeZone

use of android.icu.util.TimeZone in project android_frameworks_opt_telephony by LineageOS.

the class TimeZoneLookupHelper method lookupByCountry.

/**
 * Returns information about the time zones used in a country at a given time.
 *
 * {@code null} can be returned if a problem occurs during lookup, e.g. if the country code is
 * unrecognized, if the country is uninhabited, or if there is a problem with the data.
 */
@VisibleForTesting
@Nullable
public CountryResult lookupByCountry(@NonNull String isoCountryCode, long whenMillis) {
    CountryTimeZones countryTimeZones = getCountryTimeZones(isoCountryCode);
    if (countryTimeZones == null) {
        // Unknown country code.
        return null;
    }
    TimeZone countryDefaultZone = countryTimeZones.getDefaultTimeZone();
    if (countryDefaultZone == null) {
        // This is not expected: the country default should have been validated before.
        return null;
    }
    String debugInfo;
    int matchQuality;
    if (countryTimeZones.isDefaultTimeZoneBoosted()) {
        matchQuality = CountryResult.QUALITY_DEFAULT_BOOSTED;
        debugInfo = "Country default is boosted";
    } else {
        List<TimeZoneMapping> effectiveTimeZoneMappings = countryTimeZones.getEffectiveTimeZoneMappingsAt(whenMillis);
        if (effectiveTimeZoneMappings.isEmpty()) {
            // This should never happen unless there's been an error loading the data.
            // Treat it the same as a low quality answer.
            matchQuality = QUALITY_MULTIPLE_ZONES_DIFFERENT_OFFSETS;
            debugInfo = "No effective time zones found at whenMillis=" + whenMillis;
        } else if (effectiveTimeZoneMappings.size() == 1) {
            // The default is the only zone so it's a good candidate.
            matchQuality = CountryResult.QUALITY_SINGLE_ZONE;
            debugInfo = "One effective time zone found at whenMillis=" + whenMillis;
        } else {
            boolean countryUsesDifferentOffsets = countryUsesDifferentOffsets(whenMillis, effectiveTimeZoneMappings, countryDefaultZone);
            matchQuality = countryUsesDifferentOffsets ? QUALITY_MULTIPLE_ZONES_DIFFERENT_OFFSETS : QUALITY_MULTIPLE_ZONES_SAME_OFFSET;
            debugInfo = "countryUsesDifferentOffsets=" + countryUsesDifferentOffsets + " at" + " whenMillis=" + whenMillis;
        }
    }
    return new CountryResult(countryDefaultZone.getID(), matchQuality, debugInfo);
}
Also used : TimeZone(android.icu.util.TimeZone) CountryTimeZones(android.timezone.CountryTimeZones) TimeZoneMapping(android.timezone.CountryTimeZones.TimeZoneMapping) VisibleForTesting(com.android.internal.annotations.VisibleForTesting) Nullable(android.annotation.Nullable)

Example 67 with TimeZone

use of android.icu.util.TimeZone in project android_packages_apps_Settings by omnirom.

the class RegionZonePicker method getRegionTimeZoneInfo.

/**
 * Returns a list of {@link TimeZoneInfo} objects. The returned list will be sorted properly for
 * display in the locale.It may be smaller than the input collection, if equivalent IDs are
 * passed in.
 *
 * @param timeZoneIds a list of Olson IDs.
 */
public List<TimeZoneInfo> getRegionTimeZoneInfo(Collection<String> timeZoneIds) {
    final TimeZoneInfo.Formatter formatter = new TimeZoneInfo.Formatter(getLocale(), new Date());
    final TreeSet<TimeZoneInfo> timeZoneInfos = new TreeSet<>(new TimeZoneInfoComparator(Collator.getInstance(getLocale()), new Date()));
    for (final String timeZoneId : timeZoneIds) {
        final TimeZone timeZone = TimeZone.getFrozenTimeZone(timeZoneId);
        // Skip time zone ICU isn't aware.
        if (timeZone.getID().equals(TimeZone.UNKNOWN_ZONE_ID)) {
            continue;
        }
        timeZoneInfos.add(formatter.format(timeZone));
    }
    return Collections.unmodifiableList(new ArrayList<>(timeZoneInfos));
}
Also used : TimeZone(android.icu.util.TimeZone) TreeSet(java.util.TreeSet) Date(java.util.Date)

Example 68 with TimeZone

use of android.icu.util.TimeZone 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 69 with TimeZone

use of android.icu.util.TimeZone 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 70 with TimeZone

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

the class TimeZoneGenericNames method formatGenericNonLocationName.

/**
 * Private method to get a generic string, with fallback logics involved,
 * that is,
 *
 * 1. If a generic non-location string is available for the zone, return it.
 * 2. If a generic non-location string is associated with a meta zone and
 *    the zone never use daylight time around the given date, use the standard
 *    string (if available).
 * 3. If a generic non-location string is associated with a meta zone and
 *    the offset at the given time is different from the preferred zone for the
 *    current locale, then return the generic partial location string (if available)
 * 4. If a generic non-location string is not available, use generic location
 *    string.
 *
 * @param tz the requested time zone
 * @param date the date
 * @param type the generic name type, either LONG or SHORT
 * @return the name used for a generic name type, which could be the
 * generic name, or the standard name (if the zone does not observes DST
 * around the date), or the partial location name.
 */
private String formatGenericNonLocationName(TimeZone tz, GenericNameType type, long date) {
    assert (type == GenericNameType.LONG || type == GenericNameType.SHORT);
    String tzID = ZoneMeta.getCanonicalCLDRID(tz);
    if (tzID == null) {
        return null;
    }
    // Try to get a name from time zone first
    NameType nameType = (type == GenericNameType.LONG) ? NameType.LONG_GENERIC : NameType.SHORT_GENERIC;
    String name = _tznames.getTimeZoneDisplayName(tzID, nameType);
    if (name != null) {
        return name;
    }
    // Try meta zone
    String mzID = _tznames.getMetaZoneID(tzID, date);
    if (mzID != null) {
        boolean useStandard = false;
        int[] offsets = { 0, 0 };
        tz.getOffset(date, false, offsets);
        if (offsets[1] == 0) {
            useStandard = true;
            // Check if the zone actually uses daylight saving time around the time
            if (tz instanceof BasicTimeZone) {
                BasicTimeZone btz = (BasicTimeZone) tz;
                TimeZoneTransition before = btz.getPreviousTransition(date, true);
                if (before != null && (date - before.getTime() < DST_CHECK_RANGE) && before.getFrom().getDSTSavings() != 0) {
                    useStandard = false;
                } else {
                    TimeZoneTransition after = btz.getNextTransition(date, false);
                    if (after != null && (after.getTime() - date < DST_CHECK_RANGE) && after.getTo().getDSTSavings() != 0) {
                        useStandard = false;
                    }
                }
            } else {
                // If not BasicTimeZone... only if the instance is not an ICU's implementation.
                // We may get a wrong answer in edge case, but it should practically work OK.
                int[] tmpOffsets = new int[2];
                tz.getOffset(date - DST_CHECK_RANGE, false, tmpOffsets);
                if (tmpOffsets[1] != 0) {
                    useStandard = false;
                } else {
                    tz.getOffset(date + DST_CHECK_RANGE, false, tmpOffsets);
                    if (tmpOffsets[1] != 0) {
                        useStandard = false;
                    }
                }
            }
        }
        if (useStandard) {
            NameType stdNameType = (nameType == NameType.LONG_GENERIC) ? NameType.LONG_STANDARD : NameType.SHORT_STANDARD;
            String stdName = _tznames.getDisplayName(tzID, stdNameType, date);
            if (stdName != null) {
                name = stdName;
                // TODO: revisit this issue later
                // In CLDR, a same display name is used for both generic and standard
                // for some meta zones in some locales.  This looks like a data bugs.
                // For now, we check if the standard name is different from its generic
                // name below.
                String mzGenericName = _tznames.getMetaZoneDisplayName(mzID, nameType);
                if (stdName.equalsIgnoreCase(mzGenericName)) {
                    name = null;
                }
            }
        }
        if (name == null) {
            // Get a name from meta zone
            String mzName = _tznames.getMetaZoneDisplayName(mzID, nameType);
            if (mzName != null) {
                // Check if we need to use a partial location format.
                // This check is done by comparing offset with the meta zone's
                // golden zone at the given date.
                String goldenID = _tznames.getReferenceZoneID(mzID, getTargetRegion());
                if (goldenID != null && !goldenID.equals(tzID)) {
                    TimeZone goldenZone = TimeZone.getFrozenTimeZone(goldenID);
                    int[] offsets1 = { 0, 0 };
                    // Check offset in the golden zone with wall time.
                    // With getOffset(date, false, offsets1),
                    // you may get incorrect results because of time overlap at DST->STD
                    // transition.
                    goldenZone.getOffset(date + offsets[0] + offsets[1], true, offsets1);
                    if (offsets[0] != offsets1[0] || offsets[1] != offsets1[1]) {
                        // Now we need to use a partial location format.
                        name = getPartialLocationName(tzID, mzID, (nameType == NameType.LONG_GENERIC), mzName);
                    } else {
                        name = mzName;
                    }
                } else {
                    name = mzName;
                }
            }
        }
    }
    return name;
}
Also used : BasicTimeZone(android.icu.util.BasicTimeZone) TimeZoneTransition(android.icu.util.TimeZoneTransition) TimeZone(android.icu.util.TimeZone) BasicTimeZone(android.icu.util.BasicTimeZone) NameType(android.icu.text.TimeZoneNames.NameType)

Aggregations

TimeZone (android.icu.util.TimeZone)125 Test (org.junit.Test)98 SimpleTimeZone (android.icu.util.SimpleTimeZone)76 Date (java.util.Date)63 GregorianCalendar (android.icu.util.GregorianCalendar)49 Calendar (android.icu.util.Calendar)42 BasicTimeZone (android.icu.util.BasicTimeZone)41 SimpleDateFormat (android.icu.text.SimpleDateFormat)36 ULocale (android.icu.util.ULocale)31 RuleBasedTimeZone (android.icu.util.RuleBasedTimeZone)24 VTimeZone (android.icu.util.VTimeZone)23 DateFormat (android.icu.text.DateFormat)19 JavaTimeZone (android.icu.impl.JavaTimeZone)18 NativeTimeZone (com.google.j2objc.util.NativeTimeZone)18 JapaneseCalendar (android.icu.util.JapaneseCalendar)15 FieldPosition (java.text.FieldPosition)15 BuddhistCalendar (android.icu.util.BuddhistCalendar)13 ParsePosition (java.text.ParsePosition)13 ChineseCalendar (android.icu.util.ChineseCalendar)12 ParseException (java.text.ParseException)12