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;
}
}