• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 // © 2016 and later: Unicode, Inc. and others.
3 // License & terms of use: http://www.unicode.org/copyright.html#License
4  /*
5   *******************************************************************************
6   * Copyright (C) 2005-2016, International Business Machines Corporation and
7   * others. All Rights Reserved.
8   *******************************************************************************
9   */
10 package ohos.global.icu.impl;
11 
12 import java.io.IOException;
13 import java.io.ObjectInputStream;
14 import java.util.Arrays;
15 import java.util.Date;
16 import java.util.MissingResourceException;
17 
18 import ohos.global.icu.util.AnnualTimeZoneRule;
19 import ohos.global.icu.util.BasicTimeZone;
20 import ohos.global.icu.util.Calendar;
21 import ohos.global.icu.util.DateTimeRule;
22 import ohos.global.icu.util.GregorianCalendar;
23 import ohos.global.icu.util.InitialTimeZoneRule;
24 import ohos.global.icu.util.SimpleTimeZone;
25 import ohos.global.icu.util.TimeArrayTimeZoneRule;
26 import ohos.global.icu.util.TimeZone;
27 import ohos.global.icu.util.TimeZoneRule;
28 import ohos.global.icu.util.TimeZoneTransition;
29 import ohos.global.icu.util.UResourceBundle;
30 
31 /**
32  * A time zone based on the Olson tz database.  Olson time zones change
33  * behavior over time.  The raw offset, rules, presence or absence of
34  * daylight savings time, and even the daylight savings amount can all
35  * vary.
36  *
37  * This class uses a resource bundle named "zoneinfo".  Zoneinfo is a
38  * table containing different kinds of resources.  In several places,
39  * zones are referred to using integers.  A zone's integer is a number
40  * from 0..n-1, where n is the number of zones, with the zones sorted
41  * in lexicographic order.
42  *
43  * 1. Zones.  These have keys corresponding to the Olson IDs, e.g.,
44  * "Asia/Shanghai".  Each resource describes the behavior of the given
45  * zone.  Zones come in two different formats.
46  *
47  *   a. Zone (table).  A zone is a table resource contains several
48  *   type of resources below:
49  *
50  *   - typeOffsets:intvector (Required)
51  *
52  *   Sets of UTC raw/dst offset pairs in seconds.  Entries at
53  *   2n represents raw offset and 2n+1 represents dst offset
54  *   paired with the raw offset at 2n.  The very first pair represents
55  *   the initial zone offset (before the first transition) always.
56  *
57  *   - trans:intvector (Optional)
58  *
59  *   List of transition times represented by 32bit seconds from the
60  *   epoch (1970-01-01T00:00Z) in ascending order.
61  *
62  *   - transPre32/transPost32:intvector (Optional)
63  *
64  *   List of transition times before/after 32bit minimum seconds.
65  *   Each time is represented by a pair of 32bit integer.
66  *
67  *   - typeMap:bin (Optional)
68  *
69  *   Array of bytes representing the mapping between each transition
70  *   time (transPre32/trans/transPost32) and its corresponding offset
71  *   data (typeOffsets).
72  *
73  *   - finalRule:string (Optional)
74  *
75  *   If a recurrent transition rule is applicable to a zone forever
76  *   after the final transition time, finalRule represents the rule
77  *   in Rules data.
78  *
79  *   - finalRaw:int (Optional)
80  *
81  *   When finalRule is available, finalRaw is required and specifies
82  *   the raw (base) offset of the rule.
83  *
84  *   - finalYear:int (Optional)
85  *
86  *   When finalRule is available, finalYear is required and specifies
87  *   the start year of the rule.
88  *
89  *   - links:intvector (Optional)
90  *
91  *   When this zone data is shared with other zones, links specifies
92  *   all zones including the zone itself.  Each zone is referenced by
93  *   integer index.
94  *
95  *  b. Link (int, length 1).  A link zone is an int resource.  The
96  *  integer is the zone number of the target zone.  The key of this
97  *  resource is an alternate name for the target zone.  This data
98  *  is corresponding to Link data in the tz database.
99  *
100  *
101  * 2. Rules.  These have keys corresponding to the Olson rule IDs,
102  * with an underscore prepended, e.g., "_EU".  Each resource describes
103  * the behavior of the given rule using an intvector, containing the
104  * onset list, the cessation list, and the DST savings.  The onset and
105  * cessation lists consist of the month, dowim, dow, time, and time
106  * mode.  The end result is that the 11 integers describing the rule
107  * can be passed directly into the SimpleTimeZone 13-argument
108  * constructor (the other two arguments will be the raw offset, taken
109  * from the complex zone element 5, and the ID string, which is not
110  * used), with the times and the DST savings multiplied by 1000 to
111  * scale from seconds to milliseconds.
112  *
113  * 3. Regions.  An array specifies mapping between zones and regions.
114  * Each item is either a 2-letter ISO country code or "001"
115  * (UN M.49 - World).  This data is generated from "zone.tab"
116  * in the tz database.
117  * @hide exposed on OHOS
118  */
119 public class OlsonTimeZone extends BasicTimeZone {
120 
121     // Generated by serialver from JDK 1.4.1_01
122     static final long serialVersionUID = -6281977362477515376L;
123 
124     /* (non-Javadoc)
125      * @see ohos.global.icu.util.TimeZone#getOffset(int, int, int, int, int, int)
126      */
127     @Override
getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds)128     public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) {
129         if (month < Calendar.JANUARY || month > Calendar.DECEMBER) {
130             throw new IllegalArgumentException("Month is not in the legal range: " +month);
131         } else {
132             return getOffset(era, year, month, day, dayOfWeek, milliseconds, Grego.monthLength(year, month));
133         }
134     }
135 
136     /**
137      * TimeZone API.
138      */
getOffset(int era, int year, int month,int dom, int dow, int millis, int monthLength)139     public int getOffset(int era, int year, int month,int dom, int dow, int millis, int monthLength){
140 
141         if ((era != GregorianCalendar.AD && era != GregorianCalendar.BC)
142             || month < Calendar.JANUARY
143             || month > Calendar.DECEMBER
144             || dom < 1
145             || dom > monthLength
146             || dow < Calendar.SUNDAY
147             || dow > Calendar.SATURDAY
148             || millis < 0
149             || millis >= Grego.MILLIS_PER_DAY
150             || monthLength < 28
151             || monthLength > 31) {
152             throw new IllegalArgumentException();
153         }
154 
155         if (era == GregorianCalendar.BC) {
156             year = -year;
157         }
158 
159         if (finalZone != null && year >= finalStartYear) {
160             return finalZone.getOffset(era, year, month, dom, dow, millis);
161         }
162 
163         // Compute local epoch millis from input fields
164         long time = Grego.fieldsToDay(year, month, dom) * Grego.MILLIS_PER_DAY + millis;
165 
166         int[] offsets = new int[2];
167         getHistoricalOffset(time, true, LOCAL_DST, LOCAL_STD, offsets);
168         return offsets[0] + offsets[1];
169     }
170 
171     /* (non-Javadoc)
172      * @see ohos.global.icu.util.TimeZone#setRawOffset(int)
173      */
174     @Override
setRawOffset(int offsetMillis)175     public void setRawOffset(int offsetMillis) {
176         if (isFrozen()) {
177             throw new UnsupportedOperationException("Attempt to modify a frozen OlsonTimeZone instance.");
178         }
179 
180         if (getRawOffset() == offsetMillis) {
181             return;
182         }
183         long current = System.currentTimeMillis();
184 
185         if (current < finalStartMillis) {
186             SimpleTimeZone stz = new SimpleTimeZone(offsetMillis, getID());
187 
188             boolean bDst = useDaylightTime();
189             if (bDst) {
190                 TimeZoneRule[] currentRules = getSimpleTimeZoneRulesNear(current);
191                 if (currentRules.length != 3) {
192                     // DST was observed at the beginning of this year, so useDaylightTime
193                     // returned true.  getSimpleTimeZoneRulesNear requires at least one
194                     // future transition for making a pair of rules.  This implementation
195                     // rolls back the time before the latest offset transition.
196                     TimeZoneTransition tzt = getPreviousTransition(current, false);
197                     if (tzt != null) {
198                         currentRules = getSimpleTimeZoneRulesNear(tzt.getTime() - 1);
199                     }
200                 }
201                 if (currentRules.length == 3
202                         && (currentRules[1] instanceof AnnualTimeZoneRule)
203                         && (currentRules[2] instanceof AnnualTimeZoneRule)) {
204                     // A pair of AnnualTimeZoneRule
205                     AnnualTimeZoneRule r1 = (AnnualTimeZoneRule)currentRules[1];
206                     AnnualTimeZoneRule r2 = (AnnualTimeZoneRule)currentRules[2];
207                     DateTimeRule start, end;
208                     int offset1 = r1.getRawOffset() + r1.getDSTSavings();
209                     int offset2 = r2.getRawOffset() + r2.getDSTSavings();
210                     int sav;
211                     if (offset1 > offset2) {
212                         start = r1.getRule();
213                         end = r2.getRule();
214                         sav = offset1 - offset2;
215                     } else {
216                         start = r2.getRule();
217                         end = r1.getRule();
218                         sav = offset2 - offset1;
219                     }
220                     // getSimpleTimeZoneRulesNear always return rules using DOW / WALL_TIME
221                     stz.setStartRule(start.getRuleMonth(), start.getRuleWeekInMonth(), start.getRuleDayOfWeek(),
222                                             start.getRuleMillisInDay());
223                     stz.setEndRule(end.getRuleMonth(), end.getRuleWeekInMonth(), end.getRuleDayOfWeek(),
224                                             end.getRuleMillisInDay());
225                     // set DST saving amount and start year
226                     stz.setDSTSavings(sav);
227                 } else {
228                     // This could only happen if last rule is DST
229                     // and the rule used forever.  For example, Asia/Dhaka
230                     // in tzdata2009i stays in DST forever.
231 
232                     // Hack - set DST starting at midnight on Jan 1st,
233                     // ending 23:59:59.999 on Dec 31st
234                     stz.setStartRule(0, 1, 0);
235                     stz.setEndRule(11, 31, Grego.MILLIS_PER_DAY - 1);
236                 }
237             }
238 
239             int[] fields = Grego.timeToFields(current, null);
240 
241             finalStartYear = fields[0];
242             finalStartMillis = Grego.fieldsToDay(fields[0], 0, 1);
243 
244             if (bDst) {
245                 // we probably do not need to set start year of final rule
246                 // to finalzone itself, but we always do this for now.
247                 stz.setStartYear(finalStartYear);
248             }
249 
250             finalZone = stz;
251 
252         } else {
253             finalZone.setRawOffset(offsetMillis);
254         }
255 
256         transitionRulesInitialized = false;
257     }
258 
259     @Override
clone()260     public Object clone() {
261         if (isFrozen()) {
262             return this;
263         }
264         return cloneAsThawed();
265     }
266 
267     /**
268      * TimeZone API.
269      */
270     @Override
getOffset(long date, boolean local, int[] offsets)271     public void getOffset(long date, boolean local, int[] offsets)  {
272         if (finalZone != null && date >= finalStartMillis) {
273             finalZone.getOffset(date, local, offsets);
274         } else {
275             getHistoricalOffset(date, local,
276                     LOCAL_FORMER, LOCAL_LATTER, offsets);
277         }
278     }
279 
280     /**
281      * {@inheritDoc}
282      */
283     @Override
getOffsetFromLocal(long date, int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets)284     public void getOffsetFromLocal(long date,
285             int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) {
286         if (finalZone != null && date >= finalStartMillis) {
287             finalZone.getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
288         } else {
289             getHistoricalOffset(date, true, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
290         }
291     }
292 
293     /* (non-Javadoc)
294      * @see ohos.global.icu.util.TimeZone#getRawOffset()
295      */
296     @Override
getRawOffset()297     public int getRawOffset() {
298         int[] ret = new int[2];
299         getOffset(System.currentTimeMillis(), false, ret);
300         return ret[0];
301     }
302 
303     /* (non-Javadoc)
304      * @see ohos.global.icu.util.TimeZone#useDaylightTime()
305      */
306     @Override
useDaylightTime()307     public boolean useDaylightTime() {
308         // If DST was observed in 1942 (for example) but has never been
309         // observed from 1943 to the present, most clients will expect
310         // this method to return FALSE.  This method determines whether
311         // DST is in use in the current year (at any point in the year)
312         // and returns TRUE if so.
313         long current = System.currentTimeMillis();
314 
315         if (finalZone != null && current >= finalStartMillis) {
316             return (finalZone != null && finalZone.useDaylightTime());
317         }
318 
319         int[] fields = Grego.timeToFields(current, null);
320 
321         // Find start of this year, and start of next year
322         long start = Grego.fieldsToDay(fields[0], 0, 1) * SECONDS_PER_DAY;
323         long limit = Grego.fieldsToDay(fields[0] + 1, 0, 1) * SECONDS_PER_DAY;
324 
325         // Return TRUE if DST is observed at any time during the current
326         // year.
327         for (int i = 0; i < transitionCount; ++i) {
328             if (transitionTimes64[i] >= limit) {
329                 break;
330             }
331             if ((transitionTimes64[i] >= start && dstOffsetAt(i) != 0)
332                     || (transitionTimes64[i] > start && i > 0 && dstOffsetAt(i - 1) != 0)) {
333                 return true;
334             }
335         }
336         return false;
337     }
338 
339     /* (non-Javadoc)
340      * @see ohos.global.icu.util.TimeZone#observesDaylightTime()
341      */
342     @Override
observesDaylightTime()343     public boolean observesDaylightTime() {
344         long current = System.currentTimeMillis();
345 
346         if (finalZone != null) {
347             if (finalZone.useDaylightTime()) {
348                 return true;
349             } else if (current >= finalStartMillis) {
350                 return false;
351             }
352         }
353 
354         // Return TRUE if DST is observed at any future time
355         long currentSec = Grego.floorDivide(current, Grego.MILLIS_PER_SECOND);
356         int trsIdx = transitionCount - 1;
357         if (dstOffsetAt(trsIdx) != 0) {
358             return true;
359         }
360         while (trsIdx >= 0) {
361             if (transitionTimes64[trsIdx] <= currentSec) {
362                 break;
363             }
364             if (dstOffsetAt(trsIdx - 1) != 0) {
365                 return true;
366             }
367             trsIdx--;
368         }
369         return false;
370     }
371     /**
372      * TimeZone API
373      * Returns the amount of time to be added to local standard time
374      * to get local wall clock time.
375      */
376     @Override
getDSTSavings()377     public int getDSTSavings() {
378         if (finalZone != null){
379             return finalZone.getDSTSavings();
380         }
381         return super.getDSTSavings();
382     }
383 
384     /* (non-Javadoc)
385      * @see ohos.global.icu.util.TimeZone#inDaylightTime(java.util.Date)
386      */
387     @Override
inDaylightTime(Date date)388     public boolean inDaylightTime(Date date) {
389         int[] temp = new int[2];
390         getOffset(date.getTime(), false, temp);
391         return temp[1] != 0;
392     }
393 
394     /* (non-Javadoc)
395      * @see ohos.global.icu.util.TimeZone#hasSameRules(ohos.global.icu.util.TimeZone)
396      */
397     @Override
hasSameRules(TimeZone other)398     public boolean hasSameRules(TimeZone other) {
399         if (this == other) {
400             return true;
401         }
402         // The super class implementation only check raw offset and
403         // use of daylight saving time.
404         if (!super.hasSameRules(other)) {
405             return false;
406         }
407 
408         if (!(other instanceof OlsonTimeZone)) {
409             // We cannot reasonably compare rules in different types
410             return false;
411         }
412 
413         // Check final zone
414         OlsonTimeZone o = (OlsonTimeZone)other;
415         if (finalZone == null) {
416             if (o.finalZone != null) {
417                 return false;
418             }
419         } else {
420             if (o.finalZone == null
421                     || finalStartYear != o.finalStartYear
422                     || !(finalZone.hasSameRules(o.finalZone))) {
423                 return false;
424             }
425         }
426         // Check transitions
427         // Note: The code below actually fails to compare two equivalent rules in
428         // different representation properly.
429         if (transitionCount != o.transitionCount ||
430                 !Arrays.equals(transitionTimes64, o.transitionTimes64) ||
431                 typeCount != o.typeCount ||
432                 !Arrays.equals(typeMapData, o.typeMapData) ||
433                 !Arrays.equals(typeOffsets, o.typeOffsets)){
434             return false;
435         }
436         return true;
437     }
438 
439     /**
440      * Returns the canonical ID of this system time zone
441      */
getCanonicalID()442     public String getCanonicalID() {
443         if (canonicalID == null) {
444             synchronized(this) {
445                 if (canonicalID == null) {
446                     canonicalID = getCanonicalID(getID());
447 
448                     assert(canonicalID != null);
449                     if (canonicalID == null) {
450                         // This should never happen...
451                         canonicalID = getID();
452                     }
453                 }
454             }
455         }
456         return canonicalID;
457     }
458 
459     /**
460      * Construct a GMT+0 zone with no transitions.  This is done when a
461      * constructor fails so the resultant object is well-behaved.
462      */
constructEmpty()463     private void constructEmpty(){
464         transitionCount = 0;
465         transitionTimes64 = null;
466         typeMapData =  null;
467 
468         typeCount = 1;
469         typeOffsets = new int[]{0,0};
470         finalZone = null;
471         finalStartYear = Integer.MAX_VALUE;
472         finalStartMillis = Double.MAX_VALUE;
473 
474         transitionRulesInitialized = false;
475     }
476 
477     /**
478      * Construct from a resource bundle
479      * @param top the top-level zoneinfo resource bundle.  This is used
480      * to lookup the rule that `res' may refer to, if there is one.
481      * @param res the resource bundle of the zone to be constructed
482      * @param id time zone ID
483      */
OlsonTimeZone(UResourceBundle top, UResourceBundle res, String id)484     public OlsonTimeZone(UResourceBundle top, UResourceBundle res, String id){
485         super(id);
486         construct(top, res, id);
487     }
488 
construct(UResourceBundle top, UResourceBundle res, String id)489     private void construct(UResourceBundle top, UResourceBundle res, String id){
490 
491         if ((top == null || res == null)) {
492             throw new IllegalArgumentException();
493         }
494         if(DEBUG) System.out.println("OlsonTimeZone(" + res.getKey() +")");
495 
496         UResourceBundle r;
497         int[] transPre32, trans32, transPost32;
498         transPre32 = trans32 = transPost32 = null;
499 
500         transitionCount = 0;
501 
502         // Pre-32bit second transitions
503         try {
504             r = res.get("transPre32");
505             transPre32 = r.getIntVector();
506             if (transPre32.length % 2 != 0) {
507                 // elements in the pre-32bit must be an even number
508                 throw new IllegalArgumentException("Invalid Format");
509             }
510             transitionCount += transPre32.length / 2;
511         } catch (MissingResourceException e) {
512             // Pre-32bit transition data is optional
513         }
514 
515         // 32bit second transitions
516         try {
517             r = res.get("trans");
518             trans32 = r.getIntVector();
519             transitionCount += trans32.length;
520         } catch (MissingResourceException e) {
521             // 32bit transition data is optional
522         }
523 
524         // Post-32bit second transitions
525         try {
526             r = res.get("transPost32");
527             transPost32 = r.getIntVector();
528             if (transPost32.length % 2 != 0) {
529                 // elements in the post-32bit must be an even number
530                 throw new IllegalArgumentException("Invalid Format");
531             }
532             transitionCount += transPost32.length / 2;
533         } catch (MissingResourceException e) {
534             // Post-32bit transition data is optional
535         }
536 
537         if (transitionCount > 0) {
538             transitionTimes64 = new long[transitionCount];
539             int idx = 0;
540             if (transPre32 != null) {
541                 for (int i = 0; i < transPre32.length / 2; i++, idx++) {
542                     transitionTimes64[idx] =
543                         ((transPre32[i * 2]) & 0x00000000FFFFFFFFL) << 32
544                         | ((transPre32[i * 2 + 1]) & 0x00000000FFFFFFFFL);
545                 }
546             }
547             if (trans32 != null) {
548                 for (int i = 0; i < trans32.length; i++, idx++) {
549                     transitionTimes64[idx] = trans32[i];
550                 }
551             }
552             if (transPost32 != null) {
553                 for (int i = 0; i < transPost32.length / 2; i++, idx++) {
554                     transitionTimes64[idx] =
555                         ((transPost32[i * 2]) & 0x00000000FFFFFFFFL) << 32
556                         | ((transPost32[i * 2 + 1]) & 0x00000000FFFFFFFFL);
557                 }
558             }
559         } else {
560             transitionTimes64 = null;
561         }
562 
563         // Type offsets list must be of even size, with size >= 2
564         r = res.get("typeOffsets");
565         typeOffsets = r.getIntVector();
566         if ((typeOffsets.length < 2 || typeOffsets.length > 0x7FFE || typeOffsets.length % 2 != 0)) {
567             throw new IllegalArgumentException("Invalid Format");
568         }
569         typeCount = typeOffsets.length / 2;
570 
571         // Type map data must be of the same size as the transition count
572         if (transitionCount > 0) {
573             r = res.get("typeMap");
574             typeMapData = r.getBinary(null);
575             if (typeMapData == null || typeMapData.length != transitionCount) {
576                 throw new IllegalArgumentException("Invalid Format");
577             }
578         } else {
579             typeMapData = null;
580         }
581 
582         // Process final rule and data, if any
583         finalZone = null;
584         finalStartYear = Integer.MAX_VALUE;
585         finalStartMillis = Double.MAX_VALUE;
586 
587         String ruleID = null;
588         try {
589             ruleID = res.getString("finalRule");
590 
591             r = res.get("finalRaw");
592             int ruleRaw = r.getInt() * Grego.MILLIS_PER_SECOND;
593             r = loadRule(top, ruleID);
594             int[] ruleData = r.getIntVector();
595 
596             if (ruleData == null || ruleData.length != 11) {
597                 throw new IllegalArgumentException("Invalid Format");
598             }
599             finalZone = new SimpleTimeZone(ruleRaw, id,
600                     ruleData[0], ruleData[1], ruleData[2],
601                     ruleData[3] * Grego.MILLIS_PER_SECOND,
602                     ruleData[4],
603                     ruleData[5], ruleData[6], ruleData[7],
604                     ruleData[8] * Grego.MILLIS_PER_SECOND,
605                     ruleData[9],
606                     ruleData[10] * Grego.MILLIS_PER_SECOND);
607 
608             r = res.get("finalYear");
609             finalStartYear = r.getInt();
610 
611             // Note: Setting finalStartYear to the finalZone is problematic.  When a date is around
612             // year boundary, SimpleTimeZone may return false result when DST is observed at the
613             // beginning of year.  We could apply safe margin (day or two), but when one of recurrent
614             // rules falls around year boundary, it could return false result.  Without setting the
615             // start year, finalZone works fine around the year boundary of the start year.
616 
617             // finalZone.setStartYear(finalStartYear);
618 
619             // Compute the millis for Jan 1, 0:00 GMT of the finalYear
620 
621             // Note: finalStartMillis is used for detecting either if
622             // historic transition data or finalZone to be used.  In an
623             // extreme edge case - for example, two transitions fall into
624             // small windows of time around the year boundary, this may
625             // result incorrect offset computation.  But I think it will
626             // never happen practically.  Yoshito - Feb 20, 2010
627             finalStartMillis = Grego.fieldsToDay(finalStartYear, 0, 1) * Grego.MILLIS_PER_DAY;
628         } catch (MissingResourceException e) {
629             if (ruleID != null) {
630                 // ruleID is found, but missing other data required for
631                 // creating finalZone
632                 throw new IllegalArgumentException("Invalid Format");
633             }
634         }
635     }
636 
637     // This constructor is used for testing purpose only
OlsonTimeZone(String id)638     public OlsonTimeZone(String id){
639         super(id);
640         UResourceBundle top = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME,
641                 ZONEINFORES, ICUResourceBundle.ICU_DATA_CLASS_LOADER);
642         UResourceBundle res = ZoneMeta.openOlsonResource(top, id);
643         construct(top, res, id);
644     }
645 
646     /* (non-Javadoc)
647      * @see ohos.global.icu.util.TimeZone#setID(java.lang.String)
648      */
649     @Override
setID(String id)650     public void setID(String id){
651         if (isFrozen()) {
652             throw new UnsupportedOperationException("Attempt to modify a frozen OlsonTimeZone instance.");
653         }
654 
655         // Before updating the ID, preserve the original ID's canonical ID.
656         if (canonicalID == null) {
657             canonicalID = getCanonicalID(getID());
658             assert(canonicalID != null);
659             if (canonicalID == null) {
660                 // This should never happen...
661                 canonicalID = getID();
662             }
663         }
664 
665         if (finalZone != null){
666             finalZone.setID(id);
667         }
668         super.setID(id);
669         transitionRulesInitialized = false;
670     }
671 
672     // Maximum absolute offset in seconds = 1 day.
673     // getHistoricalOffset uses this constant as safety margin of
674     // quick zone transition checking.
675     private static final int MAX_OFFSET_SECONDS = 86400; // 60 * 60 * 24;
676 
getHistoricalOffset(long date, boolean local, int NonExistingTimeOpt, int DuplicatedTimeOpt, int[] offsets)677     private void getHistoricalOffset(long date, boolean local,
678             int NonExistingTimeOpt, int DuplicatedTimeOpt, int[] offsets) {
679         if (transitionCount != 0) {
680             long sec = Grego.floorDivide(date, Grego.MILLIS_PER_SECOND);
681             if (!local && sec < transitionTimes64[0]) {
682                 // Before the first transition time
683                 offsets[0] = initialRawOffset() * Grego.MILLIS_PER_SECOND;
684                 offsets[1] = initialDstOffset() * Grego.MILLIS_PER_SECOND;
685             } else {
686                 // Linear search from the end is the fastest approach, since
687                 // most lookups will happen at/near the end.
688                 int transIdx;
689                 for (transIdx = transitionCount - 1; transIdx >= 0; transIdx--) {
690                     long transition = transitionTimes64[transIdx];
691                     if (local && (sec >= (transition - MAX_OFFSET_SECONDS))) {
692                         int offsetBefore = zoneOffsetAt(transIdx - 1);
693                         boolean dstBefore = dstOffsetAt(transIdx - 1) != 0;
694 
695                         int offsetAfter = zoneOffsetAt(transIdx);
696                         boolean dstAfter = dstOffsetAt(transIdx) != 0;
697 
698                         boolean dstToStd = dstBefore && !dstAfter;
699                         boolean stdToDst = !dstBefore && dstAfter;
700 
701                         if (offsetAfter - offsetBefore >= 0) {
702                             // Positive transition, which makes a non-existing local time range
703                             if (((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_STD && dstToStd)
704                                     || ((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_DST && stdToDst)) {
705                                 transition += offsetBefore;
706                             } else if (((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_STD && stdToDst)
707                                     || ((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_DST && dstToStd)) {
708                                 transition += offsetAfter;
709                             } else if ((NonExistingTimeOpt & FORMER_LATTER_MASK) == LOCAL_LATTER) {
710                                 transition += offsetBefore;
711                             } else {
712                                 // Interprets the time with rule before the transition,
713                                 // default for non-existing time range
714                                 transition += offsetAfter;
715                             }
716                         } else {
717                             // Negative transition, which makes a duplicated local time range
718                             if (((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_STD && dstToStd)
719                                     || ((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_DST && stdToDst)) {
720                                 transition += offsetAfter;
721                             } else if (((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_STD && stdToDst)
722                                     || ((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_DST && dstToStd)) {
723                                 transition += offsetBefore;
724                             } else if ((DuplicatedTimeOpt & FORMER_LATTER_MASK) == LOCAL_FORMER) {
725                                 transition += offsetBefore;
726                             } else {
727                                 // Interprets the time with rule after the transition,
728                                 // default for duplicated local time range
729                                 transition += offsetAfter;
730                             }
731                         }
732                     }
733                     if (sec >= transition) {
734                         break;
735                     }
736                 }
737                 // transIdx could be -1 when local=true
738                 offsets[0] = rawOffsetAt(transIdx) * Grego.MILLIS_PER_SECOND;
739                 offsets[1] = dstOffsetAt(transIdx) * Grego.MILLIS_PER_SECOND;
740             }
741         } else {
742             // No transitions, single pair of offsets only
743             offsets[0] = initialRawOffset() * Grego.MILLIS_PER_SECOND;
744             offsets[1] = initialDstOffset() * Grego.MILLIS_PER_SECOND;
745         }
746     }
747 
getInt(byte val)748     private int getInt(byte val){
749         return val & 0xFF;
750     }
751 
752     /*
753      * Following 3 methods return an offset at the given transition time index.
754      * When the index is negative, return the initial offset.
755      */
zoneOffsetAt(int transIdx)756     private int zoneOffsetAt(int transIdx) {
757         int typeIdx = transIdx >= 0 ? getInt(typeMapData[transIdx]) * 2 : 0;
758         return typeOffsets[typeIdx] + typeOffsets[typeIdx + 1];
759     }
760 
rawOffsetAt(int transIdx)761     private int rawOffsetAt(int transIdx) {
762         int typeIdx = transIdx >= 0 ? getInt(typeMapData[transIdx]) * 2 : 0;
763         return typeOffsets[typeIdx];
764     }
765 
dstOffsetAt(int transIdx)766     private int dstOffsetAt(int transIdx) {
767         int typeIdx = transIdx >= 0 ? getInt(typeMapData[transIdx]) * 2 : 0;
768         return typeOffsets[typeIdx + 1];
769     }
770 
initialRawOffset()771     private int initialRawOffset() {
772         return typeOffsets[0];
773     }
774 
initialDstOffset()775     private int initialDstOffset() {
776         return typeOffsets[1];
777     }
778 
779     // temp
780     @Override
toString()781     public String toString() {
782         StringBuilder buf = new StringBuilder();
783         buf.append(super.toString());
784         buf.append('[');
785         buf.append("transitionCount=" + transitionCount);
786         buf.append(",typeCount=" + typeCount);
787         buf.append(",transitionTimes=");
788         if (transitionTimes64 != null) {
789             buf.append('[');
790             for (int i = 0; i < transitionTimes64.length; ++i) {
791                 if (i > 0) {
792                     buf.append(',');
793                 }
794                 buf.append(Long.toString(transitionTimes64[i]));
795             }
796             buf.append(']');
797         } else {
798             buf.append("null");
799         }
800         buf.append(",typeOffsets=");
801         if (typeOffsets != null) {
802             buf.append('[');
803             for (int i = 0; i < typeOffsets.length; ++i) {
804                 if (i > 0) {
805                     buf.append(',');
806                 }
807                 buf.append(Integer.toString(typeOffsets[i]));
808             }
809             buf.append(']');
810         } else {
811             buf.append("null");
812         }
813         buf.append(",typeMapData=");
814         if (typeMapData != null) {
815             buf.append('[');
816             for (int i = 0; i < typeMapData.length; ++i) {
817                 if (i > 0) {
818                     buf.append(',');
819                 }
820                 buf.append(Byte.toString(typeMapData[i]));
821             }
822         } else {
823             buf.append("null");
824         }
825         buf.append(",finalStartYear=" + finalStartYear);
826         buf.append(",finalStartMillis=" + finalStartMillis);
827         buf.append(",finalZone=" + finalZone);
828         buf.append(']');
829 
830         return buf.toString();
831     }
832 
833     /**
834      * Number of transitions, 0..~370
835      */
836     private int transitionCount;
837 
838     /**
839      * Number of types, 1..255
840      */
841     private int typeCount;
842 
843     /**
844      * Time of each transition in seconds from 1970 epoch.
845      */
846     private long[] transitionTimes64;
847 
848     /**
849      * Offset from GMT in seconds for each type.
850      * Length is equal to typeCount
851      */
852     private int[] typeOffsets;
853 
854     /**
855      * Type description data, consisting of transitionCount uint8_t
856      * type indices (from 0..typeCount-1).
857      * Length is equal to transitionCount
858      */
859     private byte[] typeMapData;
860 
861     /**
862      * For year >= finalStartYear, the finalZone will be used.
863      */
864     private int finalStartYear = Integer.MAX_VALUE;
865 
866     /**
867      * For date >= finalStartMillis, the finalZone will be used.
868      */
869     private double finalStartMillis = Double.MAX_VALUE;
870 
871     /**
872      * A SimpleTimeZone that governs the behavior for years >= finalYear.
873      * If and only if finalYear == INT32_MAX then finalZone == 0.
874      */
875     private SimpleTimeZone finalZone = null; // owned, may be NULL
876 
877     /**
878      * The canonical ID of this zone. Initialized when {@link #getCanonicalID()}
879      * is invoked first time, or {@link #setID(String)} is called.
880      */
881     private volatile String canonicalID = null;
882 
883     private static final String ZONEINFORES = "zoneinfo64";
884 
885     private static final boolean DEBUG = ICUDebug.enabled("olson");
886     private static final int SECONDS_PER_DAY = 24*60*60;
887 
loadRule(UResourceBundle top, String ruleid)888     private static UResourceBundle loadRule(UResourceBundle top, String ruleid) {
889         UResourceBundle r = top.get("Rules");
890         r = r.get(ruleid);
891         return r;
892     }
893 
894     @Override
equals(Object obj)895     public boolean equals(Object obj){
896         if (!super.equals(obj)) return false; // super does class check
897 
898         OlsonTimeZone z = (OlsonTimeZone) obj;
899 
900         return (Utility.arrayEquals(typeMapData, z.typeMapData) ||
901                  // If the pointers are not equal, the zones may still
902                  // be equal if their rules and transitions are equal
903                  (finalStartYear == z.finalStartYear &&
904                   // Don't compare finalMillis; if finalYear is ==, so is finalMillis
905                   ((finalZone == null && z.finalZone == null) ||
906                    (finalZone != null && z.finalZone != null &&
907                     finalZone.equals(z.finalZone)) &&
908                   transitionCount == z.transitionCount &&
909                   typeCount == z.typeCount &&
910                   Utility.arrayEquals(transitionTimes64, z.transitionTimes64) &&
911                   Utility.arrayEquals(typeOffsets, z.typeOffsets) &&
912                   Utility.arrayEquals(typeMapData, z.typeMapData)
913                   )));
914 
915     }
916 
917     @Override
hashCode()918     public int hashCode(){
919         int ret =   (int)  (finalStartYear ^ (finalStartYear>>>4) +
920                    transitionCount ^ (transitionCount>>>6) +
921                    typeCount ^ (typeCount>>>8) +
922                    Double.doubleToLongBits(finalStartMillis)+
923                    (finalZone == null ? 0 : finalZone.hashCode()) +
924                    super.hashCode());
925         if (transitionTimes64 != null) {
926             for(int i=0; i<transitionTimes64.length; i++){
927                 ret+=transitionTimes64[i]^(transitionTimes64[i]>>>8);
928             }
929         }
930         for(int i=0; i<typeOffsets.length; i++){
931             ret+=typeOffsets[i]^(typeOffsets[i]>>>8);
932         }
933         if (typeMapData != null) {
934             for(int i=0; i<typeMapData.length; i++){
935                 ret+=typeMapData[i] & 0xFF;
936             }
937         }
938         return ret;
939     }
940 
941     //
942     // BasicTimeZone methods
943     //
944 
945     /* (non-Javadoc)
946      * @see ohos.global.icu.util.BasicTimeZone#getNextTransition(long, boolean)
947      */
948     @Override
getNextTransition(long base, boolean inclusive)949     public TimeZoneTransition getNextTransition(long base, boolean inclusive) {
950         initTransitionRules();
951 
952         if (finalZone != null) {
953             if (inclusive && base == firstFinalTZTransition.getTime()) {
954                 return firstFinalTZTransition;
955             } else if (base >= firstFinalTZTransition.getTime()) {
956                 if (finalZone.useDaylightTime()) {
957                     //return finalZone.getNextTransition(base, inclusive);
958                     return finalZoneWithStartYear.getNextTransition(base, inclusive);
959                 } else {
960                     // No more transitions
961                     return null;
962                 }
963             }
964         }
965         if (historicRules != null) {
966             // Find a historical transition
967             int ttidx = transitionCount - 1;
968             for (; ttidx >= firstTZTransitionIdx; ttidx--) {
969                 long t = transitionTimes64[ttidx] * Grego.MILLIS_PER_SECOND;
970                 if (base > t || (!inclusive && base == t)) {
971                     break;
972                 }
973             }
974             if (ttidx == transitionCount - 1)  {
975                 return firstFinalTZTransition;
976             } else if (ttidx < firstTZTransitionIdx) {
977                 return firstTZTransition;
978             } else {
979                 // Create a TimeZoneTransition
980                 TimeZoneRule to = historicRules[getInt(typeMapData[ttidx + 1])];
981                 TimeZoneRule from = historicRules[getInt(typeMapData[ttidx])];
982                 long startTime = transitionTimes64[ttidx+1] * Grego.MILLIS_PER_SECOND;
983 
984                 // The transitions loaded from zoneinfo.res may contain non-transition data
985                 if (from.getName().equals(to.getName()) && from.getRawOffset() == to.getRawOffset()
986                         && from.getDSTSavings() == to.getDSTSavings()) {
987                     return getNextTransition(startTime, false);
988                 }
989 
990                 return new TimeZoneTransition(startTime, from, to);
991             }
992         }
993         return null;
994     }
995 
996     /* (non-Javadoc)
997      * @see ohos.global.icu.util.BasicTimeZone#getPreviousTransition(long, boolean)
998      */
999     @Override
getPreviousTransition(long base, boolean inclusive)1000     public TimeZoneTransition getPreviousTransition(long base, boolean inclusive) {
1001         initTransitionRules();
1002 
1003         if (finalZone != null) {
1004             if (inclusive && base == firstFinalTZTransition.getTime()) {
1005                 return firstFinalTZTransition;
1006             } else if (base > firstFinalTZTransition.getTime()) {
1007                 if (finalZone.useDaylightTime()) {
1008                     //return finalZone.getPreviousTransition(base, inclusive);
1009                     return finalZoneWithStartYear.getPreviousTransition(base, inclusive);
1010                 } else {
1011                     return firstFinalTZTransition;
1012                 }
1013             }
1014         }
1015 
1016         if (historicRules != null) {
1017             // Find a historical transition
1018             int ttidx = transitionCount - 1;
1019             for (; ttidx >= firstTZTransitionIdx; ttidx--) {
1020                 long t = transitionTimes64[ttidx] * Grego.MILLIS_PER_SECOND;
1021                 if (base > t || (inclusive && base == t)) {
1022                     break;
1023                 }
1024             }
1025             if (ttidx < firstTZTransitionIdx) {
1026                 // No more transitions
1027                 return null;
1028             } else if (ttidx == firstTZTransitionIdx) {
1029                 return firstTZTransition;
1030             } else {
1031                 // Create a TimeZoneTransition
1032                 TimeZoneRule to = historicRules[getInt(typeMapData[ttidx])];
1033                 TimeZoneRule from = historicRules[getInt(typeMapData[ttidx-1])];
1034                 long startTime = transitionTimes64[ttidx] * Grego.MILLIS_PER_SECOND;
1035 
1036                 // The transitions loaded from zoneinfo.res may contain non-transition data
1037                 if (from.getName().equals(to.getName()) && from.getRawOffset() == to.getRawOffset()
1038                         && from.getDSTSavings() == to.getDSTSavings()) {
1039                     return getPreviousTransition(startTime, false);
1040                 }
1041 
1042                 return new TimeZoneTransition(startTime, from, to);
1043             }
1044         }
1045         return null;
1046     }
1047 
1048     /* (non-Javadoc)
1049      * @see ohos.global.icu.util.BasicTimeZone#getTimeZoneRules()
1050      */
1051     @Override
getTimeZoneRules()1052     public TimeZoneRule[] getTimeZoneRules() {
1053         initTransitionRules();
1054         int size = 1;
1055         if (historicRules != null) {
1056             // historicRules may contain null entries when original zoneinfo data
1057             // includes non transition data.
1058             for (int i = 0; i < historicRules.length; i++) {
1059                 if (historicRules[i] != null) {
1060                     size++;
1061                 }
1062             }
1063         }
1064         if (finalZone != null) {
1065             if (finalZone.useDaylightTime()) {
1066                 size += 2;
1067             } else {
1068                 size++;
1069             }
1070         }
1071 
1072         TimeZoneRule[] rules = new TimeZoneRule[size];
1073         int idx = 0;
1074         rules[idx++] = initialRule;
1075 
1076         if (historicRules != null) {
1077             for (int i = 0; i < historicRules.length; i++) {
1078                 if (historicRules[i] != null) {
1079                     rules[idx++] = historicRules[i];
1080                 }
1081             }
1082          }
1083 
1084         if (finalZone != null) {
1085             if (finalZone.useDaylightTime()) {
1086                 TimeZoneRule[] stzr = finalZoneWithStartYear.getTimeZoneRules();
1087                 // Adding only transition rules
1088                 rules[idx++] = stzr[1];
1089                 rules[idx++] = stzr[2];
1090             } else {
1091                 // Create a TimeArrayTimeZoneRule at finalMillis
1092                 rules[idx++] = new TimeArrayTimeZoneRule(getID() + "(STD)", finalZone.getRawOffset(), 0,
1093                         new long[] {(long)finalStartMillis}, DateTimeRule.UTC_TIME);
1094             }
1095         }
1096         return rules;
1097     }
1098 
1099     private transient InitialTimeZoneRule initialRule;
1100     private transient TimeZoneTransition firstTZTransition;
1101     private transient int firstTZTransitionIdx;
1102     private transient TimeZoneTransition firstFinalTZTransition;
1103     private transient TimeArrayTimeZoneRule[] historicRules;
1104     private transient SimpleTimeZone finalZoneWithStartYear; // hack
1105 
1106     private transient boolean transitionRulesInitialized;
1107 
initTransitionRules()1108     private synchronized void initTransitionRules() {
1109         if (transitionRulesInitialized) {
1110             return;
1111         }
1112 
1113         initialRule = null;
1114         firstTZTransition = null;
1115         firstFinalTZTransition = null;
1116         historicRules = null;
1117         firstTZTransitionIdx = 0;
1118         finalZoneWithStartYear = null;
1119 
1120         String stdName = getID() + "(STD)";
1121         String dstName = getID() + "(DST)";
1122 
1123         int raw, dst;
1124 
1125         // Create initial rule
1126         raw = initialRawOffset() * Grego.MILLIS_PER_SECOND;
1127         dst = initialDstOffset() * Grego.MILLIS_PER_SECOND;
1128         initialRule = new InitialTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst);
1129 
1130         if (transitionCount > 0) {
1131             int transitionIdx, typeIdx;
1132 
1133             // We probably no longer need to check the first "real" transition
1134             // here, because the new tzcode remove such transitions already.
1135             // For now, keeping this code for just in case. Feb 19, 2010 Yoshito
1136             for (transitionIdx = 0; transitionIdx < transitionCount; transitionIdx++) {
1137                 if (getInt(typeMapData[transitionIdx]) != 0) { // type 0 is the initial type
1138                     break;
1139                 }
1140                 firstTZTransitionIdx++;
1141             }
1142             if (transitionIdx == transitionCount) {
1143                 // Actually no transitions...
1144             } else {
1145                 // Build historic rule array
1146                 long[] times = new long[transitionCount];
1147                 for (typeIdx = 0; typeIdx < typeCount; typeIdx++) {
1148                     // Gather all start times for each pair of offsets
1149                     int nTimes = 0;
1150                     for (transitionIdx = firstTZTransitionIdx; transitionIdx < transitionCount; transitionIdx++) {
1151                         if (typeIdx == getInt(typeMapData[transitionIdx])) {
1152                             long tt = transitionTimes64[transitionIdx] * Grego.MILLIS_PER_SECOND;
1153                             if (tt < finalStartMillis) {
1154                                 // Exclude transitions after finalMillis
1155                                 times[nTimes++] = tt;
1156                             }
1157                         }
1158                     }
1159                     if (nTimes > 0) {
1160                         long[] startTimes = new long[nTimes];
1161                         System.arraycopy(times, 0, startTimes, 0, nTimes);
1162                         // Create a TimeArrayTimeZoneRule
1163                         raw = typeOffsets[typeIdx*2]*Grego.MILLIS_PER_SECOND;
1164                         dst = typeOffsets[typeIdx*2 + 1]*Grego.MILLIS_PER_SECOND;
1165                         if (historicRules == null) {
1166                             historicRules = new TimeArrayTimeZoneRule[typeCount];
1167                         }
1168                         historicRules[typeIdx] = new TimeArrayTimeZoneRule((dst == 0 ? stdName : dstName),
1169                                 raw, dst, startTimes, DateTimeRule.UTC_TIME);
1170                     }
1171                 }
1172 
1173                 // Create initial transition
1174                 typeIdx = getInt(typeMapData[firstTZTransitionIdx]);
1175                 firstTZTransition = new TimeZoneTransition(transitionTimes64[firstTZTransitionIdx] * Grego.MILLIS_PER_SECOND,
1176                         initialRule, historicRules[typeIdx]);
1177 
1178             }
1179         }
1180 
1181         if (finalZone != null) {
1182             // Get the first occurrence of final rule starts
1183             long startTime = (long)finalStartMillis;
1184             TimeZoneRule firstFinalRule;
1185             if (finalZone.useDaylightTime()) {
1186                 /*
1187                  * Note: When an OlsonTimeZone is constructed, we should set the final year
1188                  * as the start year of finalZone.  However, the boundary condition used for
1189                  * getting offset from finalZone has some problems.  So setting the start year
1190                  * in the finalZone will cause a problem.  For now, we do not set the valid
1191                  * start year when the construction time and create a clone and set the
1192                  * start year when extracting rules.
1193                  */
1194                 finalZoneWithStartYear = (SimpleTimeZone)finalZone.clone();
1195                 finalZoneWithStartYear.setStartYear(finalStartYear);
1196 
1197                 TimeZoneTransition tzt = finalZoneWithStartYear.getNextTransition(startTime, false);
1198                 firstFinalRule  = tzt.getTo();
1199                 startTime = tzt.getTime();
1200             } else {
1201                 finalZoneWithStartYear = finalZone;
1202                 firstFinalRule = new TimeArrayTimeZoneRule(finalZone.getID(),
1203                         finalZone.getRawOffset(), 0, new long[] {startTime}, DateTimeRule.UTC_TIME);
1204             }
1205             TimeZoneRule prevRule = null;
1206             if (transitionCount > 0) {
1207                 prevRule = historicRules[getInt(typeMapData[transitionCount - 1])];
1208             }
1209             if (prevRule == null) {
1210                 // No historic transitions, but only finalZone available
1211                 prevRule = initialRule;
1212             }
1213             firstFinalTZTransition = new TimeZoneTransition(startTime, prevRule, firstFinalRule);
1214         }
1215 
1216         transitionRulesInitialized = true;
1217     }
1218 
1219     // Note: This class does not support back level serialization compatibility
1220     // very well.  ICU 4.4 introduced the 64bit transition data.  It is probably
1221     // possible to implement this class to make old version of ICU to deserialize
1222     // object stream serialized by ICU 4.4+.  However, such implementation will
1223     // introduce unnecessary complexity other than serialization support.
1224     // I decided to provide minimum level of backward compatibility, which
1225     // only support ICU 4.4+ to create an instance of OlsonTimeZone by reloading
1226     // the zone rules from bundles.  ICU 4.2 or older version of ICU cannot
1227     // deserialize object stream created by ICU 4.4+.  Yoshito -Feb 22, 2010
1228 
1229     private static final int currentSerialVersion = 1;
1230     private int serialVersionOnStream = currentSerialVersion;
1231 
readObject(ObjectInputStream stream)1232     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
1233         stream.defaultReadObject();
1234 
1235         if (serialVersionOnStream < 1) {
1236             // No version - 4.2 or older
1237             // Just reloading the rule from bundle
1238             boolean initialized = false;
1239             String tzid = getID();
1240             if (tzid != null) {
1241                 try {
1242                     UResourceBundle top = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME,
1243                             ZONEINFORES, ICUResourceBundle.ICU_DATA_CLASS_LOADER);
1244                     UResourceBundle res = ZoneMeta.openOlsonResource(top, tzid);
1245                     construct(top, res, tzid);
1246                     initialized = true;
1247                 } catch (Exception ignored) {
1248                     // throw away
1249                 }
1250             }
1251             if (!initialized) {
1252                 // final resort
1253                 constructEmpty();
1254             }
1255         }
1256 
1257         // need to rebuild transition rules when requested
1258         transitionRulesInitialized = false;
1259     }
1260 
1261     // Freezable stuffs
1262     private transient volatile boolean isFrozen = false;
1263 
1264     /* (non-Javadoc)
1265      * @see ohos.global.icu.util.TimeZone#isFrozen()
1266      */
1267     @Override
isFrozen()1268     public boolean isFrozen() {
1269         return isFrozen;
1270     }
1271 
1272     /* (non-Javadoc)
1273      * @see ohos.global.icu.util.TimeZone#freeze()
1274      */
1275     @Override
freeze()1276     public TimeZone freeze() {
1277         isFrozen = true;
1278         return this;
1279     }
1280 
1281     /* (non-Javadoc)
1282      * @see ohos.global.icu.util.TimeZone#cloneAsThawed()
1283      */
1284     @Override
cloneAsThawed()1285     public TimeZone cloneAsThawed() {
1286         OlsonTimeZone tz = (OlsonTimeZone)super.cloneAsThawed();
1287         if (finalZone != null) {
1288             tz.finalZone = (SimpleTimeZone) finalZone.clone();
1289         }
1290 
1291         // Following data are read-only and never changed.
1292         // Therefore, shallow copies should be sufficient.
1293         //
1294         // transitionTimes64
1295         // typeMapData
1296         // typeOffsets
1297 
1298         tz.isFrozen = false;
1299         return tz;
1300     }
1301 }
1302