Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/util/calendar/ZoneInfo.java
38918 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.util.calendar;2627import java.io.IOException;28import java.io.ObjectInputStream;29import java.lang.ref.SoftReference;30import java.security.AccessController;31import java.util.ArrayList;32import java.util.Arrays;33import java.util.Date;34import java.util.List;35import java.util.Locale;36import java.util.Map;37import java.util.Set;38import java.util.SimpleTimeZone;39import java.util.TimeZone;4041/**42* <code>ZoneInfo</code> is an implementation subclass of {@link43* java.util.TimeZone TimeZone} that represents GMT offsets and44* daylight saving time transitions of a time zone.45* <p>46* The daylight saving time transitions are described in the {@link47* #transitions transitions} table consisting of a chronological48* sequence of transitions of GMT offset and/or daylight saving time49* changes. Since all transitions are represented in UTC, in theory,50* <code>ZoneInfo</code> can be used with any calendar systems except51* for the {@link #getOffset(int,int,int,int,int,int) getOffset}52* method that takes Gregorian calendar date fields.53* <p>54* This table covers transitions from 1900 until 2037 (as of version55* 1.4), Before 1900, it assumes that there was no daylight saving56* time and the <code>getOffset</code> methods always return the57* {@link #getRawOffset} value. No Local Mean Time is supported. If a58* specified date is beyond the transition table and this time zone is59* supposed to observe daylight saving time in 2037, it delegates60* operations to a {@link java.util.SimpleTimeZone SimpleTimeZone}61* object created using the daylight saving time schedule as of 2037.62* <p>63* The date items, transitions, GMT offset(s), etc. are read from a database64* file. See {@link ZoneInfoFile} for details.65* @see java.util.SimpleTimeZone66* @since 1.467*/6869public class ZoneInfo extends TimeZone {7071private static final int UTC_TIME = 0;72private static final int STANDARD_TIME = 1;73private static final int WALL_TIME = 2;7475private static final long OFFSET_MASK = 0x0fL;76private static final long DST_MASK = 0xf0L;77private static final int DST_NSHIFT = 4;78// this bit field is reserved for abbreviation support79private static final long ABBR_MASK = 0xf00L;80private static final int TRANSITION_NSHIFT = 12;8182private static final CalendarSystem gcal = CalendarSystem.getGregorianCalendar();8384/**85* The raw GMT offset in milliseconds between this zone and GMT.86* Negative offsets are to the west of Greenwich. To obtain local87* <em>standard</em> time, add the offset to GMT time.88* @serial89*/90private int rawOffset;9192/**93* Difference in milliseconds from the original GMT offset in case94* the raw offset value has been modified by calling {@link95* #setRawOffset}. The initial value is 0.96* @serial97*/98private int rawOffsetDiff = 0;99100/**101* A CRC32 value of all pairs of transition time (in milliseconds102* in <code>long</code>) in local time and its GMT offset (in103* seconds in <code>int</code>) in the chronological order. Byte104* values of each <code>long</code> and <code>int</code> are taken105* in the big endian order (i.e., MSB to LSB).106* @serial107*/108private int checksum;109110/**111* The amount of time in milliseconds saved during daylight saving112* time. If <code>useDaylight</code> is false, this value is 0.113* @serial114*/115private int dstSavings;116117/**118* This array describes transitions of GMT offsets of this time119* zone, including both raw offset changes and daylight saving120* time changes.121* A long integer consists of four bit fields.122* <ul>123* <li>The most significant 52-bit field represents transition124* time in milliseconds from Gregorian January 1 1970, 00:00:00125* GMT.</li>126* <li>The next 4-bit field is reserved and must be 0.</li>127* <li>The next 4-bit field is an index value to {@link #offsets128* offsets[]} for the amount of daylight saving at the129* transition. If this value is zero, it means that no daylight130* saving, not the index value zero.</li>131* <li>The least significant 4-bit field is an index value to132* {@link #offsets offsets[]} for <em>total</em> GMT offset at the133* transition.</li>134* </ul>135* If this time zone doesn't observe daylight saving time and has136* never changed any GMT offsets in the past, this value is null.137* @serial138*/139private long[] transitions;140141/**142* This array holds all unique offset values in143* milliseconds. Index values to this array are stored in the144* transitions array elements.145* @serial146*/147private int[] offsets;148149/**150* SimpleTimeZone parameter values. It has to have either 8 for151* {@link java.util.SimpleTimeZone#SimpleTimeZone(int, String,152* int, int , int , int , int , int , int , int , int) the153* 11-argument SimpleTimeZone constructor} or 10 for {@link154* java.util.SimpleTimeZone#SimpleTimeZone(int, String, int, int,155* int , int , int , int , int , int , int, int, int) the156* 13-argument SimpleTimeZone constructor} parameters.157* @serial158*/159private int[] simpleTimeZoneParams;160161/**162* True if the raw GMT offset value would change after the time163* zone data has been generated; false, otherwise. The default164* value is false.165* @serial166*/167private boolean willGMTOffsetChange = false;168169/**170* True if the object has been modified after its instantiation.171*/172transient private boolean dirty = false;173174private static final long serialVersionUID = 2653134537216586139L;175176/**177* A constructor.178*/179public ZoneInfo() {180}181182/**183* A Constructor for CustomID.184*/185public ZoneInfo(String ID, int rawOffset) {186this(ID, rawOffset, 0, 0, null, null, null, false);187}188189/**190* Constructs a ZoneInfo instance.191*192* @param ID time zone name193* @param rawOffset GMT offset in milliseconds194* @param dstSavings daylight saving value in milliseconds or 0195* (zero) if this time zone doesn't observe Daylight Saving Time.196* @param checksum CRC32 value with all transitions table entry197* values198* @param transitions transition table199* @param offsets offset value table200* @param simpleTimeZoneParams parameter values for constructing201* SimpleTimeZone202* @param willGMTOffsetChange the value of willGMTOffsetChange203*/204ZoneInfo(String ID,205int rawOffset,206int dstSavings,207int checksum,208long[] transitions,209int[] offsets,210int[] simpleTimeZoneParams,211boolean willGMTOffsetChange) {212setID(ID);213this.rawOffset = rawOffset;214this.dstSavings = dstSavings;215this.checksum = checksum;216this.transitions = transitions;217this.offsets = offsets;218this.simpleTimeZoneParams = simpleTimeZoneParams;219this.willGMTOffsetChange = willGMTOffsetChange;220}221222/**223* Returns the difference in milliseconds between local time and UTC224* of given time, taking into account both the raw offset and the225* effect of daylight savings.226*227* @param date the milliseconds in UTC228* @return the milliseconds to add to UTC to get local wall time229*/230public int getOffset(long date) {231return getOffsets(date, null, UTC_TIME);232}233234public int getOffsets(long utc, int[] offsets) {235return getOffsets(utc, offsets, UTC_TIME);236}237238public int getOffsetsByStandard(long standard, int[] offsets) {239return getOffsets(standard, offsets, STANDARD_TIME);240}241242public int getOffsetsByWall(long wall, int[] offsets) {243return getOffsets(wall, offsets, WALL_TIME);244}245246private int getOffsets(long date, int[] offsets, int type) {247// if dst is never observed, there is no transition.248if (transitions == null) {249int offset = getLastRawOffset();250if (offsets != null) {251offsets[0] = offset;252offsets[1] = 0;253}254return offset;255}256257date -= rawOffsetDiff;258int index = getTransitionIndex(date, type);259260// prior to the transition table, returns the raw offset.261// FIXME: should support LMT.262if (index < 0) {263int offset = getLastRawOffset();264if (offsets != null) {265offsets[0] = offset;266offsets[1] = 0;267}268return offset;269}270271if (index < transitions.length) {272long val = transitions[index];273int offset = this.offsets[(int)(val & OFFSET_MASK)] + rawOffsetDiff;274if (offsets != null) {275int dst = (int)((val >>> DST_NSHIFT) & 0xfL);276int save = (dst == 0) ? 0 : this.offsets[dst];277offsets[0] = offset - save;278offsets[1] = save;279}280return offset;281}282283// beyond the transitions, delegate to SimpleTimeZone if there284// is a rule; otherwise, return rawOffset.285SimpleTimeZone tz = getLastRule();286if (tz != null) {287int rawoffset = tz.getRawOffset();288long msec = date;289if (type != UTC_TIME) {290msec -= rawOffset;291}292int dstoffset = tz.getOffset(msec) - rawOffset;293294// Check if it's in a standard-to-daylight transition.295if (dstoffset > 0 && tz.getOffset(msec - dstoffset) == rawoffset && type == WALL_TIME) {296dstoffset = 0;297}298299if (offsets != null) {300offsets[0] = rawoffset;301offsets[1] = dstoffset;302}303return rawoffset + dstoffset;304}305int offset = getLastRawOffset();306if (offsets != null) {307offsets[0] = offset;308offsets[1] = 0;309}310return offset;311}312313private int getTransitionIndex(long date, int type) {314int low = 0;315int high = transitions.length - 1;316317while (low <= high) {318int mid = (low + high) / 2;319long val = transitions[mid];320long midVal = val >> TRANSITION_NSHIFT; // sign extended321if (type != UTC_TIME) {322midVal += offsets[(int)(val & OFFSET_MASK)]; // wall time323}324if (type == STANDARD_TIME) {325int dstIndex = (int)((val >>> DST_NSHIFT) & 0xfL);326if (dstIndex != 0) {327midVal -= offsets[dstIndex]; // make it standard time328}329}330331if (midVal < date) {332low = mid + 1;333} else if (midVal > date) {334high = mid - 1;335} else {336return mid;337}338}339340// if beyond the transitions, returns that index.341if (low >= transitions.length) {342return low;343}344return low - 1;345}346347/**348* Returns the difference in milliseconds between local time and349* UTC, taking into account both the raw offset and the effect of350* daylight savings, for the specified date and time. This method351* assumes that the start and end month are distinct. This method352* assumes a Gregorian calendar for calculations.353* <p>354* <em>Note: In general, clients should use355* {@link Calendar#ZONE_OFFSET Calendar.get(ZONE_OFFSET)} +356* {@link Calendar#DST_OFFSET Calendar.get(DST_OFFSET)}357* instead of calling this method.</em>358*359* @param era The era of the given date. The value must be either360* GregorianCalendar.AD or GregorianCalendar.BC.361* @param year The year in the given date.362* @param month The month in the given date. Month is 0-based. e.g.,363* 0 for January.364* @param day The day-in-month of the given date.365* @param dayOfWeek The day-of-week of the given date.366* @param millis The milliseconds in day in <em>standard</em> local time.367* @return The milliseconds to add to UTC to get local time.368*/369public int getOffset(int era, int year, int month, int day,370int dayOfWeek, int milliseconds) {371if (milliseconds < 0 || milliseconds >= AbstractCalendar.DAY_IN_MILLIS) {372throw new IllegalArgumentException();373}374375if (era == java.util.GregorianCalendar.BC) { // BC376year = 1 - year;377} else if (era != java.util.GregorianCalendar.AD) {378throw new IllegalArgumentException();379}380381CalendarDate date = gcal.newCalendarDate(null);382date.setDate(year, month + 1, day);383if (gcal.validate(date) == false) {384throw new IllegalArgumentException();385}386387// bug-for-bug compatible argument checking388if (dayOfWeek < java.util.GregorianCalendar.SUNDAY389|| dayOfWeek > java.util.GregorianCalendar.SATURDAY) {390throw new IllegalArgumentException();391}392393if (transitions == null) {394return getLastRawOffset();395}396397long dateInMillis = gcal.getTime(date) + milliseconds;398dateInMillis -= (long) rawOffset; // make it UTC399return getOffsets(dateInMillis, null, UTC_TIME);400}401402/**403* Sets the base time zone offset from GMT. This operation404* modifies all the transitions of this ZoneInfo object, including405* historical ones, if applicable.406*407* @param offsetMillis the base time zone offset to GMT.408* @see getRawOffset409*/410public synchronized void setRawOffset(int offsetMillis) {411if (offsetMillis == rawOffset + rawOffsetDiff) {412return;413}414rawOffsetDiff = offsetMillis - rawOffset;415if (lastRule != null) {416lastRule.setRawOffset(offsetMillis);417}418dirty = true;419}420421/**422* Returns the GMT offset of the current date. This GMT offset423* value is not modified during Daylight Saving Time.424*425* @return the GMT offset value in milliseconds to add to UTC time426* to get local standard time427*/428public int getRawOffset() {429if (!willGMTOffsetChange) {430return rawOffset + rawOffsetDiff;431}432433int[] offsets = new int[2];434getOffsets(System.currentTimeMillis(), offsets, UTC_TIME);435return offsets[0];436}437438public boolean isDirty() {439return dirty;440}441442private int getLastRawOffset() {443return rawOffset + rawOffsetDiff;444}445446/**447* Queries if this time zone uses Daylight Saving Time in the last known rule.448*/449public boolean useDaylightTime() {450return (simpleTimeZoneParams != null);451}452453@Override454public boolean observesDaylightTime() {455if (simpleTimeZoneParams != null) {456return true;457}458if (transitions == null) {459return false;460}461462// Look up the transition table to see if it's in DST right463// now or if there's any standard-to-daylight transition at464// any future.465long utc = System.currentTimeMillis() - rawOffsetDiff;466int index = getTransitionIndex(utc, UTC_TIME);467468// before transitions in the transition table469if (index < 0) {470return false;471}472473// the time is in the table range.474for (int i = index; i < transitions.length; i++) {475if ((transitions[i] & DST_MASK) != 0) {476return true;477}478}479// No further DST is observed.480return false;481}482483/**484* Queries if the specified date is in Daylight Saving Time.485*/486public boolean inDaylightTime(Date date) {487if (date == null) {488throw new NullPointerException();489}490491if (transitions == null) {492return false;493}494495long utc = date.getTime() - rawOffsetDiff;496int index = getTransitionIndex(utc, UTC_TIME);497498// before transitions in the transition table499if (index < 0) {500return false;501}502503// the time is in the table range.504if (index < transitions.length) {505return (transitions[index] & DST_MASK) != 0;506}507508// beyond the transition table509SimpleTimeZone tz = getLastRule();510if (tz != null) {511return tz.inDaylightTime(date);512}513return false;514}515516/**517* Returns the amount of time in milliseconds that the clock is advanced518* during daylight saving time is in effect in its last daylight saving time rule.519*520* @return the number of milliseconds the time is advanced with respect to521* standard time when daylight saving time is in effect.522*/523public int getDSTSavings() {524return dstSavings;525}526527// /**528// * @return the last year in the transition table or -1 if this529// * time zone doesn't observe any daylight saving time.530// */531// public int getMaxTransitionYear() {532// if (transitions == null) {533// return -1;534// }535// long val = transitions[transitions.length - 1];536// int offset = this.offsets[(int)(val & OFFSET_MASK)] + rawOffsetDiff;537// val = (val >> TRANSITION_NSHIFT) + offset;538// CalendarDate lastDate = Gregorian.getCalendarDate(val);539// return lastDate.getYear();540// }541542/**543* Returns a string representation of this time zone.544* @return the string545*/546public String toString() {547return getClass().getName() +548"[id=\"" + getID() + "\"" +549",offset=" + getLastRawOffset() +550",dstSavings=" + dstSavings +551",useDaylight=" + useDaylightTime() +552",transitions=" + ((transitions != null) ? transitions.length : 0) +553",lastRule=" + (lastRule == null ? getLastRuleInstance() : lastRule) +554"]";555}556557/**558* Gets all available IDs supported in the Java run-time.559*560* @return an array of time zone IDs.561*/562public static String[] getAvailableIDs() {563return ZoneInfoFile.getZoneIds();564}565566/**567* Gets all available IDs that have the same value as the568* specified raw GMT offset.569*570* @param rawOffset the GMT offset in milliseconds. This571* value should not include any daylight saving time.572*573* @return an array of time zone IDs.574*/575public static String[] getAvailableIDs(int rawOffset) {576return ZoneInfoFile.getZoneIds(rawOffset);577}578579/**580* Gets the ZoneInfo for the given ID.581*582* @param ID the ID for a ZoneInfo. See TimeZone for detail.583*584* @return the specified ZoneInfo object, or null if there is no585* time zone of the ID.586*/587public static TimeZone getTimeZone(String ID) {588return ZoneInfoFile.getZoneInfo(ID);589}590591private transient SimpleTimeZone lastRule;592593/**594* Returns a SimpleTimeZone object representing the last GMT595* offset and DST schedule or null if this time zone doesn't596* observe DST.597*/598private synchronized SimpleTimeZone getLastRule() {599if (lastRule == null) {600lastRule = getLastRuleInstance();601}602return lastRule;603}604605/**606* Returns a SimpleTimeZone object that represents the last607* known daylight saving time rules.608*609* @return a SimpleTimeZone object or null if this time zone610* doesn't observe DST.611*/612public SimpleTimeZone getLastRuleInstance() {613if (simpleTimeZoneParams == null) {614return null;615}616if (simpleTimeZoneParams.length == 10) {617return new SimpleTimeZone(getLastRawOffset(), getID(),618simpleTimeZoneParams[0],619simpleTimeZoneParams[1],620simpleTimeZoneParams[2],621simpleTimeZoneParams[3],622simpleTimeZoneParams[4],623simpleTimeZoneParams[5],624simpleTimeZoneParams[6],625simpleTimeZoneParams[7],626simpleTimeZoneParams[8],627simpleTimeZoneParams[9],628dstSavings);629}630return new SimpleTimeZone(getLastRawOffset(), getID(),631simpleTimeZoneParams[0],632simpleTimeZoneParams[1],633simpleTimeZoneParams[2],634simpleTimeZoneParams[3],635simpleTimeZoneParams[4],636simpleTimeZoneParams[5],637simpleTimeZoneParams[6],638simpleTimeZoneParams[7],639dstSavings);640}641642/**643* Returns a copy of this <code>ZoneInfo</code>.644*/645public Object clone() {646ZoneInfo zi = (ZoneInfo) super.clone();647zi.lastRule = null;648return zi;649}650651/**652* Returns a hash code value calculated from the GMT offset and653* transitions.654* @return a hash code of this time zone655*/656public int hashCode() {657return getLastRawOffset() ^ checksum;658}659660/**661* Compares the equity of two ZoneInfo objects.662*663* @param obj the object to be compared with664* @return true if given object is same as this ZoneInfo object,665* false otherwise.666*/667public boolean equals(Object obj) {668if (this == obj) {669return true;670}671if (!(obj instanceof ZoneInfo)) {672return false;673}674ZoneInfo that = (ZoneInfo) obj;675return (getID().equals(that.getID())676&& (getLastRawOffset() == that.getLastRawOffset())677&& (checksum == that.checksum));678}679680/**681* Returns true if this zone has the same raw GMT offset value and682* transition table as another zone info. If the specified683* TimeZone object is not a ZoneInfo instance, this method returns684* true if the specified TimeZone object has the same raw GMT685* offset value with no daylight saving time.686*687* @param other the ZoneInfo object to be compared with688* @return true if the given <code>TimeZone</code> has the same689* GMT offset and transition information; false, otherwise.690*/691public boolean hasSameRules(TimeZone other) {692if (this == other) {693return true;694}695if (other == null) {696return false;697}698if (!(other instanceof ZoneInfo)) {699if (getRawOffset() != other.getRawOffset()) {700return false;701}702// if both have the same raw offset and neither observes703// DST, they have the same rule.704if ((transitions == null)705&& (useDaylightTime() == false)706&& (other.useDaylightTime() == false)) {707return true;708}709return false;710}711if (getLastRawOffset() != ((ZoneInfo)other).getLastRawOffset()) {712return false;713}714return (checksum == ((ZoneInfo)other).checksum);715}716717/**718* Returns a Map from alias time zone IDs to their standard719* time zone IDs.720*721* @return the Map that holds the mappings from alias time zone IDs722* to their standard time zone IDs, or null if723* <code>ZoneInfoMappings</code> file is not available.724*/725public static Map<String, String> getAliasTable() {726return ZoneInfoFile.getAliasMap();727}728729private void readObject(ObjectInputStream stream)730throws IOException, ClassNotFoundException {731stream.defaultReadObject();732// We don't know how this object from 1.4.x or earlier has733// been mutated. So it should always be marked as `dirty'.734dirty = true;735}736}737738739