Path: blob/master/test/jdk/sun/util/calendar/zi/ZoneInfoOld.java
41153 views
/*1* Copyright (c) 2000, 2018, 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.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.IOException;24import java.io.ObjectInputStream;25import java.lang.ref.SoftReference;26import java.time.ZoneOffset;27import java.time.LocalDateTime;28import java.util.Arrays;29import java.util.ArrayList;30import java.util.Date;31import java.util.List;32import java.util.Locale;33import java.util.Map;34import java.util.SimpleTimeZone;35import java.util.TimeZone;3637import sun.util.calendar.CalendarSystem;38import sun.util.calendar.CalendarDate;3940/**41* <code>ZoneInfoOld</code> is an implementation subclass of {@link42* java.util.TimeZone TimeZone} that represents GMT offsets and43* daylight saving time transitions of a time zone.44* <p>45* The daylight saving time transitions are described in the {@link46* #transitions transitions} table consisting of a chronological47* sequence of transitions of GMT offset and/or daylight saving time48* changes. Since all transitions are represented in UTC, in theory,49* <code>ZoneInfoOld</code> can be used with any calendar systems except50* for the {@link #getOffset(int,int,int,int,int,int) getOffset}51* method that takes Gregorian calendar date fields.52* <p>53* This table covers transitions from 1900 until 2037 (as of version54* 1.4), Before 1900, it assumes that there was no daylight saving55* time and the <code>getOffset</code> methods always return the56* {@link #getRawOffset} value. No Local Mean Time is supported. If a57* specified date is beyond the transition table and this time zone is58* supposed to observe daylight saving time in 2037, it delegates59* operations to a {@link java.util.SimpleTimeZone SimpleTimeZone}60* object created using the daylight saving time schedule as of 2037.61* <p>62* The date items, transitions, GMT offset(s), etc. are read from a database63* file. See {@link ZoneInfoFile} for details.64* @see java.util.SimpleTimeZone65* @since 1.466*/6768public class ZoneInfoOld extends TimeZone {6970// The constants assume no leap seconds support.71static final int SECOND_IN_MILLIS = 1000;72static final int MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;73static final int HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;74static final int DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;7576private static final int UTC_TIME = 0;77private static final int STANDARD_TIME = 1;78private static final int WALL_TIME = 2;7980private static final long OFFSET_MASK = 0x0fL;81private static final long DST_MASK = 0xf0L;82private static final int DST_NSHIFT = 4;83// this bit field is reserved for abbreviation support84private static final long ABBR_MASK = 0xf00L;85private static final int TRANSITION_NSHIFT = 12;8687// Flag for supporting JDK backward compatible IDs, such as "EST".88static final boolean USE_OLDMAPPING;89static {90String oldmapping = System.getProperty("sun.timezone.ids.oldmapping", "false").toLowerCase(Locale.ROOT);91USE_OLDMAPPING = (oldmapping.equals("yes") || oldmapping.equals("true"));92}9394// IDs having conflicting data between Olson and JDK 1.195static final String[] conflictingIDs = {96"EST", "MST", "HST"97};9899private static final CalendarSystem gcal = CalendarSystem.getGregorianCalendar();100101/**102* The raw GMT offset in milliseconds between this zone and GMT.103* Negative offsets are to the west of Greenwich. To obtain local104* <em>standard</em> time, add the offset to GMT time.105* @serial106*/107int rawOffset;108109/**110* Difference in milliseconds from the original GMT offset in case111* the raw offset value has been modified by calling {@link112* #setRawOffset}. The initial value is 0.113* @serial114*/115int rawOffsetDiff = 0;116117/**118* A CRC32 value of all pairs of transition time (in milliseconds119* in <code>long</code>) in local time and its GMT offset (in120* seconds in <code>int</code>) in the chronological order. Byte121* values of each <code>long</code> and <code>int</code> are taken122* in the big endian order (i.e., MSB to LSB).123* @serial124*/125int checksum;126127/**128* The amount of time in milliseconds saved during daylight saving129* time. If <code>useDaylight</code> is false, this value is 0.130* @serial131*/132int dstSavings;133134/**135* This array describes transitions of GMT offsets of this time136* zone, including both raw offset changes and daylight saving137* time changes.138* A long integer consists of four bit fields.139* <ul>140* <li>The most significant 52-bit field represents transition141* time in milliseconds from Gregorian January 1 1970, 00:00:00142* GMT.</li>143* <li>The next 4-bit field is reserved and must be 0.</li>144* <li>The next 4-bit field is an index value to {@link #offsets145* offsets[]} for the amount of daylight saving at the146* transition. If this value is zero, it means that no daylight147* saving, not the index value zero.</li>148* <li>The least significant 4-bit field is an index value to149* {@link #offsets offsets[]} for <em>total</em> GMT offset at the150* transition.</li>151* </ul>152* If this time zone doesn't observe daylight saving time and has153* never changed any GMT offsets in the past, this value is null.154* @serial155*/156long[] transitions;157158/**159* This array holds all unique offset values in160* milliseconds. Index values to this array are stored in the161* transitions array elements.162* @serial163*/164int[] offsets;165166/**167* SimpleTimeZone parameter values. It has to have either 8 for168* {@link java.util.SimpleTimeZone#SimpleTimeZone(int, String,169* int, int , int , int , int , int , int , int , int) the170* 11-argument SimpleTimeZone constructor} or 10 for {@link171* java.util.SimpleTimeZone#SimpleTimeZone(int, String, int, int,172* int , int , int , int , int , int , int, int, int) the173* 13-argument SimpleTimeZone constructor} parameters.174* @serial175*/176int[] simpleTimeZoneParams;177178/**179* True if the raw GMT offset value would change after the time180* zone data has been generated; false, otherwise. The default181* value is false.182* @serial183*/184boolean willGMTOffsetChange = false;185186/**187* True if the object has been modified after its instantiation.188*/189transient private boolean dirty = false;190191private static final long serialVersionUID = 2653134537216586139L;192193/**194* A constructor.195*/196public ZoneInfoOld() {197}198199/**200* A Constructor for CustomID.201*/202public ZoneInfoOld(String ID, int rawOffset) {203this(ID, rawOffset, 0, 0, null, null, null, false);204}205206/**207* Constructs a ZoneInfoOld instance.208*209* @param ID time zone name210* @param rawOffset GMT offset in milliseconds211* @param dstSavings daylight saving value in milliseconds or 0212* (zero) if this time zone doesn't observe Daylight Saving Time.213* @param checksum CRC32 value with all transitions table entry214* values215* @param transitions transition table216* @param offsets offset value table217* @param simpleTimeZoneParams parameter values for constructing218* SimpleTimeZone219* @param willGMTOffsetChange the value of willGMTOffsetChange220*/221ZoneInfoOld(String ID,222int rawOffset,223int dstSavings,224int checksum,225long[] transitions,226int[] offsets,227int[] simpleTimeZoneParams,228boolean willGMTOffsetChange) {229setID(ID);230this.rawOffset = rawOffset;231this.dstSavings = dstSavings;232this.checksum = checksum;233this.transitions = transitions;234this.offsets = offsets;235this.simpleTimeZoneParams = simpleTimeZoneParams;236this.willGMTOffsetChange = willGMTOffsetChange;237}238239/**240* Returns the difference in milliseconds between local time and UTC241* of given time, taking into account both the raw offset and the242* effect of daylight savings.243*244* @param date the milliseconds in UTC245* @return the milliseconds to add to UTC to get local wall time246*/247public int getOffset(long date) {248return getOffsets(date, null, UTC_TIME);249}250251public int getOffsets(long utc, int[] offsets) {252return getOffsets(utc, offsets, UTC_TIME);253}254255public int getOffsetsByStandard(long standard, int[] offsets) {256return getOffsets(standard, offsets, STANDARD_TIME);257}258259public int getOffsetsByWall(long wall, int[] offsets) {260return getOffsets(wall, offsets, WALL_TIME);261}262263private int getOffsets(long date, int[] offsets, int type) {264// if dst is never observed, there is no transition.265if (transitions == null) {266int offset = getLastRawOffset();267if (offsets != null) {268offsets[0] = offset;269offsets[1] = 0;270}271return offset;272}273274date -= rawOffsetDiff;275int index = getTransitionIndex(date, type);276277// prior to the transition table, returns the raw offset.278// FIXME: should support LMT.279if (index < 0) {280int offset = getLastRawOffset();281if (offsets != null) {282offsets[0] = offset;283offsets[1] = 0;284}285return offset;286}287288if (index < transitions.length) {289long val = transitions[index];290int offset = this.offsets[(int)(val & OFFSET_MASK)] + rawOffsetDiff;291if (offsets != null) {292int dst = (int)((val >>> DST_NSHIFT) & 0xfL);293int save = (dst == 0) ? 0 : this.offsets[dst];294offsets[0] = offset - save;295offsets[1] = save;296}297return offset;298}299300// beyond the transitions, delegate to SimpleTimeZone if there301// is a rule; otherwise, return rawOffset.302SimpleTimeZone tz = getLastRule();303if (tz != null) {304int rawoffset = tz.getRawOffset();305long msec = date;306if (type != UTC_TIME) {307msec -= rawOffset;308}309int dstoffset = tz.getOffset(msec) - rawOffset;310311// Check if it's in a standard-to-daylight transition.312if (dstoffset > 0 && tz.getOffset(msec - dstoffset) == rawoffset) {313dstoffset = 0;314}315316if (offsets != null) {317offsets[0] = rawoffset;318offsets[1] = dstoffset;319}320return rawoffset + dstoffset;321}322int offset = getLastRawOffset();323if (offsets != null) {324offsets[0] = offset;325offsets[1] = 0;326}327return offset;328}329330private int getTransitionIndex(long date, int type) {331int low = 0;332int high = transitions.length - 1;333334while (low <= high) {335int mid = (low + high) / 2;336long val = transitions[mid];337long midVal = val >> TRANSITION_NSHIFT; // sign extended338if (type != UTC_TIME) {339midVal += offsets[(int)(val & OFFSET_MASK)]; // wall time340}341if (type == STANDARD_TIME) {342int dstIndex = (int)((val >>> DST_NSHIFT) & 0xfL);343if (dstIndex != 0) {344midVal -= offsets[dstIndex]; // make it standard time345}346}347348if (midVal < date) {349low = mid + 1;350} else if (midVal > date) {351high = mid - 1;352} else {353return mid;354}355}356357// if beyond the transitions, returns that index.358if (low >= transitions.length) {359return low;360}361return low - 1;362}363364/**365* Returns the difference in milliseconds between local time and366* UTC, taking into account both the raw offset and the effect of367* daylight savings, for the specified date and time. This method368* assumes that the start and end month are distinct. This method369* assumes a Gregorian calendar for calculations.370* <p>371* <em>Note: In general, clients should use372* {@link Calendar#ZONE_OFFSET Calendar.get(ZONE_OFFSET)} +373* {@link Calendar#DST_OFFSET Calendar.get(DST_OFFSET)}374* instead of calling this method.</em>375*376* @param era The era of the given date. The value must be either377* GregorianCalendar.AD or GregorianCalendar.BC.378* @param year The year in the given date.379* @param month The month in the given date. Month is 0-based. e.g.,380* 0 for January.381* @param day The day-in-month of the given date.382* @param dayOfWeek The day-of-week of the given date.383* @param millis The milliseconds in day in <em>standard</em> local time.384* @return The milliseconds to add to UTC to get local time.385*/386public int getOffset(int era, int year, int month, int day,387int dayOfWeek, int milliseconds) {388if (milliseconds < 0 || milliseconds >= DAY_IN_MILLIS) {389throw new IllegalArgumentException();390}391392if (era == java.util.GregorianCalendar.BC) { // BC393year = 1 - year;394} else if (era != java.util.GregorianCalendar.AD) {395throw new IllegalArgumentException();396}397398CalendarDate date = gcal.newCalendarDate(null);399date.setDate(year, month + 1, day);400if (gcal.validate(date) == false) {401throw new IllegalArgumentException();402}403404// bug-for-bug compatible argument checking405if (dayOfWeek < java.util.GregorianCalendar.SUNDAY406|| dayOfWeek > java.util.GregorianCalendar.SATURDAY) {407throw new IllegalArgumentException();408}409410if (transitions == null) {411return getLastRawOffset();412}413414long dateInMillis = gcal.getTime(date) + milliseconds;415dateInMillis -= (long) rawOffset; // make it UTC416return getOffsets(dateInMillis, null, UTC_TIME);417}418419/**420* Sets the base time zone offset from GMT. This operation421* modifies all the transitions of this ZoneInfoOld object, including422* historical ones, if applicable.423*424* @param offsetMillis the base time zone offset to GMT.425* @see getRawOffset426*/427public synchronized void setRawOffset(int offsetMillis) {428if (offsetMillis == rawOffset + rawOffsetDiff) {429return;430}431rawOffsetDiff = offsetMillis - rawOffset;432if (lastRule != null) {433lastRule.setRawOffset(offsetMillis);434}435dirty = true;436}437438/**439* Returns the GMT offset of the current date. This GMT offset440* value is not modified during Daylight Saving Time.441*442* @return the GMT offset value in milliseconds to add to UTC time443* to get local standard time444*/445public int getRawOffset() {446if (!willGMTOffsetChange) {447return rawOffset + rawOffsetDiff;448}449450int[] offsets = new int[2];451getOffsets(System.currentTimeMillis(), offsets, UTC_TIME);452return offsets[0];453}454455public boolean isDirty() {456return dirty;457}458459int getLastRawOffset() {460return rawOffset + rawOffsetDiff;461}462463/**464* Queries if this time zone uses Daylight Saving Time in the last known rule.465*/466public boolean useDaylightTime() {467return (simpleTimeZoneParams != null);468}469470@Override471public boolean observesDaylightTime() {472if (simpleTimeZoneParams != null) {473return true;474}475if (transitions == null) {476return false;477}478479// Look up the transition table to see if it's in DST right480// now or if there's any standard-to-daylight transition at481// any future.482long utc = System.currentTimeMillis() - rawOffsetDiff;483int index = getTransitionIndex(utc, UTC_TIME);484485// before transitions in the transition table486if (index < 0) {487return false;488}489490// the time is in the table range.491for (int i = index; i < transitions.length; i++) {492if ((transitions[i] & DST_MASK) != 0) {493return true;494}495}496// No further DST is observed.497return false;498}499500/**501* Queries if the specified date is in Daylight Saving Time.502*/503public boolean inDaylightTime(Date date) {504if (date == null) {505throw new NullPointerException();506}507508if (transitions == null) {509return false;510}511512long utc = date.getTime() - rawOffsetDiff;513int index = getTransitionIndex(utc, UTC_TIME);514515// before transitions in the transition table516if (index < 0) {517return false;518}519520// the time is in the table range.521if (index < transitions.length) {522return (transitions[index] & DST_MASK) != 0;523}524525// beyond the transition table526SimpleTimeZone tz = getLastRule();527if (tz != null) {528return tz.inDaylightTime(date);529}530return false;531}532533/**534* Returns the amount of time in milliseconds that the clock is advanced535* during daylight saving time is in effect in its last daylight saving time rule.536*537* @return the number of milliseconds the time is advanced with respect to538* standard time when daylight saving time is in effect.539*/540public int getDSTSavings() {541return dstSavings;542}543544// /**545// * @return the last year in the transition table or -1 if this546// * time zone doesn't observe any daylight saving time.547// */548// public int getMaxTransitionYear() {549// if (transitions == null) {550// return -1;551// }552// long val = transitions[transitions.length - 1];553// int offset = this.offsets[(int)(val & OFFSET_MASK)] + rawOffsetDiff;554// val = (val >> TRANSITION_NSHIFT) + offset;555// CalendarDate lastDate = Gregorian.getCalendarDate(val);556// return lastDate.getYear();557// }558559/**560* Returns a string representation of this time zone.561* @return the string562*/563public String toString() {564return getClass().getName() +565"[id=\"" + getID() + "\"" +566",offset=" + getLastRawOffset() +567",dstSavings=" + dstSavings +568",useDaylight=" + useDaylightTime() +569",transitions=" + ((transitions != null) ? transitions.length : 0) +570",lastRule=" + (lastRule == null ? getLastRuleInstance() : lastRule) +571"]";572}573574/**575* Gets all available IDs supported in the Java run-time.576*577* @return an array of time zone IDs.578*/579public static String[] getAvailableIDs() {580List<String> idList = ZoneInfoFile.getZoneIDs();581List<String> excluded = ZoneInfoFile.getExcludedZones();582if (excluded != null) {583// List all zones from the idList and excluded lists584List<String> list = new ArrayList<>(idList.size() + excluded.size());585list.addAll(idList);586list.addAll(excluded);587idList = list;588}589String[] ids = new String[idList.size()];590return idList.toArray(ids);591}592593/**594* Gets all available IDs that have the same value as the595* specified raw GMT offset.596*597* @param rawOffset the GMT offset in milliseconds. This598* value should not include any daylight saving time.599*600* @return an array of time zone IDs.601*/602public static String[] getAvailableIDs(int rawOffset) {603String[] result;604List<String> matched = new ArrayList<>();605List<String> IDs = ZoneInfoFile.getZoneIDs();606int[] rawOffsets = ZoneInfoFile.getRawOffsets();607608loop:609for (int index = 0; index < rawOffsets.length; index++) {610if (rawOffsets[index] == rawOffset) {611byte[] indices = ZoneInfoFile.getRawOffsetIndices();612for (int i = 0; i < indices.length; i++) {613if (indices[i] == index) {614matched.add(IDs.get(i++));615while (i < indices.length && indices[i] == index) {616matched.add(IDs.get(i++));617}618break loop;619}620}621}622}623624// We need to add any zones from the excluded zone list that625// currently have the same GMT offset as the specified626// rawOffset. The zones returned by this method may not be627// correct as of return to the caller if any GMT offset628// transition is happening during this GMT offset checking...629List<String> excluded = ZoneInfoFile.getExcludedZones();630if (excluded != null) {631for (String id : excluded) {632TimeZone zi = getTimeZone(id);633if (zi != null && zi.getRawOffset() == rawOffset) {634matched.add(id);635}636}637}638639result = new String[matched.size()];640matched.toArray(result);641return result;642}643644/**645* Gets the ZoneInfoOld for the given ID.646*647* @param ID the ID for a ZoneInfoOld. See TimeZone for detail.648*649* @return the specified ZoneInfoOld object, or null if there is no650* time zone of the ID.651*/652public static TimeZone getTimeZone(String ID) {653String givenID = null;654655/*656* If old JDK compatibility is specified, get the old alias657* name.658*/659if (USE_OLDMAPPING) {660String compatibleID = TzIDOldMapping.MAP.get(ID);661if (compatibleID != null) {662givenID = ID;663ID = compatibleID;664}665}666667ZoneInfoOld zi = ZoneInfoFile.getZoneInfoOld(ID);668if (zi == null) {669// if we can't create an object for the ID, try aliases.670try {671Map<String, String> map = getAliasTable();672String alias = ID;673while ((alias = map.get(alias)) != null) {674zi = ZoneInfoFile.getZoneInfoOld(alias);675if (zi != null) {676zi.setID(ID);677zi = ZoneInfoFile.addToCache(ID, zi);678zi = (ZoneInfoOld) zi.clone();679break;680}681}682} catch (Exception e) {683// ignore exceptions684}685}686687if (givenID != null && zi != null) {688zi.setID(givenID);689}690return zi;691}692693private transient SimpleTimeZone lastRule;694695/**696* Returns a SimpleTimeZone object representing the last GMT697* offset and DST schedule or null if this time zone doesn't698* observe DST.699*/700synchronized SimpleTimeZone getLastRule() {701if (lastRule == null) {702lastRule = getLastRuleInstance();703}704return lastRule;705}706707/**708* Returns a SimpleTimeZone object that represents the last709* known daylight saving time rules.710*711* @return a SimpleTimeZone object or null if this time zone712* doesn't observe DST.713*/714public SimpleTimeZone getLastRuleInstance() {715if (simpleTimeZoneParams == null) {716return null;717}718if (simpleTimeZoneParams.length == 10) {719return new SimpleTimeZone(getLastRawOffset(), getID(),720simpleTimeZoneParams[0],721simpleTimeZoneParams[1],722simpleTimeZoneParams[2],723simpleTimeZoneParams[3],724simpleTimeZoneParams[4],725simpleTimeZoneParams[5],726simpleTimeZoneParams[6],727simpleTimeZoneParams[7],728simpleTimeZoneParams[8],729simpleTimeZoneParams[9],730dstSavings);731}732return new SimpleTimeZone(getLastRawOffset(), getID(),733simpleTimeZoneParams[0],734simpleTimeZoneParams[1],735simpleTimeZoneParams[2],736simpleTimeZoneParams[3],737simpleTimeZoneParams[4],738simpleTimeZoneParams[5],739simpleTimeZoneParams[6],740simpleTimeZoneParams[7],741dstSavings);742}743744/**745* Returns a copy of this <code>ZoneInfoOld</code>.746*/747public Object clone() {748ZoneInfoOld zi = (ZoneInfoOld) super.clone();749zi.lastRule = null;750return zi;751}752753/**754* Returns a hash code value calculated from the GMT offset and755* transitions.756* @return a hash code of this time zone757*/758public int hashCode() {759return getLastRawOffset() ^ checksum;760}761762/**763* Compares the equity of two ZoneInfoOld objects.764*765* @param obj the object to be compared with766* @return true if given object is same as this ZoneInfoOld object,767* false otherwise.768*/769public boolean equals(Object obj) {770if (this == obj) {771return true;772}773if (!(obj instanceof ZoneInfoOld)) {774return false;775}776ZoneInfoOld that = (ZoneInfoOld) obj;777return (getID().equals(that.getID())778&& (getLastRawOffset() == that.getLastRawOffset())779&& (checksum == that.checksum));780}781782/**783* Returns true if this zone has the same raw GMT offset value and784* transition table as another zone info. If the specified785* TimeZone object is not a ZoneInfoOld instance, this method returns786* true if the specified TimeZone object has the same raw GMT787* offset value with no daylight saving time.788*789* @param other the ZoneInfoOld object to be compared with790* @return true if the given <code>TimeZone</code> has the same791* GMT offset and transition information; false, otherwise.792*/793public boolean hasSameRules(TimeZone other) {794if (this == other) {795return true;796}797if (other == null) {798return false;799}800if (!(other instanceof ZoneInfoOld)) {801if (getRawOffset() != other.getRawOffset()) {802return false;803}804// if both have the same raw offset and neither observes805// DST, they have the same rule.806if ((transitions == null)807&& (useDaylightTime() == false)808&& (other.useDaylightTime() == false)) {809return true;810}811return false;812}813if (getLastRawOffset() != ((ZoneInfoOld)other).getLastRawOffset()) {814return false;815}816return (checksum == ((ZoneInfoOld)other).checksum);817}818819private static SoftReference<Map<String, String>> aliasTable;820821static Map<String, String> getCachedAliasTable() {822Map<String, String> aliases = null;823824SoftReference<Map<String, String>> cache = aliasTable;825if (cache != null) {826aliases = cache.get();827}828return aliases;829}830831/**832* Returns a Map from alias time zone IDs to their standard833* time zone IDs.834*835* @return the Map that holds the mappings from alias time zone IDs836* to their standard time zone IDs, or null if837* <code>ZoneInfoOldMappings</code> file is not available.838*/839public synchronized static Map<String, String> getAliasTable() {840Map<String, String> aliases = getCachedAliasTable();841if (aliases == null) {842aliases = ZoneInfoFile.getZoneAliases();843if (aliases != null) {844if (!USE_OLDMAPPING) {845// Remove the conflicting IDs from the alias table.846for (String key : conflictingIDs) {847aliases.remove(key);848}849}850aliasTable = new SoftReference<Map<String, String>>(aliases);851}852}853return aliases;854}855856private void readObject(ObjectInputStream stream)857throws IOException, ClassNotFoundException {858stream.defaultReadObject();859// We don't know how this object from 1.4.x or earlier has860// been mutated. So it should always be marked as `dirty'.861dirty = true;862}863864//////////////////////////////////////////////////////////////865public boolean equalsTo(ZoneInfoOld other) {866return (getID().equals(other.getID())867&& (getLastRawOffset() == other.getLastRawOffset())868&& (dstSavings == other.dstSavings)869&& (willGMTOffsetChange == other.willGMTOffsetChange)870&& (checksum == other.checksum)871&& equalsTransOffsets(other)872&& (Arrays.equals(simpleTimeZoneParams, other.simpleTimeZoneParams) ||873getLastRule().equals(other.getLastRule())));874}875876private boolean equalsTransOffsets(ZoneInfoOld other) {877if (transitions == null) {878return (other.transitions == null &&879Arrays.equals(offsets, other.offsets));880}881if (other.transitions == null ||882transitions.length != other.transitions.length) {883return false;884}885// if offsets and other.offsets have different order886// the last 4-bit in trans are different.887for (int i = 0; i < transitions.length; i++) {888long val = transitions[i];889int dst = (int)((val >>> DST_NSHIFT) & 0xfL);890int save = (dst == 0) ? 0 : offsets[dst] / 1000;891int off = offsets[(int)(val & OFFSET_MASK)]/1000;892long second = (val >> TRANSITION_NSHIFT)/1000;893894val = other.transitions[i];895int dstO = (int)((val >>> DST_NSHIFT) & 0xfL);896int saveO = (dstO == 0) ? 0 : other.offsets[dstO] / 1000;897int offO = other.offsets[(int)(val & OFFSET_MASK)]/1000;898long secondO = (val >> TRANSITION_NSHIFT)/1000;899if ((dst == 0) != (dstO == 0) || save != saveO || off != offO || second != secondO)900return false;901}902return true;903}904905private int transToString(long val, int off_old, int[] offsets, StringBuilder sb) {906int dst = (int)((val >>> DST_NSHIFT) & 0xfL);907int save = (dst == 0) ? 0 : offsets[dst] / 1000;908int off = offsets[(int)(val & OFFSET_MASK)]/1000;909long second = (val >> TRANSITION_NSHIFT)/1000;910ZoneOffset offset_old = ZoneOffset.ofTotalSeconds(off_old);911ZoneOffset offset = ZoneOffset.ofTotalSeconds(off);912sb.append(" " + LocalDateTime.ofEpochSecond(second, 0, offset_old));913914sb.append(" [utc=" + second +915" raw=" + Long.toHexString(val >> TRANSITION_NSHIFT) +916", offset=" + off + "/" + offset + ", saving=" + save + "]");917return off;918}919920public String diffsTo(ZoneInfoOld other) {921922int rawOffset0 = other.rawOffset;923int checksum0 = other.checksum;924int dstSavings0 = other.dstSavings;925long[] transitions0 = other.transitions;926int[] offsets0 = other.offsets;927int[] simpleTimeZoneParams0 = other.simpleTimeZoneParams;928boolean willGMTOffsetChange0 = other.willGMTOffsetChange;929930931//return getClass().getName() +932StringBuilder sb = new StringBuilder();933sb.append("******************************\n" +934getID() + " : " + other.getID());935// ROC is excluded by ZoneInfoOld936if ("ROC".equals(getID())) {937return sb.toString();938}939if (rawOffset != rawOffset0 ||940dstSavings != dstSavings0 ||941checksum != checksum0 ||942willGMTOffsetChange != willGMTOffsetChange0 ||943(simpleTimeZoneParams != null ) != (simpleTimeZoneParams0 != null) ||944(transitions != null && transitions0 != null &&945transitions.length != transitions0.length))946{947sb.append("\n offset=" + getLastRawOffset() +948",dstSavings=" + dstSavings +949",useDaylight=" + useDaylightTime() +950",transitions=" + ((transitions != null) ? transitions.length : 0) +951",offsets=" + ((offsets != null) ? offsets.length : 0) +952",checksum=" + checksum +953",gmtChanged=" + willGMTOffsetChange)954.append("\n[NG]offset=" + rawOffset0 +955",dstSavings=" + dstSavings0 +956",useDaylight=" + (simpleTimeZoneParams != null) +957",transitions=" + ((transitions0 != null) ? transitions0.length : 0) +958",offsets=" + ((offsets0 != null) ? offsets0.length : 0) +959",checksum=" + checksum0 +960",gmtChanged=" + willGMTOffsetChange0 +961"");962}963// offsets964if (!Arrays.equals(offsets, offsets0)) {965sb.append("\n offset.len=" + ((offsets != null)? offsets.length : "null") +966" " + ((offsets0 != null)? offsets0.length : "null"));967if (offsets != null && offsets0.length != 0) {968int len = Math.min(offsets.length, offsets0.length);969int i = 0;970for (i = 0; i < len; i++) {971sb.append("\n " +972ZoneOffset.ofTotalSeconds(offsets[i]/1000) + " " +973ZoneOffset.ofTotalSeconds(offsets0[i]/1000));974}975for (; i < offsets0.length; i++) {976sb.append("\n " + ZoneOffset.ofTotalSeconds(offsets0[i]/1000));977}978}979}980// trans981int offset = 0;982int offset0 = 0;983if (!equalsTransOffsets(other)) {984sb.append("\n -------------");985if ((transitions == null) != (transitions0 == null)) {986sb.append("\n (NG) Different trans(null) :" +987transitions + ", " + transitions0);988if (transitions != null) {989for (int i = 0; i < transitions.length; i++) {990sb.append("\n (NG)");991offset = transToString(transitions[i], offset, offsets, sb);992}993}994} else {995if (transitions.length != transitions0.length) {996sb.append("\n (NG) Different trans size :" +997transitions.length + ", " + transitions0.length);998}999int length = Math.min(transitions.length, transitions0.length);1000for (int i = 0; i < length; i++) {1001// sb.append("\n[" + i + "] ");1002// offset = transToString(transitions[i], offset, offsets, sb);1003long val = transitions[i];1004int dst = (int)((val >>> DST_NSHIFT) & 0xfL);1005int save = (dst == 0) ? 0 : offsets[dst] / 1000;1006int off = offsets[(int)(val & OFFSET_MASK)]/1000;1007long second = (val >> TRANSITION_NSHIFT)/1000;1008sb.append("\n ");1009offset = transToString(transitions[i], offset, offsets, sb);1010if (transitions0 == null || i >= transitions0.length) {1011sb.append("\n ");1012offset = transToString(transitions[i], offset, offsets, sb);1013sb.append("\n (NG) trans0 is null or < trans.length");1014} else {1015long val0 = transitions0[i];1016int dst0 = (int)((val0 >>> DST_NSHIFT) & 0xfL);1017int save0 = (dst0 == 0) ? 0 : offsets0[dst0] / 1000;1018int off0 = offsets0[(int)(val0 & OFFSET_MASK)]/1000;1019long second0 = (val0 >> TRANSITION_NSHIFT)/1000;1020if (save != save0 || off != off0 || second != second0) {1021sb.append("\n (NG)");1022} else {1023sb.append("\n (OK)");1024}1025offset0 = transToString(transitions0[i], offset0, offsets0, sb);1026sb.append("\n -----");1027}1028}1029}1030}1031SimpleTimeZone stz = getLastRuleInstance();1032if (stz != null) {1033SimpleTimeZone stz0 = other.getLastRule();1034if (!stz.hasSameRules(stz0)) {1035sb.append("\n -------------")1036.append("\n SimpleTimeZone (NG)")1037.append("\n stz=" + stz)1038.append("\n stz0=" + stz0);1039}1040}1041sb.append("\n -------------");1042return sb.toString();1043}1044}104510461047