12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others.
22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License
37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /*
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  *******************************************************************************
52d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert  * Copyright (C) 2005-2016, International Business Machines Corporation and
62d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert  * others. All Rights Reserved.
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  *******************************************************************************
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert  */
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.impl;
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.IOException;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.ObjectInputStream;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Arrays;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Date;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.MissingResourceException;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.AnnualTimeZoneRule;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.BasicTimeZone;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Calendar;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.DateTimeRule;
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.GregorianCalendar;
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.InitialTimeZoneRule;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.SimpleTimeZone;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.TimeArrayTimeZoneRule;
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.TimeZone;
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.TimeZoneRule;
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.TimeZoneTransition;
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.UResourceBundle;
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * A time zone based on the Olson tz database.  Olson time zones change
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * behavior over time.  The raw offset, rules, presence or absence of
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * daylight savings time, and even the daylight savings amount can all
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * vary.
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * This class uses a resource bundle named "zoneinfo".  Zoneinfo is a
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * table containing different kinds of resources.  In several places,
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * zones are referred to using integers.  A zone's integer is a number
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * from 0..n-1, where n is the number of zones, with the zones sorted
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in lexicographic order.
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1. Zones.  These have keys corresponding to the Olson IDs, e.g.,
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "Asia/Shanghai".  Each resource describes the behavior of the given
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * zone.  Zones come in two different formats.
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   a. Zone (table).  A zone is a table resource contains several
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   type of resources below:
482d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   - typeOffsets:intvector (Required)
502d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   Sets of UTC raw/dst offset pairs in seconds.  Entries at
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   2n represents raw offset and 2n+1 represents dst offset
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   paired with the raw offset at 2n.  The very first pair represents
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   the initial zone offset (before the first transition) always.
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
562d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *   - trans:intvector (Optional)
572d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   List of transition times represented by 32bit seconds from the
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   epoch (1970-01-01T00:00Z) in ascending order.
602d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   - transPre32/transPost32:intvector (Optional)
622d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   List of transition times before/after 32bit minimum seconds.
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   Each time is represented by a pair of 32bit integer.
652d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   - typeMap:bin (Optional)
672d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   Array of bytes representing the mapping between each transition
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   time (transPre32/trans/transPost32) and its corresponding offset
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   data (typeOffsets).
712d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   - finalRule:string (Optional)
732d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   If a recurrent transition rule is applicable to a zone forever
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   after the final transition time, finalRule represents the rule
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   in Rules data.
772d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   - finalRaw:int (Optional)
792d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   When finalRule is available, finalRaw is required and specifies
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   the raw (base) offset of the rule.
822d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   - finalYear:int (Optional)
842d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   When finalRule is available, finalYear is required and specifies
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   the start year of the rule.
872d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   - links:intvector (Optional)
892d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   When this zone data is shared with other zones, links specifies
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   all zones including the zone itself.  Each zone is referenced by
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *   integer index.
932d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert *
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  b. Link (int, length 1).  A link zone is an int resource.  The
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  integer is the zone number of the target zone.  The key of this
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  resource is an alternate name for the target zone.  This data
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  is corresponding to Link data in the tz database.
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 2. Rules.  These have keys corresponding to the Olson rule IDs,
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * with an underscore prepended, e.g., "_EU".  Each resource describes
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the behavior of the given rule using an intvector, containing the
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * onset list, the cessation list, and the DST savings.  The onset and
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * cessation lists consist of the month, dowim, dow, time, and time
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * mode.  The end result is that the 11 integers describing the rule
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * can be passed directly into the SimpleTimeZone 13-argument
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * constructor (the other two arguments will be the raw offset, taken
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * from the complex zone element 5, and the ID string, which is not
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * used), with the times and the DST savings multiplied by 1000 to
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * scale from seconds to milliseconds.
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 3. Regions.  An array specifies mapping between zones and regions.
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Each item is either a 2-letter ISO country code or "001"
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * (UN M.49 - World).  This data is generated from "zone.tab"
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in the tz database.
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class OlsonTimeZone extends BasicTimeZone {
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Generated by serialver from JDK 1.4.1_01
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final long serialVersionUID = -6281977362477515376L;
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.TimeZone#getOffset(int, int, int, int, int, int)
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) {
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (month < Calendar.JANUARY || month > Calendar.DECEMBER) {
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("Month is not in the legal range: " +month);
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return getOffset(era, year, month, day, dayOfWeek, milliseconds, Grego.monthLength(year, month));
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * TimeZone API.
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int getOffset(int era, int year, int month,int dom, int dow, int millis, int monthLength){
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ((era != GregorianCalendar.AD && era != GregorianCalendar.BC)
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            || month < Calendar.JANUARY
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            || month > Calendar.DECEMBER
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            || dom < 1
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            || dom > monthLength
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            || dow < Calendar.SUNDAY
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            || dow > Calendar.SATURDAY
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            || millis < 0
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            || millis >= Grego.MILLIS_PER_DAY
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            || monthLength < 28
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            || monthLength > 31) {
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException();
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (era == GregorianCalendar.BC) {
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            year = -year;
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null && year >= finalStartYear) {
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return finalZone.getOffset(era, year, month, dom, dow, millis);
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Compute local epoch millis from input fields
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long time = Grego.fieldsToDay(year, month, dom) * Grego.MILLIS_PER_DAY + millis;
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] offsets = new int[2];
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        getHistoricalOffset(time, true, LOCAL_DST, LOCAL_STD, offsets);
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return offsets[0] + offsets[1];
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.TimeZone#setRawOffset(int)
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setRawOffset(int offsetMillis) {
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (isFrozen()) {
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new UnsupportedOperationException("Attempt to modify a frozen OlsonTimeZone instance.");
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (getRawOffset() == offsetMillis) {
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return;
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long current = System.currentTimeMillis();
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (current < finalStartMillis) {
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            SimpleTimeZone stz = new SimpleTimeZone(offsetMillis, getID());
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean bDst = useDaylightTime();
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (bDst) {
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeZoneRule[] currentRules = getSimpleTimeZoneRulesNear(current);
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (currentRules.length != 3) {
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // DST was observed at the beginning of this year, so useDaylightTime
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // returned true.  getSimpleTimeZoneRulesNear requires at least one
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // future transition for making a pair of rules.  This implementation
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // rolls back the time before the latest offset transition.
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    TimeZoneTransition tzt = getPreviousTransition(current, false);
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (tzt != null) {
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        currentRules = getSimpleTimeZoneRulesNear(tzt.getTime() - 1);
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (currentRules.length == 3
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && (currentRules[1] instanceof AnnualTimeZoneRule)
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && (currentRules[2] instanceof AnnualTimeZoneRule)) {
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // A pair of AnnualTimeZoneRule
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    AnnualTimeZoneRule r1 = (AnnualTimeZoneRule)currentRules[1];
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    AnnualTimeZoneRule r2 = (AnnualTimeZoneRule)currentRules[2];
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    DateTimeRule start, end;
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int offset1 = r1.getRawOffset() + r1.getDSTSavings();
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int offset2 = r2.getRawOffset() + r2.getDSTSavings();
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int sav;
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (offset1 > offset2) {
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        start = r1.getRule();
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        end = r2.getRule();
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        sav = offset1 - offset2;
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        start = r2.getRule();
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        end = r1.getRule();
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        sav = offset2 - offset1;
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // getSimpleTimeZoneRulesNear always return rules using DOW / WALL_TIME
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    stz.setStartRule(start.getRuleMonth(), start.getRuleWeekInMonth(), start.getRuleDayOfWeek(),
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            start.getRuleMillisInDay());
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    stz.setEndRule(end.getRuleMonth(), end.getRuleWeekInMonth(), end.getRuleDayOfWeek(),
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                            end.getRuleMillisInDay());
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // set DST saving amount and start year
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    stz.setDSTSavings(sav);
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // This could only happen if last rule is DST
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // and the rule used forever.  For example, Asia/Dhaka
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // in tzdata2009i stays in DST forever.
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Hack - set DST starting at midnight on Jan 1st,
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // ending 23:59:59.999 on Dec 31st
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    stz.setStartRule(0, 1, 0);
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    stz.setEndRule(11, 31, Grego.MILLIS_PER_DAY - 1);
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int[] fields = Grego.timeToFields(current, null);
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finalStartYear = fields[0];
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finalStartMillis = Grego.fieldsToDay(fields[0], 0, 1);
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (bDst) {
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // we probably do not need to set start year of final rule
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // to finalzone itself, but we always do this for now.
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                stz.setStartYear(finalStartYear);
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finalZone = stz;
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finalZone.setRawOffset(offsetMillis);
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        transitionRulesInitialized = false;
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Object clone() {
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (isFrozen()) {
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return cloneAsThawed();
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * TimeZone API.
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void getOffset(long date, boolean local, int[] offsets)  {
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null && date >= finalStartMillis) {
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finalZone.getOffset(date, local, offsets);
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            getHistoricalOffset(date, local,
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    LOCAL_FORMER, LOCAL_LATTER, offsets);
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@inheritDoc}
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void getOffsetFromLocal(long date,
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) {
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null && date >= finalStartMillis) {
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finalZone.getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            getHistoricalOffset(date, true, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.TimeZone#getRawOffset()
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int getRawOffset() {
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] ret = new int[2];
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        getOffset(System.currentTimeMillis(), false, ret);
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return ret[0];
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.TimeZone#useDaylightTime()
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean useDaylightTime() {
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // If DST was observed in 1942 (for example) but has never been
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // observed from 1943 to the present, most clients will expect
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // this method to return FALSE.  This method determines whether
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // DST is in use in the current year (at any point in the year)
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // and returns TRUE if so.
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long current = System.currentTimeMillis();
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null && current >= finalStartMillis) {
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return (finalZone != null && finalZone.useDaylightTime());
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] fields = Grego.timeToFields(current, null);
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Find start of this year, and start of next year
3202d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        long start = Grego.fieldsToDay(fields[0], 0, 1) * SECONDS_PER_DAY;
3212d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        long limit = Grego.fieldsToDay(fields[0] + 1, 0, 1) * SECONDS_PER_DAY;
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Return TRUE if DST is observed at any time during the current
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // year.
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0; i < transitionCount; ++i) {
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (transitionTimes64[i] >= limit) {
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ((transitionTimes64[i] >= start && dstOffsetAt(i) != 0)
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    || (transitionTimes64[i] > start && i > 0 && dstOffsetAt(i - 1) != 0)) {
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return true;
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return false;
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.TimeZone#observesDaylightTime()
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean observesDaylightTime() {
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long current = System.currentTimeMillis();
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null) {
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (finalZone.useDaylightTime()) {
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return true;
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (current >= finalStartMillis) {
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return false;
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Return TRUE if DST is observed at any future time
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        long currentSec = Grego.floorDivide(current, Grego.MILLIS_PER_SECOND);
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int trsIdx = transitionCount - 1;
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (dstOffsetAt(trsIdx) != 0) {
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return true;
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        while (trsIdx >= 0) {
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (transitionTimes64[trsIdx] <= currentSec) {
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (dstOffsetAt(trsIdx - 1) != 0) {
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return true;
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            trsIdx--;
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return false;
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * TimeZone API
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the amount of time to be added to local standard time
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * to get local wall clock time.
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int getDSTSavings() {
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null){
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return finalZone.getDSTSavings();
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return super.getDSTSavings();
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.TimeZone#inDaylightTime(java.util.Date)
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean inDaylightTime(Date date) {
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] temp = new int[2];
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        getOffset(date.getTime(), false, temp);
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return temp[1] != 0;
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.TimeZone#hasSameRules(com.ibm.icu.util.TimeZone)
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean hasSameRules(TimeZone other) {
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (this == other) {
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return true;
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // The super class implementation only check raw offset and
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // use of daylight saving time.
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!super.hasSameRules(other)) {
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!(other instanceof OlsonTimeZone)) {
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // We cannot reasonably compare rules in different types
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Check final zone
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        OlsonTimeZone o = (OlsonTimeZone)other;
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone == null) {
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (o.finalZone != null) {
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return false;
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (o.finalZone == null
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    || finalStartYear != o.finalStartYear
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    || !(finalZone.hasSameRules(o.finalZone))) {
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return false;
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Check transitions
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Note: The code below actually fails to compare two equivalent rules in
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // different representation properly.
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (transitionCount != o.transitionCount ||
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                !Arrays.equals(transitionTimes64, o.transitionTimes64) ||
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                typeCount != o.typeCount ||
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                !Arrays.equals(typeMapData, o.typeMapData) ||
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                !Arrays.equals(typeOffsets, o.typeOffsets)){
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return true;
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the canonical ID of this system time zone
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getCanonicalID() {
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (canonicalID == null) {
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            synchronized(this) {
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (canonicalID == null) {
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    canonicalID = getCanonicalID(getID());
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    assert(canonicalID != null);
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (canonicalID == null) {
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // This should never happen...
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        canonicalID = getID();
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return canonicalID;
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct a GMT+0 zone with no transitions.  This is done when a
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * constructor fails so the resultant object is well-behaved.
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void constructEmpty(){
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        transitionCount = 0;
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        transitionTimes64 = null;
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        typeMapData =  null;
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        typeCount = 1;
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        typeOffsets = new int[]{0,0};
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        finalZone = null;
4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        finalStartYear = Integer.MAX_VALUE;
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        finalStartMillis = Double.MAX_VALUE;
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        transitionRulesInitialized = false;
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct from a resource bundle
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param top the top-level zoneinfo resource bundle.  This is used
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * to lookup the rule that `res' may refer to, if there is one.
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param res the resource bundle of the zone to be constructed
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param id time zone ID
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public OlsonTimeZone(UResourceBundle top, UResourceBundle res, String id){
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        super(id);
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        construct(top, res);
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void construct(UResourceBundle top, UResourceBundle res){
4882d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ((top == null || res == null)) {
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException();
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if(DEBUG) System.out.println("OlsonTimeZone(" + res.getKey() +")");
4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UResourceBundle r;
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] transPre32, trans32, transPost32;
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        transPre32 = trans32 = transPost32 = null;
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        transitionCount = 0;
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Pre-32bit second transitions
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = res.get("transPre32");
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            transPre32 = r.getIntVector();
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (transPre32.length % 2 != 0) {
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // elements in the pre-32bit must be an even number
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllegalArgumentException("Invalid Format");
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            transitionCount += transPre32.length / 2;
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch (MissingResourceException e) {
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Pre-32bit transition data is optional
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 32bit second transitions
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = res.get("trans");
5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            trans32 = r.getIntVector();
5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            transitionCount += trans32.length;
5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch (MissingResourceException e) {
5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // 32bit transition data is optional
5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Post-32bit second transitions
5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = res.get("transPost32");
5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            transPost32 = r.getIntVector();
5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (transPost32.length % 2 != 0) {
5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // elements in the post-32bit must be an even number
5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllegalArgumentException("Invalid Format");
5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            transitionCount += transPost32.length / 2;
5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch (MissingResourceException e) {
5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Post-32bit transition data is optional
5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (transitionCount > 0) {
5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            transitionTimes64 = new long[transitionCount];
5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int idx = 0;
5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (transPre32 != null) {
5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; i < transPre32.length / 2; i++, idx++) {
5402d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    transitionTimes64[idx] =
5412d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        ((transPre32[i * 2]) & 0x00000000FFFFFFFFL) << 32
5422d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        | ((transPre32[i * 2 + 1]) & 0x00000000FFFFFFFFL);
5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (trans32 != null) {
5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; i < trans32.length; i++, idx++) {
5472d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    transitionTimes64[idx] = trans32[i];
5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (transPost32 != null) {
5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (int i = 0; i < transPost32.length / 2; i++, idx++) {
5522d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    transitionTimes64[idx] =
5532d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        ((transPost32[i * 2]) & 0x00000000FFFFFFFFL) << 32
5542d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        | ((transPost32[i * 2 + 1]) & 0x00000000FFFFFFFFL);
5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            transitionTimes64 = null;
5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Type offsets list must be of even size, with size >= 2
5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        r = res.get("typeOffsets");
5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        typeOffsets = r.getIntVector();
5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ((typeOffsets.length < 2 || typeOffsets.length > 0x7FFE || typeOffsets.length % 2 != 0)) {
5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("Invalid Format");
5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        typeCount = typeOffsets.length / 2;
5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Type map data must be of the same size as the transition count
5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (transitionCount > 0) {
5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = res.get("typeMap");
5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            typeMapData = r.getBinary(null);
5732d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            if (typeMapData == null || typeMapData.length != transitionCount) {
5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllegalArgumentException("Invalid Format");
5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            typeMapData = null;
5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Process final rule and data, if any
5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        finalZone = null;
5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        finalStartYear = Integer.MAX_VALUE;
5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        finalStartMillis = Double.MAX_VALUE;
5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String ruleID = null;
5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ruleID = res.getString("finalRule");
5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = res.get("finalRaw");
5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int ruleRaw = r.getInt() * Grego.MILLIS_PER_SECOND;
5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = loadRule(top, ruleID);
5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int[] ruleData = r.getIntVector();
5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ruleData == null || ruleData.length != 11) {
5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllegalArgumentException("Invalid Format");
5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finalZone = new SimpleTimeZone(ruleRaw, "",
5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ruleData[0], ruleData[1], ruleData[2],
5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ruleData[3] * Grego.MILLIS_PER_SECOND,
6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ruleData[4],
6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ruleData[5], ruleData[6], ruleData[7],
6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ruleData[8] * Grego.MILLIS_PER_SECOND,
6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ruleData[9],
6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ruleData[10] * Grego.MILLIS_PER_SECOND);
6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = res.get("finalYear");
6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finalStartYear = r.getInt();
6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Note: Setting finalStartYear to the finalZone is problematic.  When a date is around
6102d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            // year boundary, SimpleTimeZone may return false result when DST is observed at the
6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // beginning of year.  We could apply safe margin (day or two), but when one of recurrent
6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // rules falls around year boundary, it could return false result.  Without setting the
6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // start year, finalZone works fine around the year boundary of the start year.
6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // finalZone.setStartYear(finalStartYear);
6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Compute the millis for Jan 1, 0:00 GMT of the finalYear
6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Note: finalStartMillis is used for detecting either if
6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // historic transition data or finalZone to be used.  In an
6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // extreme edge case - for example, two transitions fall into
6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // small windows of time around the year boundary, this may
6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // result incorrect offset computation.  But I think it will
6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // never happen practically.  Yoshito - Feb 20, 2010
6257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finalStartMillis = Grego.fieldsToDay(finalStartYear, 0, 1) * Grego.MILLIS_PER_DAY;
6267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch (MissingResourceException e) {
6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ruleID != null) {
6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // ruleID is found, but missing other data required for
6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // creating finalZone
6307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                throw new IllegalArgumentException("Invalid Format");
6317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // This constructor is used for testing purpose only
6367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public OlsonTimeZone(String id){
6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        super(id);
6382d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        UResourceBundle top = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME,
6397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ZONEINFORES, ICUResourceBundle.ICU_DATA_CLASS_LOADER);
6407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UResourceBundle res = ZoneMeta.openOlsonResource(top, id);
6417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        construct(top, res);
6427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null){
6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finalZone.setID(id);
6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.TimeZone#setID(java.lang.String)
6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setID(String id){
6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (isFrozen()) {
6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new UnsupportedOperationException("Attempt to modify a frozen OlsonTimeZone instance.");
6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Before updating the ID, preserve the original ID's canonical ID.
6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (canonicalID == null) {
6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            canonicalID = getCanonicalID(getID());
6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            assert(canonicalID != null);
6607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (canonicalID == null) {
6617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // This should never happen...
6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                canonicalID = getID();
6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null){
6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finalZone.setID(id);
6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        super.setID(id);
6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        transitionRulesInitialized = false;
6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Maximum absolute offset in seconds = 1 day.
6747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // getHistoricalOffset uses this constant as safety margin of
6757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // quick zone transition checking.
6767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int MAX_OFFSET_SECONDS = 86400; // 60 * 60 * 24;
6777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void getHistoricalOffset(long date, boolean local,
6797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int NonExistingTimeOpt, int DuplicatedTimeOpt, int[] offsets) {
6807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (transitionCount != 0) {
6817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long sec = Grego.floorDivide(date, Grego.MILLIS_PER_SECOND);
6827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!local && sec < transitionTimes64[0]) {
6837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Before the first transition time
6847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                offsets[0] = initialRawOffset() * Grego.MILLIS_PER_SECOND;
6857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                offsets[1] = initialDstOffset() * Grego.MILLIS_PER_SECOND;
6867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
6877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Linear search from the end is the fastest approach, since
6887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // most lookups will happen at/near the end.
6897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int transIdx;
6907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (transIdx = transitionCount - 1; transIdx >= 0; transIdx--) {
6917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    long transition = transitionTimes64[transIdx];
6927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (local && (sec >= (transition - MAX_OFFSET_SECONDS))) {
6937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        int offsetBefore = zoneOffsetAt(transIdx - 1);
6947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        boolean dstBefore = dstOffsetAt(transIdx - 1) != 0;
6957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        int offsetAfter = zoneOffsetAt(transIdx);
6977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        boolean dstAfter = dstOffsetAt(transIdx) != 0;
6987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        boolean dstToStd = dstBefore && !dstAfter;
7007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        boolean stdToDst = !dstBefore && dstAfter;
7017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (offsetAfter - offsetBefore >= 0) {
7037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // Positive transition, which makes a non-existing local time range
7047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_STD && dstToStd)
7057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    || ((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_DST && stdToDst)) {
7067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                transition += offsetBefore;
7077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else if (((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_STD && stdToDst)
7087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    || ((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_DST && dstToStd)) {
7097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                transition += offsetAfter;
7107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else if ((NonExistingTimeOpt & FORMER_LATTER_MASK) == LOCAL_LATTER) {
7117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                transition += offsetBefore;
7127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else {
7137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // Interprets the time with rule before the transition,
7147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // default for non-existing time range
7157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                transition += offsetAfter;
7167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
7177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        } else {
7187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            // Negative transition, which makes a duplicated local time range
7197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_STD && dstToStd)
7207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    || ((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_DST && stdToDst)) {
7217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                transition += offsetAfter;
7227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else if (((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_STD && stdToDst)
7237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    || ((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_DST && dstToStd)) {
7247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                transition += offsetBefore;
7257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else if ((DuplicatedTimeOpt & FORMER_LATTER_MASK) == LOCAL_FORMER) {
7267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                transition += offsetBefore;
7277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            } else {
7287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // Interprets the time with rule after the transition,
7297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // default for duplicated local time range
7307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                transition += offsetAfter;
7317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
7327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
7337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
7347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (sec >= transition) {
7357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        break;
7367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
7377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
7387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // transIdx could be -1 when local=true
7397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                offsets[0] = rawOffsetAt(transIdx) * Grego.MILLIS_PER_SECOND;
7407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                offsets[1] = dstOffsetAt(transIdx) * Grego.MILLIS_PER_SECOND;
7417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
7437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // No transitions, single pair of offsets only
7447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            offsets[0] = initialRawOffset() * Grego.MILLIS_PER_SECOND;
7457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            offsets[1] = initialDstOffset() * Grego.MILLIS_PER_SECOND;
7467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int getInt(byte val){
7502d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert        return val & 0xFF;
7517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
7547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Following 3 methods return an offset at the given transition time index.
7557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * When the index is negative, return the initial offset.
7567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int zoneOffsetAt(int transIdx) {
7587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int typeIdx = transIdx >= 0 ? getInt(typeMapData[transIdx]) * 2 : 0;
7597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return typeOffsets[typeIdx] + typeOffsets[typeIdx + 1];
7607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int rawOffsetAt(int transIdx) {
7637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int typeIdx = transIdx >= 0 ? getInt(typeMapData[transIdx]) * 2 : 0;
7647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return typeOffsets[typeIdx];
7657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int dstOffsetAt(int transIdx) {
7687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int typeIdx = transIdx >= 0 ? getInt(typeMapData[transIdx]) * 2 : 0;
7697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return typeOffsets[typeIdx + 1];
7707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int initialRawOffset() {
7737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return typeOffsets[0];
7747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int initialDstOffset() {
7777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return typeOffsets[1];
7787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // temp
7817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
7827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String toString() {
7837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        StringBuilder buf = new StringBuilder();
7847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append(super.toString());
7857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append('[');
7867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append("transitionCount=" + transitionCount);
7877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append(",typeCount=" + typeCount);
7887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append(",transitionTimes=");
7897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (transitionTimes64 != null) {
7907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append('[');
7917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < transitionTimes64.length; ++i) {
7927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (i > 0) {
7937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append(',');
7947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
7957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(Long.toString(transitionTimes64[i]));
7967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(']');
7987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
7997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append("null");
8007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append(",typeOffsets=");
8027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (typeOffsets != null) {
8037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append('[');
8047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < typeOffsets.length; ++i) {
8057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (i > 0) {
8067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append(',');
8077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
8087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(Integer.toString(typeOffsets[i]));
8097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
8107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(']');
8117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
8127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append("null");
8137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append(",typeMapData=");
8157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (typeMapData != null) {
8167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append('[');
8177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < typeMapData.length; ++i) {
8187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (i > 0) {
8197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append(',');
8207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
8217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(Byte.toString(typeMapData[i]));
8227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
8237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
8247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append("null");
8257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append(",finalStartYear=" + finalStartYear);
8277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append(",finalStartMillis=" + finalStartMillis);
8287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append(",finalZone=" + finalZone);
8297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        buf.append(']');
8302d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
8317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return buf.toString();
8327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Number of transitions, 0..~370
8367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int transitionCount;
8387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Number of types, 1..255
8417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int typeCount;
8437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Time of each transition in seconds from 1970 epoch.
8467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private long[] transitionTimes64;
8487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Offset from GMT in seconds for each type.
8517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Length is equal to typeCount
8527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int[] typeOffsets;
8547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Type description data, consisting of transitionCount uint8_t
8577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * type indices (from 0..typeCount-1).
8587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Length is equal to transitionCount
8597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private byte[] typeMapData;
8617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For year >= finalStartYear, the finalZone will be used.
8647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int finalStartYear = Integer.MAX_VALUE;
8667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For date >= finalStartMillis, the finalZone will be used.
8697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private double finalStartMillis = Double.MAX_VALUE;
8717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * A SimpleTimeZone that governs the behavior for years >= finalYear.
8747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If and only if finalYear == INT32_MAX then finalZone == 0.
8757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private SimpleTimeZone finalZone = null; // owned, may be NULL
8772d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
8787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The canonical ID of this zone. Initialized when {@link #getCanonicalID()}
8807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * is invoked first time, or {@link #setID(String)} is called.
8817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private volatile String canonicalID = null;
8837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String ZONEINFORES = "zoneinfo64";
8857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final boolean DEBUG = ICUDebug.enabled("olson");
8877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int SECONDS_PER_DAY = 24*60*60;
8882d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
8897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static UResourceBundle loadRule(UResourceBundle top, String ruleid) {
8907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UResourceBundle r = top.get("Rules");
8917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        r = r.get(ruleid);
8927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return r;
8937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
8967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean equals(Object obj){
8977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (!super.equals(obj)) return false; // super does class check
8987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        OlsonTimeZone z = (OlsonTimeZone) obj;
9007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return (Utility.arrayEquals(typeMapData, z.typeMapData) ||
9027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 // If the pointers are not equal, the zones may still
9037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 // be equal if their rules and transitions are equal
9047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 (finalStartYear == z.finalStartYear &&
9057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                  // Don't compare finalMillis; if finalYear is ==, so is finalMillis
9067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                  ((finalZone == null && z.finalZone == null) ||
9077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                   (finalZone != null && z.finalZone != null &&
9087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    finalZone.equals(z.finalZone)) &&
9097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                  transitionCount == z.transitionCount &&
9107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                  typeCount == z.typeCount &&
9117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                  Utility.arrayEquals(transitionTimes64, z.transitionTimes64) &&
9127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                  Utility.arrayEquals(typeOffsets, z.typeOffsets) &&
9137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                  Utility.arrayEquals(typeMapData, z.typeMapData)
9147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                  )));
9157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
9197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int hashCode(){
9207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int ret =   (int)  (finalStartYear ^ (finalStartYear>>>4) +
9217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                   transitionCount ^ (transitionCount>>>6) +
9222d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                   typeCount ^ (typeCount>>>8) +
9237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                   Double.doubleToLongBits(finalStartMillis)+
9242d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                   (finalZone == null ? 0 : finalZone.hashCode()) +
9257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                   super.hashCode());
9267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (transitionTimes64 != null) {
9277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for(int i=0; i<transitionTimes64.length; i++){
9287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ret+=transitionTimes64[i]^(transitionTimes64[i]>>>8);
9297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for(int i=0; i<typeOffsets.length; i++){
9327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ret+=typeOffsets[i]^(typeOffsets[i]>>>8);
9337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (typeMapData != null) {
9357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for(int i=0; i<typeMapData.length; i++){
9367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ret+=typeMapData[i] & 0xFF;
9372d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert            }
9387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return ret;
9407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //
9437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // BasicTimeZone methods
9447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //
9457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
9477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.BasicTimeZone#getNextTransition(long, boolean)
9487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
9507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public TimeZoneTransition getNextTransition(long base, boolean inclusive) {
9517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initTransitionRules();
9527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null) {
9547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (inclusive && base == firstFinalTZTransition.getTime()) {
9557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return firstFinalTZTransition;
9567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (base >= firstFinalTZTransition.getTime()) {
9577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (finalZone.useDaylightTime()) {
9587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //return finalZone.getNextTransition(base, inclusive);
9597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return finalZoneWithStartYear.getNextTransition(base, inclusive);
9607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
9617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // No more transitions
9627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return null;
9637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
9647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (historicRules != null) {
9677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Find a historical transition
9687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int ttidx = transitionCount - 1;
9697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (; ttidx >= firstTZTransitionIdx; ttidx--) {
9707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long t = transitionTimes64[ttidx] * Grego.MILLIS_PER_SECOND;
9717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (base > t || (!inclusive && base == t)) {
9727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
9737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
9747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ttidx == transitionCount - 1)  {
9767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return firstFinalTZTransition;
9777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (ttidx < firstTZTransitionIdx) {
9787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return firstTZTransition;
9797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
9807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Create a TimeZoneTransition
9817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeZoneRule to = historicRules[getInt(typeMapData[ttidx + 1])];
9827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeZoneRule from = historicRules[getInt(typeMapData[ttidx])];
9837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long startTime = transitionTimes64[ttidx+1] * Grego.MILLIS_PER_SECOND;
9847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // The transitions loaded from zoneinfo.res may contain non-transition data
9867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (from.getName().equals(to.getName()) && from.getRawOffset() == to.getRawOffset()
9877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && from.getDSTSavings() == to.getDSTSavings()) {
9887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return getNextTransition(startTime, false);
9897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
9907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return new TimeZoneTransition(startTime, from, to);
9927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return null;
9957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
9987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.BasicTimeZone#getPreviousTransition(long, boolean)
9997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
10017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public TimeZoneTransition getPreviousTransition(long base, boolean inclusive) {
10027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initTransitionRules();
10037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null) {
10057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (inclusive && base == firstFinalTZTransition.getTime()) {
10067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return firstFinalTZTransition;
10077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (base > firstFinalTZTransition.getTime()) {
10087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (finalZone.useDaylightTime()) {
10097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    //return finalZone.getPreviousTransition(base, inclusive);
10107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return finalZoneWithStartYear.getPreviousTransition(base, inclusive);
10117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
10127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return firstFinalTZTransition;
10132d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                }
10147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (historicRules != null) {
10187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Find a historical transition
10197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int ttidx = transitionCount - 1;
10207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (; ttidx >= firstTZTransitionIdx; ttidx--) {
10217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long t = transitionTimes64[ttidx] * Grego.MILLIS_PER_SECOND;
10227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (base > t || (inclusive && base == t)) {
10237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
10247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
10257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ttidx < firstTZTransitionIdx) {
10277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // No more transitions
10287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return null;
10297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if (ttidx == firstTZTransitionIdx) {
10307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return firstTZTransition;
10317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
10327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Create a TimeZoneTransition
10337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeZoneRule to = historicRules[getInt(typeMapData[ttidx])];
10347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeZoneRule from = historicRules[getInt(typeMapData[ttidx-1])];
10357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long startTime = transitionTimes64[ttidx] * Grego.MILLIS_PER_SECOND;
10367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // The transitions loaded from zoneinfo.res may contain non-transition data
10387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (from.getName().equals(to.getName()) && from.getRawOffset() == to.getRawOffset()
10397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        && from.getDSTSavings() == to.getDSTSavings()) {
10407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return getPreviousTransition(startTime, false);
10417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
10427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return new TimeZoneTransition(startTime, from, to);
10447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return null;
10477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
10507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.BasicTimeZone#getTimeZoneRules()
10517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Override
10537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public TimeZoneRule[] getTimeZoneRules() {
10547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initTransitionRules();
10557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int size = 1;
10567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (historicRules != null) {
10577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // historicRules may contain null entries when original zoneinfo data
10587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // includes non transition data.
10597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < historicRules.length; i++) {
10607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (historicRules[i] != null) {
10617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    size++;
10627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
10637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null) {
10667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (finalZone.useDaylightTime()) {
10677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                size += 2;
10687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
10697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                size++;
10707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TimeZoneRule[] rules = new TimeZoneRule[size];
10747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int idx = 0;
10757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        rules[idx++] = initialRule;
10767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (historicRules != null) {
10787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (int i = 0; i < historicRules.length; i++) {
10797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (historicRules[i] != null) {
10807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    rules[idx++] = historicRules[i];
10817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
10827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         }
10847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null) {
10867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (finalZone.useDaylightTime()) {
10877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeZoneRule[] stzr = finalZoneWithStartYear.getTimeZoneRules();
10887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Adding only transition rules
10897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                rules[idx++] = stzr[1];
10907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                rules[idx++] = stzr[2];
10917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
10927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Create a TimeArrayTimeZoneRule at finalMillis
10937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                rules[idx++] = new TimeArrayTimeZoneRule(getID() + "(STD)", finalZone.getRawOffset(), 0,
10942d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                        new long[] {(long)finalStartMillis}, DateTimeRule.UTC_TIME);
10957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return rules;
10987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient InitialTimeZoneRule initialRule;
11017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient TimeZoneTransition firstTZTransition;
11027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient int firstTZTransitionIdx;
11037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient TimeZoneTransition firstFinalTZTransition;
11047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient TimeArrayTimeZoneRule[] historicRules;
11057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient SimpleTimeZone finalZoneWithStartYear; // hack
11067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient boolean transitionRulesInitialized;
11087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private synchronized void initTransitionRules() {
11107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (transitionRulesInitialized) {
11117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return;
11127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initialRule = null;
11157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        firstTZTransition = null;
11167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        firstFinalTZTransition = null;
11177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        historicRules = null;
11187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        firstTZTransitionIdx = 0;
11197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        finalZoneWithStartYear = null;
11207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String stdName = getID() + "(STD)";
11227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String dstName = getID() + "(DST)";
11237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int raw, dst;
11257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Create initial rule
11277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        raw = initialRawOffset() * Grego.MILLIS_PER_SECOND;
11287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        dst = initialDstOffset() * Grego.MILLIS_PER_SECOND;
11297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initialRule = new InitialTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst);
11307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (transitionCount > 0) {
11327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int transitionIdx, typeIdx;
11337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // We probably no longer need to check the first "real" transition
11357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // here, because the new tzcode remove such transitions already.
11367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // For now, keeping this code for just in case. Feb 19, 2010 Yoshito
11377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (transitionIdx = 0; transitionIdx < transitionCount; transitionIdx++) {
11387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (getInt(typeMapData[transitionIdx]) != 0) { // type 0 is the initial type
11397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
11407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
11417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                firstTZTransitionIdx++;
11427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (transitionIdx == transitionCount) {
11447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Actually no transitions...
11457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
11467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Build historic rule array
11477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                long[] times = new long[transitionCount];
11487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for (typeIdx = 0; typeIdx < typeCount; typeIdx++) {
11497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Gather all start times for each pair of offsets
11507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int nTimes = 0;
11517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for (transitionIdx = firstTZTransitionIdx; transitionIdx < transitionCount; transitionIdx++) {
11527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (typeIdx == getInt(typeMapData[transitionIdx])) {
11537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            long tt = transitionTimes64[transitionIdx] * Grego.MILLIS_PER_SECOND;
11547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            if (tt < finalStartMillis) {
11557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                // Exclude transitions after finalMillis
11567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                times[nTimes++] = tt;
11577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            }
11587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
11597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
11607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (nTimes > 0) {
11617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        long[] startTimes = new long[nTimes];
11627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        System.arraycopy(times, 0, startTimes, 0, nTimes);
11637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        // Create a TimeArrayTimeZoneRule
11647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        raw = typeOffsets[typeIdx*2]*Grego.MILLIS_PER_SECOND;
11657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        dst = typeOffsets[typeIdx*2 + 1]*Grego.MILLIS_PER_SECOND;
11667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if (historicRules == null) {
11677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            historicRules = new TimeArrayTimeZoneRule[typeCount];
11687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
11697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        historicRules[typeIdx] = new TimeArrayTimeZoneRule((dst == 0 ? stdName : dstName),
11707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                raw, dst, startTimes, DateTimeRule.UTC_TIME);
11717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
11727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
11737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Create initial transition
11757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                typeIdx = getInt(typeMapData[firstTZTransitionIdx]);
11767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                firstTZTransition = new TimeZoneTransition(transitionTimes64[firstTZTransitionIdx] * Grego.MILLIS_PER_SECOND,
11777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        initialRule, historicRules[typeIdx]);
11782d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert
11797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
11807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
11817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null) {
11837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Get the first occurrence of final rule starts
11847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            long startTime = (long)finalStartMillis;
11857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TimeZoneRule firstFinalRule;
11867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (finalZone.useDaylightTime()) {
11877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                /*
11887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * Note: When an OlsonTimeZone is constructed, we should set the final year
11897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * as the start year of finalZone.  However, the boundary condition used for
11907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * getting offset from finalZone has some problems.  So setting the start year
11917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * in the finalZone will cause a problem.  For now, we do not set the valid
11927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * start year when the construction time and create a clone and set the
11937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 * start year when extracting rules.
11947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 */
11957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                finalZoneWithStartYear = (SimpleTimeZone)finalZone.clone();
11967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                finalZoneWithStartYear.setStartYear(finalStartYear);
11977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
11987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                TimeZoneTransition tzt = finalZoneWithStartYear.getNextTransition(startTime, false);
11997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                firstFinalRule  = tzt.getTo();
12007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                startTime = tzt.getTime();
12017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
12027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                finalZoneWithStartYear = finalZone;
12037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                firstFinalRule = new TimeArrayTimeZoneRule(finalZone.getID(),
12047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        finalZone.getRawOffset(), 0, new long[] {startTime}, DateTimeRule.UTC_TIME);
12057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            TimeZoneRule prevRule = null;
12077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (transitionCount > 0) {
12087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                prevRule = historicRules[getInt(typeMapData[transitionCount - 1])];
12097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (prevRule == null) {
12117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // No historic transitions, but only finalZone available
12127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                prevRule = initialRule;
12137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            firstFinalTZTransition = new TimeZoneTransition(startTime, prevRule, firstFinalRule);
12157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        transitionRulesInitialized = true;
12187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
12197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Note: This class does not support back level serialization compatibility
12217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // very well.  ICU 4.4 introduced the 64bit transition data.  It is probably
12227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // possible to implement this class to make old version of ICU to deserialize
12237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // object stream serialized by ICU 4.4+.  However, such implementation will
12247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // introduce unnecessary complexity other than serialization support.
12257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // I decided to provide minimum level of backward compatibility, which
12267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // only support ICU 4.4+ to create an instance of OlsonTimeZone by reloading
12277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // the zone rules from bundles.  ICU 4.2 or older version of ICU cannot
12287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // deserialize object stream created by ICU 4.4+.  Yoshito -Feb 22, 2010
12297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int currentSerialVersion = 1;
12317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int serialVersionOnStream = currentSerialVersion;
12327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
12347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        stream.defaultReadObject();
12357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (serialVersionOnStream < 1) {
12377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // No version - 4.2 or older
12387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Just reloading the rule from bundle
12397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            boolean initialized = false;
12407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String tzid = getID();
12417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (tzid != null) {
12427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
12432d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                    UResourceBundle top = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME,
12447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            ZONEINFORES, ICUResourceBundle.ICU_DATA_CLASS_LOADER);
12457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    UResourceBundle res = ZoneMeta.openOlsonResource(top, tzid);
12467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    construct(top, res);
12477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (finalZone != null){
12487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        finalZone.setID(tzid);
12497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
12507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    initialized = true;
12512d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert                } catch (Exception ignored) {
12527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // throw away
12537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
12547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!initialized) {
12567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // final resort
12577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                constructEmpty();
12587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
12597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // need to rebuild transition rules when requested
12627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        transitionRulesInitialized = false;
12637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
12647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Freezable stuffs
12667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient volatile boolean isFrozen = false;
12677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
12697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.TimeZone#isFrozen()
12707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
12712d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Override
12727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean isFrozen() {
12737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return isFrozen;
12747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
12757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
12777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.TimeZone#freeze()
12787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
12792d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Override
12807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public TimeZone freeze() {
12817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        isFrozen = true;
12827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return this;
12837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
12847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* (non-Javadoc)
12867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @see com.ibm.icu.util.TimeZone#cloneAsThawed()
12877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
12882d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert    @Override
12897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public TimeZone cloneAsThawed() {
12907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        OlsonTimeZone tz = (OlsonTimeZone)super.cloneAsThawed();
12917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (finalZone != null) {
12927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // TODO Do we really need this?
12937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            finalZone.setID(getID());
12947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            tz.finalZone = (SimpleTimeZone) finalZone.clone();
12957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
12967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
12977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Following data are read-only and never changed.
12987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Therefore, shallow copies should be sufficient.
12997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        //
13007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // transitionTimes64
13017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // typeMapData
13027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // typeOffsets
13037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
13047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        tz.isFrozen = false;
13057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return tz;
13067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
13077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
1308