/* * Copyright (c) 2000, 2013, 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; } }