Search in sources :

Example 6 with TimeZoneTransition

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

the class OlsonTimeZone method initTransitionRules.

private synchronized void initTransitionRules() {
    if (transitionRulesInitialized) {
        return;
    }
    initialRule = null;
    firstTZTransition = null;
    firstFinalTZTransition = null;
    historicRules = null;
    firstTZTransitionIdx = 0;
    finalZoneWithStartYear = null;
    String stdName = getID() + "(STD)";
    String dstName = getID() + "(DST)";
    int raw, dst;
    // Create initial rule
    raw = initialRawOffset() * Grego.MILLIS_PER_SECOND;
    dst = initialDstOffset() * Grego.MILLIS_PER_SECOND;
    initialRule = new InitialTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst);
    if (transitionCount > 0) {
        int transitionIdx, typeIdx;
        // For now, keeping this code for just in case. Feb 19, 2010 Yoshito
        for (transitionIdx = 0; transitionIdx < transitionCount; transitionIdx++) {
            if (getInt(typeMapData[transitionIdx]) != 0) {
                // type 0 is the initial type
                break;
            }
            firstTZTransitionIdx++;
        }
        if (transitionIdx == transitionCount) {
        // Actually no transitions...
        } else {
            // Build historic rule array
            long[] times = new long[transitionCount];
            for (typeIdx = 0; typeIdx < typeCount; typeIdx++) {
                // Gather all start times for each pair of offsets
                int nTimes = 0;
                for (transitionIdx = firstTZTransitionIdx; transitionIdx < transitionCount; transitionIdx++) {
                    if (typeIdx == getInt(typeMapData[transitionIdx])) {
                        long tt = transitionTimes64[transitionIdx] * Grego.MILLIS_PER_SECOND;
                        if (tt < finalStartMillis) {
                            // Exclude transitions after finalMillis
                            times[nTimes++] = tt;
                        }
                    }
                }
                if (nTimes > 0) {
                    long[] startTimes = new long[nTimes];
                    System.arraycopy(times, 0, startTimes, 0, nTimes);
                    // Create a TimeArrayTimeZoneRule
                    raw = typeOffsets[typeIdx * 2] * Grego.MILLIS_PER_SECOND;
                    dst = typeOffsets[typeIdx * 2 + 1] * Grego.MILLIS_PER_SECOND;
                    if (historicRules == null) {
                        historicRules = new TimeArrayTimeZoneRule[typeCount];
                    }
                    historicRules[typeIdx] = new TimeArrayTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst, startTimes, DateTimeRule.UTC_TIME);
                }
            }
            // Create initial transition
            typeIdx = getInt(typeMapData[firstTZTransitionIdx]);
            firstTZTransition = new TimeZoneTransition(transitionTimes64[firstTZTransitionIdx] * Grego.MILLIS_PER_SECOND, initialRule, historicRules[typeIdx]);
        }
    }
    if (finalZone != null) {
        // Get the first occurrence of final rule starts
        long startTime = (long) finalStartMillis;
        TimeZoneRule firstFinalRule;
        if (finalZone.useDaylightTime()) {
            /*
                 * Note: When an OlsonTimeZone is constructed, we should set the final year
                 * as the start year of finalZone.  However, the boundary condition used for
                 * getting offset from finalZone has some problems.  So setting the start year
                 * in the finalZone will cause a problem.  For now, we do not set the valid
                 * start year when the construction time and create a clone and set the
                 * start year when extracting rules.
                 */
            finalZoneWithStartYear = (SimpleTimeZone) finalZone.clone();
            finalZoneWithStartYear.setStartYear(finalStartYear);
            TimeZoneTransition tzt = finalZoneWithStartYear.getNextTransition(startTime, false);
            firstFinalRule = tzt.getTo();
            startTime = tzt.getTime();
        } else {
            finalZoneWithStartYear = finalZone;
            firstFinalRule = new TimeArrayTimeZoneRule(finalZone.getID(), finalZone.getRawOffset(), 0, new long[] { startTime }, DateTimeRule.UTC_TIME);
        }
        TimeZoneRule prevRule = null;
        if (transitionCount > 0) {
            prevRule = historicRules[getInt(typeMapData[transitionCount - 1])];
        }
        if (prevRule == null) {
            // No historic transitions, but only finalZone available
            prevRule = initialRule;
        }
        firstFinalTZTransition = new TimeZoneTransition(startTime, prevRule, firstFinalRule);
    }
    transitionRulesInitialized = true;
}
Also used : InitialTimeZoneRule(android.icu.util.InitialTimeZoneRule) TimeArrayTimeZoneRule(android.icu.util.TimeArrayTimeZoneRule) AnnualTimeZoneRule(android.icu.util.AnnualTimeZoneRule) TimeZoneRule(android.icu.util.TimeZoneRule) TimeArrayTimeZoneRule(android.icu.util.TimeArrayTimeZoneRule) TimeZoneTransition(android.icu.util.TimeZoneTransition) InitialTimeZoneRule(android.icu.util.InitialTimeZoneRule)

Example 7 with TimeZoneTransition

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

the class TimeZoneFormatTest method TestTimeRoundTrip.

/*
     * Test case of round trip time and text.  This test case detects every canonical TimeZone's
     * rule transition since 1900 until 2020, then check if time around each transition can
     * round trip as expected.
     */
@Test
public void TestTimeRoundTrip() {
    boolean TEST_ALL = getBooleanProperty("TimeZoneRoundTripAll", false);
    int startYear, endYear;
    if (TEST_ALL || TestFmwk.getExhaustiveness() > 5) {
        startYear = 1900;
    } else {
        startYear = 1990;
    }
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    endYear = cal.get(Calendar.YEAR) + 3;
    cal.set(startYear, Calendar.JANUARY, 1);
    final long START_TIME = cal.getTimeInMillis();
    cal.set(endYear, Calendar.JANUARY, 1);
    final long END_TIME = cal.getTimeInMillis();
    // These patterns are ambiguous at DST->STD local time overlap
    List<String> AMBIGUOUS_DST_DECESSION = Arrays.asList("v", "vvvv", "V", "VV", "VVV", "VVVV");
    // These patterns are ambiguous at STD->STD/DST->DST local time overlap
    List<String> AMBIGUOUS_NEGATIVE_SHIFT = Arrays.asList("z", "zzzz", "v", "vvvv", "V", "VV", "VVV", "VVVV");
    // These patterns only support integer minutes offset
    List<String> MINUTES_OFFSET = Arrays.asList("X", "XX", "XXX", "x", "xx", "xxx");
    // Regex pattern used for filtering zone IDs without exemplar location
    final Pattern LOC_EXCLUSION_PATTERN = Pattern.compile("Etc/.*|SystemV/.*|.*/Riyadh8[7-9]");
    final String BASEPATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";
    ULocale[] LOCALES = null;
    // timer for performance analysis
    long[] times = new long[PATTERNS.length];
    long timer;
    if (TEST_ALL) {
        // It may take about an hour for testing all locales
        LOCALES = ULocale.getAvailableLocales();
    } else if (TestFmwk.getExhaustiveness() > 5) {
        LOCALES = new ULocale[] { new ULocale("ar_EG"), new ULocale("bg_BG"), new ULocale("ca_ES"), new ULocale("da_DK"), new ULocale("de"), new ULocale("de_DE"), new ULocale("el_GR"), new ULocale("en"), new ULocale("en_AU"), new ULocale("en_CA"), new ULocale("en_US"), new ULocale("es"), new ULocale("es_ES"), new ULocale("es_MX"), new ULocale("fi_FI"), new ULocale("fr"), new ULocale("fr_CA"), new ULocale("fr_FR"), new ULocale("he_IL"), new ULocale("hu_HU"), new ULocale("it"), new ULocale("it_IT"), new ULocale("ja"), new ULocale("ja_JP"), new ULocale("ko"), new ULocale("ko_KR"), new ULocale("nb_NO"), new ULocale("nl_NL"), new ULocale("nn_NO"), new ULocale("pl_PL"), new ULocale("pt"), new ULocale("pt_BR"), new ULocale("pt_PT"), new ULocale("ru_RU"), new ULocale("sv_SE"), new ULocale("th_TH"), new ULocale("tr_TR"), new ULocale("zh"), new ULocale("zh_Hans"), new ULocale("zh_Hans_CN"), new ULocale("zh_Hant"), new ULocale("zh_Hant_HK"), new ULocale("zh_Hant_TW") };
    } else {
        LOCALES = new ULocale[] { new ULocale("en") };
    }
    SimpleDateFormat sdfGMT = new SimpleDateFormat(BASEPATTERN);
    sdfGMT.setTimeZone(TimeZone.getTimeZone("Etc/GMT"));
    long testCounts = 0;
    long[] testTimes = new long[4];
    boolean[] expectedRoundTrip = new boolean[4];
    int testLen = 0;
    for (int locidx = 0; locidx < LOCALES.length; locidx++) {
        logln("Locale: " + LOCALES[locidx].toString());
        for (int patidx = 0; patidx < PATTERNS.length; patidx++) {
            logln("    pattern: " + PATTERNS[patidx]);
            String pattern = BASEPATTERN + " " + PATTERNS[patidx];
            SimpleDateFormat sdf = new SimpleDateFormat(pattern, LOCALES[locidx]);
            boolean minutesOffset = MINUTES_OFFSET.contains(PATTERNS[patidx]);
            Set<String> ids = null;
            if (JDKTZ) {
                ids = new TreeSet<String>();
                String[] jdkIDs = java.util.TimeZone.getAvailableIDs();
                for (String jdkID : jdkIDs) {
                    if (EXCL_TZ_PATTERN.matcher(jdkID).matches()) {
                        continue;
                    }
                    String tmpID = TimeZone.getCanonicalID(jdkID);
                    if (tmpID != null) {
                        ids.add(tmpID);
                    }
                }
            } else {
                ids = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL, null, null);
            }
            for (String id : ids) {
                if (PATTERNS[patidx].equals("V")) {
                    // Some zones do not have short ID assigned, such as Asia/Riyadh87.
                    // The time roundtrip will fail for such zones with pattern "V" (short zone ID).
                    // This is expected behavior.
                    String shortZoneID = ZoneMeta.getShortID(id);
                    if (shortZoneID == null) {
                        continue;
                    }
                } else if (PATTERNS[patidx].equals("VVV")) {
                    // This is expected behavior.
                    if (id.indexOf('/') < 0 || LOC_EXCLUSION_PATTERN.matcher(id).matches()) {
                        continue;
                    }
                }
                if (id.equals("Pacific/Apia") && PATTERNS[patidx].equals("vvvv") && logKnownIssue("11052", "Ambiguous zone name - Samoa Time")) {
                    continue;
                }
                BasicTimeZone btz = (BasicTimeZone) TimeZone.getTimeZone(id, TimeZone.TIMEZONE_ICU);
                TimeZone tz = TimeZone.getTimeZone(id);
                sdf.setTimeZone(tz);
                long t = START_TIME;
                TimeZoneTransition tzt = null;
                boolean middle = true;
                boolean last = false;
                while (t < END_TIME) {
                    if (tzt == null) {
                        testTimes[0] = t;
                        expectedRoundTrip[0] = true;
                        testLen = 1;
                    } else {
                        int fromOffset = tzt.getFrom().getRawOffset() + tzt.getFrom().getDSTSavings();
                        int toOffset = tzt.getTo().getRawOffset() + tzt.getTo().getDSTSavings();
                        int delta = toOffset - fromOffset;
                        if (delta < 0) {
                            boolean isDstDecession = tzt.getFrom().getDSTSavings() > 0 && tzt.getTo().getDSTSavings() == 0;
                            testTimes[0] = t + delta - 1;
                            expectedRoundTrip[0] = true;
                            testTimes[1] = t + delta;
                            expectedRoundTrip[1] = isDstDecession ? !AMBIGUOUS_DST_DECESSION.contains(PATTERNS[patidx]) : !AMBIGUOUS_NEGATIVE_SHIFT.contains(PATTERNS[patidx]);
                            testTimes[2] = t - 1;
                            expectedRoundTrip[2] = isDstDecession ? !AMBIGUOUS_DST_DECESSION.contains(PATTERNS[patidx]) : !AMBIGUOUS_NEGATIVE_SHIFT.contains(PATTERNS[patidx]);
                            testTimes[3] = t;
                            expectedRoundTrip[3] = true;
                            testLen = 4;
                        } else {
                            testTimes[0] = t - 1;
                            expectedRoundTrip[0] = true;
                            testTimes[1] = t;
                            expectedRoundTrip[1] = true;
                            testLen = 2;
                        }
                    }
                    for (int testidx = 0; testidx < testLen; testidx++) {
                        testCounts++;
                        timer = System.currentTimeMillis();
                        String text = sdf.format(new Date(testTimes[testidx]));
                        try {
                            Date parsedDate = sdf.parse(text);
                            long restime = parsedDate.getTime();
                            long timeDiff = restime - testTimes[testidx];
                            boolean bTimeMatch = minutesOffset ? (timeDiff / 60000) * 60000 == 0 : timeDiff == 0;
                            if (!bTimeMatch) {
                                StringBuffer msg = new StringBuffer();
                                msg.append("Time round trip failed for ").append("tzid=").append(id).append(", locale=").append(LOCALES[locidx]).append(", pattern=").append(PATTERNS[patidx]).append(", text=").append(text).append(", gmt=").append(sdfGMT.format(new Date(testTimes[testidx]))).append(", time=").append(testTimes[testidx]).append(", restime=").append(restime).append(", diff=").append(timeDiff);
                                if (expectedRoundTrip[testidx] && !isSpecialTimeRoundTripCase(LOCALES[locidx], id, PATTERNS[patidx], testTimes[testidx])) {
                                    errln("FAIL: " + msg.toString());
                                } else if (REALLY_VERBOSE_LOG) {
                                    logln(msg.toString());
                                }
                            }
                        } catch (ParseException pe) {
                            errln("FAIL: " + pe.getMessage() + " tzid=" + id + ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx] + ", text=" + text);
                        }
                        times[patidx] += System.currentTimeMillis() - timer;
                    }
                    if (last) {
                        break;
                    }
                    tzt = btz.getNextTransition(t, false);
                    if (tzt == null) {
                        last = true;
                        t = END_TIME - 1;
                    } else if (middle) {
                        // Test the date in the middle of two transitions.
                        t += (tzt.getTime() - t) / 2;
                        middle = false;
                        tzt = null;
                    } else {
                        t = tzt.getTime();
                    }
                }
            }
        }
    }
    long total = 0;
    logln("### Elapsed time by patterns ###");
    for (int i = 0; i < PATTERNS.length; i++) {
        logln(times[i] + "ms (" + PATTERNS[i] + ")");
        total += times[i];
    }
    logln("Total: " + total + "ms");
    logln("Iteration: " + testCounts);
}
Also used : Pattern(java.util.regex.Pattern) ULocale(android.icu.util.ULocale) Calendar(android.icu.util.Calendar) Date(java.util.Date) BasicTimeZone(android.icu.util.BasicTimeZone) SimpleTimeZone(android.icu.util.SimpleTimeZone) TimeZone(android.icu.util.TimeZone) BasicTimeZone(android.icu.util.BasicTimeZone) TimeZoneTransition(android.icu.util.TimeZoneTransition) ParseException(java.text.ParseException) SimpleDateFormat(android.icu.text.SimpleDateFormat) Test(org.junit.Test)

Example 8 with TimeZoneTransition

use of android.icu.util.TimeZoneTransition in project Resurrection_packages_apps_Settings by ResurrectionRemix.

the class TimeZoneInfoPreferenceController method findNextDstTransition.

private TimeZoneTransition findNextDstTransition(TimeZone timeZone) {
    if (!(timeZone instanceof BasicTimeZone)) {
        return null;
    }
    final BasicTimeZone basicTimeZone = (BasicTimeZone) timeZone;
    TimeZoneTransition transition = basicTimeZone.getNextTransition(mDate.getTime(), /* inclusive */
    false);
    do {
        if (transition.getTo().getDSTSavings() != transition.getFrom().getDSTSavings()) {
            break;
        }
        transition = basicTimeZone.getNextTransition(transition.getTime(), /*inclusive */
        false);
    } while (transition != null);
    return transition;
}
Also used : BasicTimeZone(android.icu.util.BasicTimeZone) TimeZoneTransition(android.icu.util.TimeZoneTransition)

Example 9 with TimeZoneTransition

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

the class TimeZoneInfoPreferenceController method findNextDstTransition.

private TimeZoneTransition findNextDstTransition(TimeZone timeZone) {
    if (!(timeZone instanceof BasicTimeZone)) {
        return null;
    }
    final BasicTimeZone basicTimeZone = (BasicTimeZone) timeZone;
    TimeZoneTransition transition = basicTimeZone.getNextTransition(mDate.getTime(), /* inclusive */
    false);
    do {
        if (transition.getTo().getDSTSavings() != transition.getFrom().getDSTSavings()) {
            break;
        }
        transition = basicTimeZone.getNextTransition(transition.getTime(), /*inclusive */
        false);
    } while (transition != null);
    return transition;
}
Also used : BasicTimeZone(android.icu.util.BasicTimeZone) TimeZoneTransition(android.icu.util.TimeZoneTransition)

Example 10 with TimeZoneTransition

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

the class SimpleDateFormat method parse.

/**
 * Overrides DateFormat
 * @see DateFormat
 */
@Override
public void parse(String text, Calendar cal, ParsePosition parsePos) {
    TimeZone backupTZ = null;
    Calendar resultCal = null;
    if (cal != calendar && !cal.getType().equals(calendar.getType())) {
        // Different calendar type
        // We use the time/zone from the input calendar, but
        // do not use the input calendar for field calculation.
        calendar.setTimeInMillis(cal.getTimeInMillis());
        backupTZ = calendar.getTimeZone();
        calendar.setTimeZone(cal.getTimeZone());
        resultCal = cal;
        cal = calendar;
    }
    int pos = parsePos.getIndex();
    if (pos < 0) {
        parsePos.setErrorIndex(0);
        return;
    }
    int start = pos;
    // Hold the day period until everything else is parsed, because we need
    // the hour to interpret time correctly.
    // Using an one-element array for output parameter.
    Output<DayPeriodRules.DayPeriod> dayPeriod = new Output<DayPeriodRules.DayPeriod>(null);
    Output<TimeType> tzTimeType = new Output<TimeType>(TimeType.UNKNOWN);
    boolean[] ambiguousYear = { false };
    // item index for the first numeric field within a contiguous numeric run
    int numericFieldStart = -1;
    // item length for the first numeric field within a contiguous numeric run
    int numericFieldLength = 0;
    // start index of numeric text run in the input text
    int numericStartPos = 0;
    MessageFormat numericLeapMonthFormatter = null;
    if (formatData.leapMonthPatterns != null && formatData.leapMonthPatterns.length >= DateFormatSymbols.DT_MONTH_PATTERN_COUNT) {
        numericLeapMonthFormatter = new MessageFormat(formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_NUMERIC], locale);
    }
    Object[] items = getPatternItems();
    int i = 0;
    while (i < items.length) {
        if (items[i] instanceof PatternItem) {
            // Handle pattern field
            PatternItem field = (PatternItem) items[i];
            if (field.isNumeric) {
                // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
                if (numericFieldStart == -1) {
                    // check if this field is followed by abutting another numeric field
                    if ((i + 1) < items.length && (items[i + 1] instanceof PatternItem) && ((PatternItem) items[i + 1]).isNumeric) {
                        // record the first numeric field within a numeric text run
                        numericFieldStart = i;
                        numericFieldLength = field.length;
                        numericStartPos = pos;
                    }
                }
            }
            if (numericFieldStart != -1) {
                // Handle a numeric field within abutting numeric fields
                int len = field.length;
                if (numericFieldStart == i) {
                    len = numericFieldLength;
                }
                // Parse a numeric field
                pos = subParse(text, pos, field.type, len, true, false, ambiguousYear, cal, numericLeapMonthFormatter, tzTimeType);
                if (pos < 0) {
                    // If the parse fails anywhere in the numeric run, back up to the
                    // start of the run and use shorter pattern length for the first
                    // numeric field.
                    --numericFieldLength;
                    if (numericFieldLength == 0) {
                        // can not make shorter any more
                        parsePos.setIndex(start);
                        parsePos.setErrorIndex(pos);
                        if (backupTZ != null) {
                            calendar.setTimeZone(backupTZ);
                        }
                        return;
                    }
                    i = numericFieldStart;
                    pos = numericStartPos;
                    continue;
                }
            } else if (field.type != 'l') {
                // (SMALL LETTER L) obsolete pattern char just gets ignored
                // Handle a non-numeric field or a non-abutting numeric field
                numericFieldStart = -1;
                int s = pos;
                pos = subParse(text, pos, field.type, field.length, false, true, ambiguousYear, cal, numericLeapMonthFormatter, tzTimeType, dayPeriod);
                if (pos < 0) {
                    if (pos == ISOSpecialEra) {
                        // era not present, in special cases allow this to continue
                        pos = s;
                        if (i + 1 < items.length) {
                            String patl = null;
                            // if it will cause a class cast exception to String, we can't use it
                            try {
                                patl = (String) items[i + 1];
                            } catch (ClassCastException cce) {
                                parsePos.setIndex(start);
                                parsePos.setErrorIndex(s);
                                if (backupTZ != null) {
                                    calendar.setTimeZone(backupTZ);
                                }
                                return;
                            }
                            // get next item in pattern
                            if (patl == null)
                                patl = (String) items[i + 1];
                            int plen = patl.length();
                            int idx = 0;
                            // Skip contiguous white spaces.
                            while (idx < plen) {
                                char pch = patl.charAt(idx);
                                if (PatternProps.isWhiteSpace(pch))
                                    idx++;
                                else
                                    break;
                            }
                            // if next item in pattern is all whitespace, skip it
                            if (idx == plen) {
                                i++;
                            }
                        }
                    } else {
                        parsePos.setIndex(start);
                        parsePos.setErrorIndex(s);
                        if (backupTZ != null) {
                            calendar.setTimeZone(backupTZ);
                        }
                        return;
                    }
                }
            }
        } else {
            // Handle literal pattern text literal
            numericFieldStart = -1;
            boolean[] complete = new boolean[1];
            pos = matchLiteral(text, pos, items, i, complete);
            if (!complete[0]) {
                // Set the position of mismatch
                parsePos.setIndex(start);
                parsePos.setErrorIndex(pos);
                if (backupTZ != null) {
                    calendar.setTimeZone(backupTZ);
                }
                return;
            }
        }
        ++i;
    }
    // Special hack for trailing "." after non-numeric field.
    if (pos < text.length()) {
        char extra = text.charAt(pos);
        if (extra == '.' && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_WHITESPACE) && items.length != 0) {
            // only do if the last field is not numeric
            Object lastItem = items[items.length - 1];
            if (lastItem instanceof PatternItem && !((PatternItem) lastItem).isNumeric) {
                // skip the extra "."
                pos++;
            }
        }
    }
    // If dayPeriod is set, use it in conjunction with hour-of-day to determine am/pm.
    if (dayPeriod.value != null) {
        DayPeriodRules ruleSet = DayPeriodRules.getInstance(getLocale());
        if (!cal.isSet(Calendar.HOUR) && !cal.isSet(Calendar.HOUR_OF_DAY)) {
            // If hour is not set, set time to the midpoint of current day period, overwriting
            // minutes if it's set.
            double midPoint = ruleSet.getMidPointForDayPeriod(dayPeriod.value);
            // Truncate midPoint toward zero to get the hour.
            // Any leftover means it was a half-hour.
            int midPointHour = (int) midPoint;
            int midPointMinute = (midPoint - midPointHour) > 0 ? 30 : 0;
            // No need to set am/pm because hour-of-day is set last therefore takes precedence.
            cal.set(Calendar.HOUR_OF_DAY, midPointHour);
            cal.set(Calendar.MINUTE, midPointMinute);
        } else {
            int hourOfDay;
            if (cal.isSet(Calendar.HOUR_OF_DAY)) {
                // Hour is parsed in 24-hour format.
                hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
            } else {
                // Hour is parsed in 12-hour format.
                hourOfDay = cal.get(Calendar.HOUR);
                // so 0 unambiguously means a 24-hour time from above.
                if (hourOfDay == 0) {
                    hourOfDay = 12;
                }
            }
            assert (0 <= hourOfDay && hourOfDay <= 23);
            // If hour-of-day is 0 or 13 thru 23 then input time in unambiguously in 24-hour format.
            if (hourOfDay == 0 || (13 <= hourOfDay && hourOfDay <= 23)) {
                // Make hour-of-day take precedence over (hour + am/pm) by setting it again.
                cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
            } else {
                // - cal.get(MINUTE) will return 0 if MINUTE is unset, which works.
                if (hourOfDay == 12) {
                    hourOfDay = 0;
                }
                double currentHour = hourOfDay + cal.get(Calendar.MINUTE) / 60.0;
                double midPointHour = ruleSet.getMidPointForDayPeriod(dayPeriod.value);
                double hoursAheadMidPoint = currentHour - midPointHour;
                // Assume current time is in the AM.
                if (-6 <= hoursAheadMidPoint && hoursAheadMidPoint < 6) {
                    // Assumption holds; set time as such.
                    cal.set(Calendar.AM_PM, 0);
                } else {
                    cal.set(Calendar.AM_PM, 1);
                }
            }
        }
    }
    // At this point the fields of Calendar have been set.  Calendar
    // will fill in default values for missing fields when the time
    // is computed.
    parsePos.setIndex(pos);
    // the year correctly to start with in other cases -- see subParse().
    try {
        TimeType tztype = tzTimeType.value;
        if (ambiguousYear[0] || tztype != TimeType.UNKNOWN) {
            // We need a copy of the fields, and we need to avoid triggering a call to
            // complete(), which will recalculate the fields.  Since we can't access
            // the fields[] array in Calendar, we clone the entire object.  This will
            // stop working if Calendar.clone() is ever rewritten to call complete().
            Calendar copy;
            if (ambiguousYear[0]) {
                // the two-digit year == the default start year
                copy = (Calendar) cal.clone();
                Date parsedDate = copy.getTime();
                if (parsedDate.before(getDefaultCenturyStart())) {
                    // We can't use add here because that does a complete() first.
                    cal.set(Calendar.YEAR, getDefaultCenturyStartYear() + 100);
                }
            }
            if (tztype != TimeType.UNKNOWN) {
                copy = (Calendar) cal.clone();
                TimeZone tz = copy.getTimeZone();
                BasicTimeZone btz = null;
                if (tz instanceof BasicTimeZone) {
                    btz = (BasicTimeZone) tz;
                }
                // Get local millis
                copy.set(Calendar.ZONE_OFFSET, 0);
                copy.set(Calendar.DST_OFFSET, 0);
                long localMillis = copy.getTimeInMillis();
                // Make sure parsed time zone type (Standard or Daylight)
                // matches the rule used by the parsed time zone.
                int[] offsets = new int[2];
                if (btz != null) {
                    if (tztype == TimeType.STANDARD) {
                        btz.getOffsetFromLocal(localMillis, BasicTimeZone.LOCAL_STD, BasicTimeZone.LOCAL_STD, offsets);
                    } else {
                        btz.getOffsetFromLocal(localMillis, BasicTimeZone.LOCAL_DST, BasicTimeZone.LOCAL_DST, offsets);
                    }
                } else {
                    // No good way to resolve ambiguous time at transition,
                    // but following code work in most case.
                    tz.getOffset(localMillis, true, offsets);
                    if (tztype == TimeType.STANDARD && offsets[1] != 0 || tztype == TimeType.DAYLIGHT && offsets[1] == 0) {
                        // Roll back one day and try it again.
                        // Note: This code assumes 1. timezone transition only happens
                        // once within 24 hours at max
                        // 2. the difference of local offsets at the transition is
                        // less than 24 hours.
                        tz.getOffset(localMillis - (24 * 60 * 60 * 1000), true, offsets);
                    }
                }
                // Now, compare the results with parsed type, either standard or
                // daylight saving time
                int resolvedSavings = offsets[1];
                if (tztype == TimeType.STANDARD) {
                    if (offsets[1] != 0) {
                        // Override DST_OFFSET = 0 in the result calendar
                        resolvedSavings = 0;
                    }
                } else {
                    // tztype == TZTYPE_DST
                    if (offsets[1] == 0) {
                        if (btz != null) {
                            long time = localMillis + offsets[0];
                            // We use the nearest daylight saving time rule.
                            TimeZoneTransition beforeTrs, afterTrs;
                            long beforeT = time, afterT = time;
                            int beforeSav = 0, afterSav = 0;
                            // Search for DST rule before or on the time
                            while (true) {
                                beforeTrs = btz.getPreviousTransition(beforeT, true);
                                if (beforeTrs == null) {
                                    break;
                                }
                                beforeT = beforeTrs.getTime() - 1;
                                beforeSav = beforeTrs.getFrom().getDSTSavings();
                                if (beforeSav != 0) {
                                    break;
                                }
                            }
                            // Search for DST rule after the time
                            while (true) {
                                afterTrs = btz.getNextTransition(afterT, false);
                                if (afterTrs == null) {
                                    break;
                                }
                                afterT = afterTrs.getTime();
                                afterSav = afterTrs.getTo().getDSTSavings();
                                if (afterSav != 0) {
                                    break;
                                }
                            }
                            if (beforeTrs != null && afterTrs != null) {
                                if (time - beforeT > afterT - time) {
                                    resolvedSavings = afterSav;
                                } else {
                                    resolvedSavings = beforeSav;
                                }
                            } else if (beforeTrs != null && beforeSav != 0) {
                                resolvedSavings = beforeSav;
                            } else if (afterTrs != null && afterSav != 0) {
                                resolvedSavings = afterSav;
                            } else {
                                resolvedSavings = btz.getDSTSavings();
                            }
                        } else {
                            resolvedSavings = tz.getDSTSavings();
                        }
                        if (resolvedSavings == 0) {
                            // Final fallback
                            resolvedSavings = millisPerHour;
                        }
                    }
                }
                cal.set(Calendar.ZONE_OFFSET, offsets[0]);
                cal.set(Calendar.DST_OFFSET, resolvedSavings);
            }
        }
    }// if any fields are out of range, e.g., MONTH == 17.
     catch (IllegalArgumentException e) {
        parsePos.setErrorIndex(pos);
        parsePos.setIndex(start);
        if (backupTZ != null) {
            calendar.setTimeZone(backupTZ);
        }
        return;
    }
    // instead of the input calendar
    if (resultCal != null) {
        resultCal.setTimeZone(cal.getTimeZone());
        resultCal.setTimeInMillis(cal.getTimeInMillis());
    }
    // Restore the original time zone if required
    if (backupTZ != null) {
        calendar.setTimeZone(backupTZ);
    }
}
Also used : DayPeriodRules(android.icu.impl.DayPeriodRules) HebrewCalendar(android.icu.util.HebrewCalendar) Calendar(android.icu.util.Calendar) AttributedString(java.text.AttributedString) Date(java.util.Date) TimeType(android.icu.text.TimeZoneFormat.TimeType) TimeZone(android.icu.util.TimeZone) BasicTimeZone(android.icu.util.BasicTimeZone) BasicTimeZone(android.icu.util.BasicTimeZone) TimeZoneTransition(android.icu.util.TimeZoneTransition) Output(android.icu.util.Output)

Aggregations

TimeZoneTransition (android.icu.util.TimeZoneTransition)22 BasicTimeZone (android.icu.util.BasicTimeZone)17 Test (org.junit.Test)8 TimeZone (android.icu.util.TimeZone)6 AnnualTimeZoneRule (android.icu.util.AnnualTimeZoneRule)5 InitialTimeZoneRule (android.icu.util.InitialTimeZoneRule)5 TimeZoneRule (android.icu.util.TimeZoneRule)5 Date (java.util.Date)5 Calendar (android.icu.util.Calendar)4 SimpleTimeZone (android.icu.util.SimpleTimeZone)4 TimeArrayTimeZoneRule (android.icu.util.TimeArrayTimeZoneRule)4 VTimeZone (android.icu.util.VTimeZone)3 ByteArrayInputStream (java.io.ByteArrayInputStream)2 ByteArrayOutputStream (java.io.ByteArrayOutputStream)2 IOException (java.io.IOException)2 InputStreamReader (java.io.InputStreamReader)2 OutputStreamWriter (java.io.OutputStreamWriter)2 Instant (java.time.Instant)2 ZoneOffset (java.time.ZoneOffset)2 ZoneOffsetTransition (java.time.zone.ZoneOffsetTransition)2