12ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/* GENERATED SOURCE. DO NOT MODIFY. */
2f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert// © 2016 and later: Unicode, Inc. and others.
3f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License
42ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/*
52ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *******************************************************************************
62ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * Copyright (C) 2007-2014, International Business Machines Corporation and    *
72ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * others. All Rights Reserved.                                                *
82ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *******************************************************************************
92ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller */
102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerpackage android.icu.util;
112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.ArrayList;
122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.BitSet;
132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Date;
142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.List;
152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.impl.Grego;
172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/**
192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <code>RuleBasedTimeZone</code> is a concrete subclass of <code>TimeZone</code> that allows users to define
202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * custom historic time transition rules.
212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * @see android.icu.util.TimeZoneRule
232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
24836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller * @hide Only a subset of ICU is exposed in Android
252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller */
262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerpublic class RuleBasedTimeZone extends BasicTimeZone {
272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final long serialVersionUID = 7580833058949327935L;
292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private final InitialTimeZoneRule initialRule;
312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private List<TimeZoneRule> historicRules;
322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private AnnualTimeZoneRule[] finalRules;
332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private transient List<TimeZoneTransition> historicTransitions;
352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private transient boolean upToDate;
362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Constructs a <code>RuleBasedTimeZone</code> object with the ID and the
392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <code>InitialTimeZoneRule</code>
402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param id                The time zone ID.
422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param initialRule       The initial time zone rule.
432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public RuleBasedTimeZone(String id, InitialTimeZoneRule initialRule) {
452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        super(id);
462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.initialRule = initialRule;
472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Adds the <code>TimeZoneRule</code> which represents time transitions.
512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * The <code>TimeZoneRule</code> must have start times, that is, the result
522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * of {@link android.icu.util.TimeZoneRule#isTransitionRule()} must be true.
532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Otherwise, <code>IllegalArgumentException</code> is thrown.
542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param rule The <code>TimeZoneRule</code>.
562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public void addTransitionRule(TimeZoneRule rule) {
582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (isFrozen()) {
592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new UnsupportedOperationException("Attempt to modify a frozen RuleBasedTimeZone instance.");
602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!rule.isTransitionRule()) {
622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new IllegalArgumentException("Rule must be a transition rule");
632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (rule instanceof AnnualTimeZoneRule
652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                && ((AnnualTimeZoneRule)rule).getEndYear() == AnnualTimeZoneRule.MAX_YEAR) {
662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // One of the final rules applicable in future forever
672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (finalRules == null) {
682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                finalRules = new AnnualTimeZoneRule[2];
692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                finalRules[0] = (AnnualTimeZoneRule)rule;
702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else if (finalRules[1] == null) {
712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                finalRules[1] = (AnnualTimeZoneRule)rule;
722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Only a pair of AnnualTimeZoneRule is allowed.
742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                throw new IllegalStateException("Too many final rules");
752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // If this is not a final rule, add it to the historic rule list
782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (historicRules == null) {
792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                historicRules = new ArrayList<TimeZoneRule>();
802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            historicRules.add(rule);
822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Mark dirty, so transitions are recalculated when offset information is
842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // accessed next time.
852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        upToDate = false;
862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public int getOffset(int era, int year, int month, int day, int dayOfWeek,
932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int milliseconds) {
942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (era == GregorianCalendar.BC) {
952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Convert to extended year
962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            year = 1 - year;
972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        long time = Grego.fieldsToDay(year, month, day) * Grego.MILLIS_PER_DAY + milliseconds;
992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int[] offsets = new int[2];
1002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        getOffset(time, true, LOCAL_DST, LOCAL_STD, offsets);
1012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return (offsets[0] + offsets[1]);
1022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
1062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
1082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public void getOffset(long time, boolean local, int[] offsets) {
1092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        getOffset(time, local, LOCAL_FORMER, LOCAL_LATTER, offsets);
1102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
1142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @deprecated This API is ICU internal only.
115836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller     * @hide draft / provisional / internal are hidden on Android
1162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Deprecated
1182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
1192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public void getOffsetFromLocal(long date,
1202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) {
1212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        getOffset(date, true, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
1222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
1262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
1282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public int getRawOffset() {
1292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Note: This implementation returns standard GMT offset
1302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // as of current time.
1312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        long now = System.currentTimeMillis();
1322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int[] offsets = new int[2];
1332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        getOffset(now, false, offsets);
1342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return offsets[0];
1352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
1392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
1412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public boolean inDaylightTime(Date date) {
1422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int[] offsets = new int[2];
1432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        getOffset(date.getTime(), false, offsets);
1442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return (offsets[1] != 0);
1452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
1492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
1512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    ///CLOVER:OFF
1522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public void setRawOffset(int offsetMillis) {
1532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // TODO: Do nothing for now..
1542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        throw new UnsupportedOperationException("setRawOffset in RuleBasedTimeZone is not supported.");
1552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    ///CLOVER:ON
1572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
1602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
1622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public boolean useDaylightTime() {
1632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Note: This implementation returns true when
1642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // daylight saving time is used as of now or
1652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // after the next transition.
1662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        long now = System.currentTimeMillis();
1672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int[] offsets = new int[2];
1682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        getOffset(now, false, offsets);
1692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (offsets[1] != 0) {
1702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return true;
1712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // If DST is not used now, check if DST is used after the next transition
1732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        TimeZoneTransition tt = getNextTransition(now, false);
1742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (tt != null && tt.getTo().getDSTSavings() != 0) {
1752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return true;
1762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return false;
1782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
1822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
1842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public boolean observesDaylightTime() {
1852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        long time = System.currentTimeMillis();
1862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Check if daylight saving time is observed now.
1882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int[] offsets = new int[2];
1892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        getOffset(time, false, offsets);
1902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (offsets[1] != 0) {
1912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return true;
1922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // If DST is not used now, check if DST is used after each transition.
1952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        BitSet checkFinals = finalRules == null ? null : new BitSet(finalRules.length);
1962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        while (true) {
1972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            TimeZoneTransition tt = getNextTransition(time, false);
1982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (tt == null) {
1992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // no more transition
2002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                break;
2012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            TimeZoneRule toRule = tt.getTo();
2032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (toRule.getDSTSavings() != 0) {
2042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return true;
2052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (checkFinals != null) {
2072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // final rules exist - check if we saw all of them
2082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                for (int i = 0; i < finalRules.length; i++) {
2092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (finalRules[i].equals(toRule)) {
2102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        checkFinals.set(i);
2112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
2122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (checkFinals.cardinality() == finalRules.length) {
2142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // already saw all final rules
2152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    break;
2162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            time = tt.getTime();
2192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return false;
2212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
2242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
2252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
2272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public boolean hasSameRules(TimeZone other) {
2282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (this == other) {
2292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return true;
2302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!(other instanceof RuleBasedTimeZone)) {
2332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // We cannot reasonably compare rules in different types
2342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return false;
2352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        RuleBasedTimeZone otherRBTZ = (RuleBasedTimeZone)other;
2372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // initial rule
2392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!initialRule.isEquivalentTo(otherRBTZ.initialRule)) {
2402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return false;
2412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // final rules
2442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (finalRules != null && otherRBTZ.finalRules != null) {
2452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (int i = 0; i < finalRules.length; i++) {
2462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (finalRules[i] == null && otherRBTZ.finalRules[i] == null) {
2472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    continue;
2482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (finalRules[i] != null && otherRBTZ.finalRules[i] != null
2502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        && finalRules[i].isEquivalentTo(otherRBTZ.finalRules[i])) {
2512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    continue;
2522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return false;
2552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else if (finalRules != null || otherRBTZ.finalRules != null) {
2572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return false;
2582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // historic rules
2612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (historicRules != null && otherRBTZ.historicRules != null) {
2622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (historicRules.size() != otherRBTZ.historicRules.size()) {
2632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return false;
2642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (TimeZoneRule rule : historicRules) {
2662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                boolean foundSameRule = false;
2672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                for (TimeZoneRule orule : otherRBTZ.historicRules) {
2682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (rule.isEquivalentTo(orule)) {
2692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        foundSameRule = true;
2702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        break;
2712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
2722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (!foundSameRule) {
2742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    return false;
2752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else if (historicRules != null || otherRBTZ.historicRules != null) {
2782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return false;
2792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return true;
2812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // BasicTimeZone methods
2842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
2862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
2872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
2892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public TimeZoneRule[] getTimeZoneRules() {
2902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int size = 1;
2912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (historicRules != null) {
2922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            size += historicRules.size();
2932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (finalRules != null) {
2962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (finalRules[1] != null) {
2972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                size += 2;
2982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
2992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                size++;
3002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        TimeZoneRule[] rules = new TimeZoneRule[size];
3032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        rules[0] = initialRule;
3042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int idx = 1;
3062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (historicRules != null) {
3072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (; idx < historicRules.size() + 1; idx++) {
3082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                rules[idx] = historicRules.get(idx - 1);
3092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (finalRules != null) {
3122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            rules[idx++] = finalRules[0];
3132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (finalRules[1] != null) {
3142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                rules[idx] = finalRules[1];
3152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return rules;
3182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
3212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
3222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
3242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public TimeZoneTransition getNextTransition(long base, boolean inclusive) {
3252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        complete();
3262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (historicTransitions == null) {
3272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return null;
3282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        boolean isFinal = false;
330f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        TimeZoneTransition result;
3312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        TimeZoneTransition tzt = historicTransitions.get(0);
3322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        long tt = tzt.getTime();
3332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (tt > base || (inclusive && tt == base)) {
3342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            result = tzt;
3352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
3362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int idx = historicTransitions.size() - 1;
3372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            tzt = historicTransitions.get(idx);
3382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            tt = tzt.getTime();
3392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (inclusive && tt == base) {
3402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                result = tzt;
3412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else if (tt <= base) {
3422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (finalRules != null) {
3432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // Find a transion time with finalRules
3442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    Date start0 = finalRules[0].getNextStart(base,
3452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            finalRules[1].getRawOffset(), finalRules[1].getDSTSavings(), inclusive);
3462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    Date start1 = finalRules[1].getNextStart(base,
3472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            finalRules[0].getRawOffset(), finalRules[0].getDSTSavings(), inclusive);
3482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (start1.after(start0)) {
3502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        tzt = new TimeZoneTransition(start0.getTime(), finalRules[1], finalRules[0]);
3512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    } else {
3522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        tzt = new TimeZoneTransition(start1.getTime(), finalRules[0], finalRules[1]);
3532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
3542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    result = tzt;
3552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    isFinal = true;
3562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
3572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    return null;
3582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
3592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
3602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Find a transition within the historic transitions
3612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                idx--;
3622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                TimeZoneTransition prev = tzt;
3632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                while (idx > 0) {
3642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    tzt = historicTransitions.get(idx);
3652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    tt = tzt.getTime();
3662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (tt < base || (!inclusive && tt == base)) {
3672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        break;
3682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
3692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    idx--;
3702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    prev = tzt;
3712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
3722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                result = prev;
3732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
375f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        // For now, this implementation ignore transitions with only zone name changes.
376f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        TimeZoneRule from = result.getFrom();
377f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        TimeZoneRule to = result.getTo();
378f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        if (from.getRawOffset() == to.getRawOffset()
379f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                && from.getDSTSavings() == to.getDSTSavings()) {
380f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            // No offset changes.  Try next one if not final
381f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            if (isFinal) {
382f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                return null;
383f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            } else {
384f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                result = getNextTransition(result.getTime(), false /* always exclusive */);
3852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return result;
3882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
3912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
3922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
3942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public TimeZoneTransition getPreviousTransition(long base, boolean inclusive) {
3952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        complete();
3962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (historicTransitions == null) {
3972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return null;
3982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
399f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        TimeZoneTransition result;
4002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        TimeZoneTransition tzt = historicTransitions.get(0);
4012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        long tt = tzt.getTime();
4022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (inclusive && tt == base) {
4032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            result = tzt;
4042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else if (tt >= base) {
4052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return null;
4062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
4072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int idx = historicTransitions.size() - 1;
4082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            tzt = historicTransitions.get(idx);
4092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            tt = tzt.getTime();
4102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (inclusive && tt == base) {
4112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                result = tzt;
4122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else if (tt < base) {
4132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (finalRules != null) {
4142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // Find a transion time with finalRules
4152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    Date start0 = finalRules[0].getPreviousStart(base,
4162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            finalRules[1].getRawOffset(), finalRules[1].getDSTSavings(), inclusive);
4172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    Date start1 = finalRules[1].getPreviousStart(base,
4182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            finalRules[0].getRawOffset(), finalRules[0].getDSTSavings(), inclusive);
4192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (start1.before(start0)) {
4212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        tzt = new TimeZoneTransition(start0.getTime(), finalRules[1], finalRules[0]);
4222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    } else {
4232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        tzt = new TimeZoneTransition(start1.getTime(), finalRules[0], finalRules[1]);
4242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
4262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                result = tzt;
4272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
4282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Find a transition within the historic transitions
4292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                idx--;
4302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                while (idx >= 0) {
4312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    tzt = historicTransitions.get(idx);
4322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    tt = tzt.getTime();
4332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (tt < base || (inclusive && tt == base)) {
4342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        break;
4352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    idx--;
4372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
4382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                result = tzt;
4392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
4402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
441f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        // For now, this implementation ignore transitions with only zone name changes.
442f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        TimeZoneRule from = result.getFrom();
443f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        TimeZoneRule to = result.getTo();
444f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        if (from.getRawOffset() == to.getRawOffset()
445f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                && from.getDSTSavings() == to.getDSTSavings()) {
446f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            // No offset changes.  Try previous one
447f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            result = getPreviousTransition(result.getTime(), false /* always exclusive */);
4482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return result;
4502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
4512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
4532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
4542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
4552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
4562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Object clone() {
4572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (isFrozen()) {
4582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return this;
4592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return cloneAsThawed();
4612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
4622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // private stuff
4642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
4662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Resolve historic transition times and update fields used for offset
4672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * calculation.
4682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
4692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private void complete() {
4702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (upToDate) {
4712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // No rules were added since last time.
4722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return;
4732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Make sure either no final rules or a pair of AnnualTimeZoneRules
4762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // are available.
4772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (finalRules != null && finalRules[1] == null) {
4782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new IllegalStateException("Incomplete final rules");
4792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Create a TimezoneTransition and add to the list
4822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (historicRules != null || finalRules != null) {
4832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            TimeZoneRule curRule = initialRule;
4842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            long lastTransitionTime = Grego.MIN_MILLIS;
4852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Build the transition array which represents historical time zone
4872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // transitions.
4882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (historicRules != null) {
4892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                BitSet done = new BitSet(historicRules.size()); // for skipping rules already processed
4902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                while (true) {
4922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    int curStdOffset = curRule.getRawOffset();
4932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    int curDstSavings = curRule.getDSTSavings();
4942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    long nextTransitionTime = Grego.MAX_MILLIS;
4952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    TimeZoneRule nextRule = null;
4962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    Date d;
4972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    long tt;
4982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    for (int i = 0; i < historicRules.size(); i++) {
5002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (done.get(i)) {
5012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            continue;
5022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
5032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        TimeZoneRule r = historicRules.get(i);
5042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        d = r.getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false);
5052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (d == null) {
5062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            // No more transitions from this rule - skip this rule next time
5072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            done.set(i);
5082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        } else {
5092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            if (r == curRule ||
5102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                    (r.getName().equals(curRule.getName())
5112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                            && r.getRawOffset() == curRule.getRawOffset()
5122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                            && r.getDSTSavings() == curRule.getDSTSavings())) {
5132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                continue;
5142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            }
5152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            tt = d.getTime();
5162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            if (tt < nextTransitionTime) {
5172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                nextTransitionTime = tt;
5182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                nextRule = r;
5192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            }
5202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
5212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
5222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (nextRule ==  null) {
5242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // Check if all historic rules are done
5252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        boolean bDoneAll = true;
5262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        for (int j = 0; j < historicRules.size(); j++) {
5272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            if (!done.get(j)) {
5282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                bDoneAll = false;
5292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                break;
5302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            }
5312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
5322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (bDoneAll) {
5332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            break;
5342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
5352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
5362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (finalRules != null) {
5382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // Check if one of final rules has earlier transition date
5392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        for (int i = 0; i < 2 /* finalRules.length */; i++) {
5402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            if (finalRules[i] == curRule) {
5412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                continue;
5422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            }
5432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            d = finalRules[i].getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false);
5442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            if (d != null) {
5452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                tt = d.getTime();
5462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                if (tt < nextTransitionTime) {
5472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                    nextTransitionTime = tt;
5482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                    nextRule = finalRules[i];
5492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                }
5502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            }
5512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
5522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
5532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (nextRule == null) {
5552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // Nothing more
5562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        break;
5572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
5582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (historicTransitions == null) {
5602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        historicTransitions = new ArrayList<TimeZoneTransition>();
5612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
5622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    historicTransitions.add(new TimeZoneTransition(nextTransitionTime, curRule, nextRule));
5632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    lastTransitionTime = nextTransitionTime;
5642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    curRule = nextRule;
5652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
5662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (finalRules != null) {
5682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (historicTransitions == null) {
5692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    historicTransitions = new ArrayList<TimeZoneTransition>();
5702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
5712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Append the first transition for each
5722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                Date d0 = finalRules[0].getNextStart(lastTransitionTime, curRule.getRawOffset(), curRule.getDSTSavings(), false);
5732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                Date d1 = finalRules[1].getNextStart(lastTransitionTime, curRule.getRawOffset(), curRule.getDSTSavings(), false);
5742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (d1.after(d0)) {
5752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    historicTransitions.add(new TimeZoneTransition(d0.getTime(), curRule, finalRules[0]));
5762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    d1 = finalRules[1].getNextStart(d0.getTime(), finalRules[0].getRawOffset(), finalRules[0].getDSTSavings(), false);
5772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    historicTransitions.add(new TimeZoneTransition(d1.getTime(), finalRules[0], finalRules[1]));
5782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
5792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    historicTransitions.add(new TimeZoneTransition(d1.getTime(), curRule, finalRules[1]));
5802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    d0 = finalRules[0].getNextStart(d1.getTime(), finalRules[1].getRawOffset(), finalRules[1].getDSTSavings(), false);
5812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    historicTransitions.add(new TimeZoneTransition(d0.getTime(), finalRules[1], finalRules[0]));
5822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
5832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        upToDate = true;
5862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
5892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * getOffset internal implementation
5902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private void getOffset(long time, boolean local, int NonExistingTimeOpt, int DuplicatedTimeOpt, int[] offsets) {
5922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        complete();
5932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        TimeZoneRule rule = null;
5942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (historicTransitions == null) {
5952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            rule = initialRule;
5962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
5972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            long tstart = getTransitionTime(historicTransitions.get(0),
5982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    local, NonExistingTimeOpt, DuplicatedTimeOpt);
5992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (time < tstart) {
6002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                rule = initialRule;
6012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
6022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                int idx = historicTransitions.size() - 1;
6032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                long tend = getTransitionTime(historicTransitions.get(idx),
6042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        local, NonExistingTimeOpt, DuplicatedTimeOpt);
6052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (time > tend) {
6062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (finalRules != null) {
6072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        rule = findRuleInFinal(time, local, NonExistingTimeOpt, DuplicatedTimeOpt);
6082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
6092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (rule == null) {
6102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // no final rules or the given time is before the first transition
6112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // specified by the final rules -> use the last rule
6122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        rule = (historicTransitions.get(idx)).getTo();
6132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
6142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
6152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // Find a historical transition
6162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    while (idx >= 0) {
6172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (time >= getTransitionTime(historicTransitions.get(idx),
6182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                local, NonExistingTimeOpt, DuplicatedTimeOpt)) {
6192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            break;
6202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
6212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        idx--;
6222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
6232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    rule = (historicTransitions.get(idx)).getTo();
6242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
6252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        offsets[0] = rule.getRawOffset();
6282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        offsets[1] = rule.getDSTSavings();
6292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
6322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Find a time zone rule applicable to the specified time
6332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private TimeZoneRule findRuleInFinal(long time, boolean local, int NonExistingTimeOpt, int DuplicatedTimeOpt) {
6352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (finalRules == null || finalRules.length != 2 || finalRules[0] == null || finalRules[1] == null) {
6362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return null;
6372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Date start0, start1;
6402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        long base;
6412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int localDelta;
6422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        base = time;
6442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (local) {
6452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            localDelta = getLocalDelta(finalRules[1].getRawOffset(), finalRules[1].getDSTSavings(),
6462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    finalRules[0].getRawOffset(), finalRules[0].getDSTSavings(),
6472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    NonExistingTimeOpt, DuplicatedTimeOpt);
6482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            base -= localDelta;
6492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        start0 = finalRules[0].getPreviousStart(base, finalRules[1].getRawOffset(), finalRules[1].getDSTSavings(), true);
6512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        base = time;
6532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (local) {
6542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            localDelta = getLocalDelta(finalRules[0].getRawOffset(), finalRules[0].getDSTSavings(),
6552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    finalRules[1].getRawOffset(), finalRules[1].getDSTSavings(),
6562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    NonExistingTimeOpt, DuplicatedTimeOpt);
6572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            base -= localDelta;
6582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        start1 = finalRules[1].getPreviousStart(base, finalRules[0].getRawOffset(), finalRules[0].getDSTSavings(), true);
6602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (start0 == null || start1 == null) {
6622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (start0 != null) {
6632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return finalRules[0];
6642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else if (start1 != null) {
6652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return finalRules[1];
6662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Both rules take effect after the given time
6682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return null;
6692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return start0.after(start1) ? finalRules[0] : finalRules[1];
6722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
6752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Get the transition time in local wall clock
6762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static long getTransitionTime(TimeZoneTransition tzt, boolean local,
6782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int NonExistingTimeOpt, int DuplicatedTimeOpt) {
6792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        long time = tzt.getTime();
6802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (local) {
6812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            time += getLocalDelta(tzt.getFrom().getRawOffset(), tzt.getFrom().getDSTSavings(),
6822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                tzt.getTo().getRawOffset(), tzt.getTo().getDSTSavings(),
6832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                NonExistingTimeOpt, DuplicatedTimeOpt);
6842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return time;
6862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
6892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Returns amount of local time adjustment used for checking rule transitions
6902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static int getLocalDelta(int rawBefore, int dstBefore, int rawAfter, int dstAfter,
6922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int NonExistingTimeOpt, int DuplicatedTimeOpt) {
6932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int delta = 0;
6942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int offsetBefore = rawBefore + dstBefore;
6962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int offsetAfter = rawAfter + dstAfter;
6972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        boolean dstToStd = (dstBefore != 0) && (dstAfter == 0);
6992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        boolean stdToDst = (dstBefore == 0) && (dstAfter != 0);
7002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (offsetAfter - offsetBefore >= 0) {
7022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Positive transition, which makes a non-existing local time range
7032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_STD && dstToStd)
7042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    || ((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_DST && stdToDst)) {
7052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                delta = offsetBefore;
7062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else if (((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_STD && stdToDst)
7072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    || ((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_DST && dstToStd)) {
7082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                delta = offsetAfter;
7092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else if ((NonExistingTimeOpt & FORMER_LATTER_MASK) == LOCAL_LATTER) {
7102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                delta = offsetBefore;
7112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
7122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Interprets the time with rule before the transition,
7132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // default for non-existing time range
7142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                delta = offsetAfter;
7152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
7162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
7172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Negative transition, which makes a duplicated local time range
7182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_STD && dstToStd)
7192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    || ((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_DST && stdToDst)) {
7202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                delta = offsetAfter;
7212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else if (((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_STD && stdToDst)
7222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    || ((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_DST && dstToStd)) {
7232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                delta = offsetBefore;
7242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else if ((DuplicatedTimeOpt & FORMER_LATTER_MASK) == LOCAL_FORMER) {
7252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                delta = offsetBefore;
7262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
7272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Interprets the time with rule after the transition,
7282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // default for duplicated local time range
7292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                delta = offsetAfter;
7302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
7312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return delta;
7332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // Freezable stuffs
7362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private volatile transient boolean isFrozen = false;
7372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
7392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
7402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
7412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public boolean isFrozen() {
7422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return isFrozen;
7432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
7462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
7472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
7482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public TimeZone freeze() {
7492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        complete();
7502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        isFrozen = true;
7512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
7522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
7552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
7562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
7572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public TimeZone cloneAsThawed() {
7582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        RuleBasedTimeZone tz = (RuleBasedTimeZone)super.cloneAsThawed();
7592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (historicRules != null) {
7602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            tz.historicRules = new ArrayList<TimeZoneRule>(historicRules); // rules are immutable
7612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (finalRules != null) {
7632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            tz.finalRules = finalRules.clone();
7642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        tz.isFrozen = false;
7662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return tz;
7672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller}
7692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
770