jdk/test/sun/util/calendar/zi/Timezone.java
author sherman
Tue, 12 Feb 2013 09:25:43 -0800
changeset 15658 55b829ca2334
child 23010 6dadb192ad81
permissions -rw-r--r--
8007392: JSR 310: DateTime API Updates 8007520: Update date/time classes in j.util and j.sql packages 8007572: Replace existing jdk timezone data at <java.home>/lib/zi with JSR310's tzdb Summary: Integration of JSR310 Date/Time API for M7 Reviewed-by: darcy, alanb, naoto Contributed-by: scolebourne@joda.org, roger.riggs@oracle.com, masayoshi.okutsu@oracle.com, patrick.zhang@oracle.com

/*
 * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

import java.util.ArrayList;
import java.util.List;

/**
 * Timezone represents all information of a single point of time to
 * generate its time zone database.
 *
 * @since 1.4
 */
class Timezone {
    /**
     * zone name of this time zone
     */
    private String name;

    /**
     * transition time values in UTC (millisecond)
     */
    private List<Long> transitions;

    /**
     * All offset values in millisecond
     * @see sun.util.calendar.ZoneInfo
     */
    private List<Integer> offsets;

    /**
     * Indices of GMT offset values (both raw and raw+saving)
     * at transitions
     */
    private List<Integer> gmtOffsets;

    /**
     * Indices of regular or "direct" saving time values
     * at transitions
     */
    private List<Integer> dstOffsets;

    /**
     * Zone records of this time zone
     */
    private List<ZoneRec> usedZoneRecs;

    /**
     * Rule records referred to by this time zone
     */
    private List<RuleRec> usedRuleRecs;

    /**
     * Type of DST rules in this time zone
     */
    private int dstType;
    static final int UNDEF_DST = 0;     // DST type not set yet
    static final int NO_DST = 1;        // never observed DST
    static final int LAST_DST = 2;      // last rule ends in DST (all year round DST-only)
    static final int X_DST = 3;         // used to observe DST
    static final int DST = 4;           // observing DST regularly

    /**
     * Raw GMT offset of this time zone in the last rule
     */
    private int rawOffset;

    /**
     * The CRC32 value of the transitions data
     */
    private int crc32;

    /**
     * The last ZoneRec
     */
    private ZoneRec lastZoneRec;

    /**
     * The last DST rules. lastRules[0] is the DST start
     * rule. lastRules[1] is the DST end rules.
     */
    private List<RuleRec> lastRules;

    /**
     * The amount of DST saving value (millisecond) in the last DST
     * rule.
     */
    private int lastSaving;

    /**
     * true if the raw offset will change in the future time.
     */
    private boolean willRawOffsetChange = false;


    /**
     * Constracts a Timezone object with the given zone name.
     * @param name the zone name
     */
    Timezone(String name) {
        this.name = name;
    }

    /**
     * @return the number of transitions
     */
    int getNTransitions() {
        if (transitions == null) {
            return 0;
        }
        return transitions.size();
    }

    /**
     * @return the zone name
     */
    String getName() {
        return name;
    }

    /**
     * Returns the list of all rule records that have been referred to
     * by this time zone.
     * @return the rule records list
     */
    List<RuleRec> getRules() {
        return usedRuleRecs;
    }

    /**
     * Returns the list of all zone records that have been referred to
     * by this time zone.
     * @return the zone records list
     */
    List<ZoneRec> getZones() {
        return usedZoneRecs;
    }

    /**
     * @return the transition table (list)
     */
    List<Long> getTransitions() {
        return transitions;
    }

    /**
     * @return the offsets list
     */
    List<Integer> getOffsets() {
        return offsets;
    }

    /**
     * @return the DST saving offsets list
     */
    List<Integer> getDstOffsets() {
        return dstOffsets;
    }

    /**
     * @return the GMT offsets list
     */
    List<Integer> getGmtOffsets() {
        return gmtOffsets;
    }

    /**
     * @return the checksum (crc32) value of the trasition table
     */
    int getCRC32() {
        return crc32;
    }

    /**
     * @return true if the GMT offset of this time zone would change
     * after the time zone database has been generated, false, otherwise.
     */
    boolean willGMTOffsetChange() {
        return willRawOffsetChange;
    }

    /**
     * @return the last known GMT offset value in milliseconds
     */
    int getRawOffset() {
        return rawOffset;
    }

    /**
     * Sets time zone's GMT offset to <code>offset</code>.
     * @param offset the GMT offset value in milliseconds
     */
    void setRawOffset(int offset) {
        rawOffset = offset;
    }

    /**
     * Sets time zone's GMT offset value to <code>offset</code>. If
     * <code>startTime</code> is future time, then the {@link
     * #willRawOffsetChange} value is set to true.
     * @param offset the GMT offset value in milliseconds
     * @param startTime the UTC time at which the GMT offset is in effective
     */
    void setRawOffset(int offset, long startTime) {
        // if this rawOffset is for the future time, let the run-time
        // look for the current GMT offset.
        if (startTime > Time.getCurrentTime()) {
            willRawOffsetChange = true;
        }
        setRawOffset(offset);
    }

    /**
     * Adds the specified transition information to the end of the transition table.
     * @param time the UTC time at which this transition happens
     * @param offset the total amount of the offset from GMT in milliseconds
     * @param dstOffset the amount of time in milliseconds saved at this transition
     */
    void addTransition(long time, int offset, int dstOffset) {
        if (transitions == null) {
            transitions = new ArrayList<Long>();
            offsets = new ArrayList<Integer>();
            dstOffsets = new ArrayList<Integer>();
        }
        transitions.add(time);
        offsets.add(offset);
        dstOffsets.add(dstOffset);
    }

    /**
     * Sets the type of historical daylight saving time
     * observation. For example, China used to observed daylight
     * saving time, but it no longer does. Then, X_DST is set to the
     * China time zone.
     * @param type the type of daylight saving time
     */
    void setDSTType(int type) {
        dstType = type;
    }

    /**
     * @return the type of historical daylight saving time
     * observation.
     */
    int getDSTType() {
        return dstType;
    }

    /**
     * Adds the specified zone record to the zone records list.
     * @param rec the zone record
     */
    void addUsedRec(ZoneRec rec) {
        if (usedZoneRecs == null) {
            usedZoneRecs = new ArrayList<ZoneRec>();
        }
        usedZoneRecs.add(rec);
    }

    /**
     * Adds the specified rule record to the rule records list.
     * @param rec the rule record
     */
    void addUsedRec(RuleRec rec) {
        if (usedRuleRecs == null) {
            usedRuleRecs = new ArrayList<RuleRec>();
        }
        // if the last used rec is the same as the given rec, avoid
        // putting the same rule.
        int n = usedRuleRecs.size();
        for (int i = 0; i < n; i++) {
            if (usedRuleRecs.get(i).equals(rec)) {
                return;
            }
        }
        usedRuleRecs.add(rec);
    }

    /**
     * Sets the last zone record for this time zone.
     * @param the last zone record
     */
    void setLastZoneRec(ZoneRec zrec) {
        lastZoneRec = zrec;
    }

    /**
     * @return the last zone record for this time zone.
     */
    ZoneRec getLastZoneRec() {
        return lastZoneRec;
    }

    /**
     * Sets the last rule records for this time zone. Those are used
     * for generating SimpleTimeZone parameters.
     * @param rules the last rule records
     */
    void setLastRules(List<RuleRec> rules) {
        int n = rules.size();
        if (n > 0) {
            lastRules = rules;
            RuleRec rec = rules.get(0);
            int offset = rec.getSave();
            if (offset > 0) {
                setLastDSTSaving(offset);
            } else {
                System.err.println("\t    No DST starting rule in the last rules.");
            }
        }
    }

    /**
     * @return the last rule records for this time zone.
     */
    List<RuleRec> getLastRules() {
        return lastRules;
    }

    /**
     * Sets the last daylight saving amount.
     * @param the daylight saving amount
     */
    void setLastDSTSaving(int offset) {
        lastSaving = offset;
    }

    /**
     * @return the last daylight saving amount.
     */
    int getLastDSTSaving() {
        return lastSaving;
    }

    /**
     * Calculates the CRC32 value from the transition table and sets
     * the value to <code>crc32</code>.
     */
    void checksum() {
        if (transitions == null) {
            crc32 = 0;
            return;
        }
        Checksum sum = new Checksum();
        for (int i = 0; i < transitions.size(); i++) {
            int offset = offsets.get(i);
            // adjust back to make the transition in local time
            sum.update(transitions.get(i) + offset);
            sum.update(offset);
            sum.update(dstOffsets.get(i));
        }
        crc32 = (int)sum.getValue();
    }

    /**
     * Removes unnecessary transitions for Java time zone support.
     */
    void optimize() {
        // if there is only one offset, delete all transitions. This
        // could happen if only time zone abbreviations changed.
        if (gmtOffsets.size() == 1) {
            transitions = null;
            usedRuleRecs =  null;
            setDSTType(NO_DST);
            return;
        }
        for (int i = 0; i < (transitions.size() - 2); i++) { // don't remove the last one
            if (transitions.get(i) == transitions.get(i+1)) {
                transitions.remove(i);
                offsets.remove(i);
                dstOffsets.remove(i);
                i--;
            }
        }

        for (int i = 0; i < (transitions.size() - 2); i++) { // don't remove the last one
            if (offsets.get(i) == offsets.get(i+1)
                && dstOffsets.get(i) == dstOffsets.get(i+1)) {
                transitions.remove(i+1);
                offsets.remove(i+1);
                dstOffsets.remove(i+1);
                i--;
            }
        }
    }

    /**
     * Stores the specified offset value from GMT in the GMT offsets
     * table and returns its index. The offset value includes the base
     * GMT offset and any additional daylight saving if applicable. If
     * the same value as the specified offset is already in the table,
     * its index is returned.
     * @param offset the offset value in milliseconds
     * @return the index to the offset value in the GMT offsets table.
     */
    int getOffsetIndex(int offset) {
        return getOffsetIndex(offset, 0);
    }

    /**
     * Stores the specified daylight saving value in the GMT offsets
     * table and returns its index. If the same value as the specified
     * offset is already in the table, its index is returned. If 0 is
     * specified, it's not stored in the table and -1 is returned.
     * @param offset the offset value in milliseconds
     * @return the index to the specified offset value in the GMT
     * offsets table, or -1 if 0 is specified.
     */
    int getDstOffsetIndex(int offset) {
        if (offset == 0) {
            return -1;
        }
        return getOffsetIndex(offset, 1);
    }

    private int getOffsetIndex(int offset, int index) {
        if (gmtOffsets == null) {
            gmtOffsets = new ArrayList<Integer>();
        }
        for (int i = index; i < gmtOffsets.size(); i++) {
            if (offset == gmtOffsets.get(i)) {
                return i;
            }
        }
        if (gmtOffsets.size() < index) {
            gmtOffsets.add(0);
        }
        gmtOffsets.add(offset);
        return gmtOffsets.size() - 1;
    }
}