src/share/classes/sun/util/calendar/ZoneInfoFile.java

Print this page


   1 /*
   2  * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.util.calendar;
  27 



  28 import  java.io.File;
  29 import  java.io.FileInputStream;
  30 import  java.io.FileNotFoundException;
  31 import  java.io.IOException;
  32 import  java.lang.ref.SoftReference;
  33 import  java.nio.file.FileSystems;
  34 import  java.security.AccessController;
  35 import  java.security.PrivilegedAction;
  36 import  java.security.PrivilegedActionException;
  37 import  java.security.PrivilegedExceptionAction;









  38 import  java.util.ArrayList;



  39 import  java.util.HashMap;
  40 import  java.util.List;

  41 import  java.util.Map;







  42 
  43 /**
  44  * <code>ZoneInfoFile</code> reads Zone information files in the
  45  * &lt;java.home&gt;/lib/zi directory and provides time zone
  46  * information in the form of a {@link ZoneInfo} object. Also, it
  47  * reads the ZoneInfoMappings file to obtain time zone IDs information
  48  * that is used by the {@link ZoneInfo} class. The directory layout
  49  * and data file formats are as follows.
  50  *
  51  * <p><strong>Directory layout</strong><p>
  52  *
  53  * All zone data files and ZoneInfoMappings are put under the
  54  * &lt;java.home&gt;/lib/zi directory. A path name for a given time
  55  * zone ID is a concatenation of &lt;java.home&gt;/lib/zi/ and the
  56  * time zone ID. (The file separator is replaced with the platform
  57  * dependent value. e.g., '\' for Win32.) An example layout will look
  58  * like as follows.
  59  * <blockquote>
  60  * <pre>
  61  * &lt;java.home&gt;/lib/zi/Africa/Addis_Ababa
  62  *                   /Africa/Dakar
  63  *                   /America/Los_Angeles
  64  *                   /Asia/Singapore
  65  *                   /EET
  66  *                   /Europe/Oslo
  67  *                   /GMT
  68  *                   /Pacific/Galapagos
  69  *                       ...
  70  *                   /ZoneInfoMappings
  71  * </pre>
  72  * </blockquote>
  73  *
  74  * A zone data file has specific information of each zone.
  75  * <code>ZoneInfoMappings</code> has global information of zone IDs so
  76  * that the information can be obtained without instantiating all time
  77  * zones.
  78  *
  79  * <p><strong>File format</strong><p>
  80  *
  81  * Two binary-file formats based on a simple Tag-Length-Value format are used
  82  * to describe TimeZone information. The generic format of a data file is:
  83  * <blockquote>
  84  * <pre>
  85  *    DataFile {
  86  *      u1              magic[7];
  87  *      u1              version;
  88  *      data_item       data[];
  89  *    }
  90  * </pre>
  91  * </blockquote>
  92  * where <code>magic</code> is a magic number identifying a file
  93  * format, <code>version</code> is the format version number, and
  94  * <code>data</code> is one or more <code>data_item</code>s. The
  95  * <code>data_item</code> structure is:
  96  * <blockquote>
  97  * <pre>
  98  *    data_item {
  99  *      u1              tag;
 100  *      u2              length;
 101  *      u1              value[length];
 102  *    }
 103  * </pre>
 104  * </blockquote>
 105  * where <code>tag</code> indicates the data type of the item,
 106  * <code>length</code> is a byte count of the following
 107  * <code>value</code> that is the content of item data.
 108  * <p>
 109  * All data is stored in the big-endian order. There is no boundary
 110  * alignment between date items.
 111  *
 112  * <p><strong>1. ZoneInfo data file</strong><p>
 113  *
 114  * Each ZoneInfo data file consists of the following members.
 115  * <br>
 116  * <blockquote>
 117  * <pre>
 118  *    ZoneInfoDataFile {
 119  *      u1              magic[7];
 120  *      u1              version;
 121  *      SET OF<sup>1</sup> {
 122  *        transition            transitions<sup>2</sup>;
 123  *        offset_table          offsets<sup>2</sup>;
 124  *        simpletimezone        stzparams<sup>2</sup>;
 125  *        raw_offset            rawoffset;
 126  *        dstsaving             dst;
 127  *        checksum              crc32;
 128  *        gmtoffsetwillchange   gmtflag<sup>2</sup>;
 129  *      }
 130  *   }
 131  *   1: an unordered collection of zero or one occurrences of each item
 132  *   2: optional item
 133  * </pre>
 134  * </blockquote>
 135  * <code>magic</code> is a byte-string constant identifying the
 136  * ZoneInfo data file.  This field must be <code>"javazi&#92;0"</code>
 137  * defined as {@link #JAVAZI_LABEL}.
 138  * <p>
 139  * <code>version</code> is the version number of the file format. This
 140  * will be used for compatibility check. This field must be
 141  * <code>0x01</code> in this version.
 142  * <p>
 143  * <code>transition</code>, <code>offset_table</code> and
 144  * <code>simpletimezone</code> have information of time transition
 145  * from the past to the future.  Therefore, these structures don't
 146  * exist if the zone didn't change zone names and haven't applied DST in
 147  * the past, and haven't planned to apply it.  (e.g. Asia/Tokyo zone)
 148  * <p>
 149  * <code>raw_offset</code>, <code>dstsaving</code> and <code>checksum</code>
 150  * exist in every zoneinfo file. They are used by TimeZone.class indirectly.
 151  *
 152  * <p><strong>1.1 <code>transition</code> structure</strong><p><a name="transition"></a>
 153  * <blockquote>
 154  * <pre>
 155  *    transition {
 156  *      u1      tag;              // 0x04 : constant
 157  *      u2      length;           // byte length of whole values
 158  *      s8      value[length/8];  // transitions in `long'
 159  *    }
 160  * </pre>
 161  * </blockquote>
 162  * See {@link ZoneInfo#transitions ZoneInfo.transitions} about the value.
 163  *
 164  * <p><strong>1.2 <code>offset_table</code> structure</strong><p>
 165  * <blockquote>
 166  * <pre>
 167  *    offset_table {
 168  *      u1      tag;              // 0x05 : constant
 169  *      u2      length;           // byte length of whole values
 170  *      s4      value[length/4];  // offset values in `int'
 171  *    }
 172  * </pre>
 173  * </blockquote>
 174  *
 175  * <p><strong>1.3 <code>simpletimezone</code> structure</strong><p>
 176  * See {@link ZoneInfo#simpleTimeZoneParams ZoneInfo.simpleTimeZoneParams}
 177  * about the value.
 178  * <blockquote>
 179  * <pre>
 180  *    simpletimezone {
 181  *      u1      tag;              // 0x06 : constant
 182  *      u2      length;           // byte length of whole values
 183  *      s4      value[length/4];  // SimpleTimeZone parameters
 184  *    }
 185  * </pre>
 186  * </blockquote>
 187  * See {@link ZoneInfo#offsets ZoneInfo.offsets} about the value.
 188  *
 189  * <p><strong>1.4 <code>raw_offset</code> structure</strong><p>
 190  * <blockquote>
 191  * <pre>
 192  *    raw_offset {
 193  *      u1      tag;              // 0x01 : constant
 194  *      u2      length;           // must be 4.
 195  *      s4      value;            // raw GMT offset [millisecond]
 196  *    }
 197  * </pre>
 198  * </blockquote>
 199  * See {@link ZoneInfo#rawOffset ZoneInfo.rawOffset} about the value.
 200  *
 201  * <p><strong>1.5 <code>dstsaving</code> structure</strong><p>
 202  * Value has dstSaving in seconds.
 203  * <blockquote>
 204  * <pre>
 205  *    dstsaving {
 206  *      u1      tag;              // 0x02 : constant
 207  *      u2      length;           // must be 2.
 208  *      s2      value;            // DST save value [second]
 209  *    }
 210  * </pre>
 211  * </blockquote>
 212  * See {@link ZoneInfo#dstSavings ZoneInfo.dstSavings} about value.
 213  *
 214  * <p><strong>1.6 <code>checksum</code> structure</strong><p>
 215  * <blockquote>
 216  * <pre>
 217  *    checksum {
 218  *      u1      tag;              // 0x03 : constant
 219  *      u2      length;           // must be 4.
 220  *      s4      value;            // CRC32 value of transitions
 221  *    }
 222  * </pre>
 223  * </blockquote>
 224  * See {@link ZoneInfo#checksum ZoneInfo.checksum}.
 225  *
 226  * <p><strong>1.7 <code>gmtoffsetwillchange</code> structure</strong><p>
 227  * This record has a flag value for {@link ZoneInfo#rawOffsetWillChange}.
 228  * If this record is not present in a zoneinfo file, 0 is assumed for
 229  * the value.
 230  * <blockquote>
 231  * <pre>
 232  *    gmtoffsetwillchange {
 233  *      u1      tag;             // 0x07 : constant
 234  *      u2      length;          // must be 1.
 235  *      u1      value;           // 1: if the GMT raw offset will change
 236  *                               // in the future, 0, otherwise.
 237  *     }
 238  * </pre>
 239  * </blockquote>
 240  *
 241  *
 242  * <p><strong>2. ZoneInfoMappings file</strong><p>
 243  *
 244  * The ZoneInfoMappings file consists of the following members.
 245  * <br>
 246  * <blockquote>
 247  * <pre>
 248  *    ZoneInfoMappings {
 249  *      u1      magic[7];
 250  *      u1      version;
 251  *      SET OF {
 252  *        versionName                   version;
 253  *        zone_id_table                 zoneIDs;
 254  *        raw_offset_table              rawoffsets;
 255  *        raw_offset_index_table        rawoffsetindices;
 256  *        alias_table                   aliases;
 257  *        excluded_list                 excludedList;
 258  *      }
 259  *   }
 260  * </pre>
 261  * </blockquote>
 262  *
 263  * <code>magic</code> is a byte-string constant which has the file type.
 264  * This field must be <code>"javazm&#92;0"</code> defined as {@link #JAVAZM_LABEL}.
 265  * <p>
 266  * <code>version</code> is the version number of this file
 267  * format. This will be used for compatibility check. This field must
 268  * be <code>0x01</code> in this version.
 269  * <p>
 270  * <code>versionName</code> shows which version of Olson's data has been used
 271  * to generate this ZoneInfoMappings. (e.g. <code>tzdata2000g</code>) <br>
 272  * This field is for trouble-shooting and isn't usually used in runtime.
 273  * <p>
 274  * <code>zone_id_table</code>, <code>raw_offset_index_table</code> and
 275  * <code>alias_table</code> are general information of supported
 276  * zones.
 277  *
 278  * <p><strong>2.1 <code>zone_id_table</code> structure</strong><p>
 279  * The list of zone IDs included in the zi database. The list does
 280  * <em>not</em> include zone IDs, if any, listed in excludedList.
 281  * <br>
 282  * <blockquote>
 283  * <pre>
 284  *    zone_id_table {
 285  *      u1      tag;              // 0x40 : constant
 286  *      u2      length;           // byte length of whole values
 287  *      u2      zone_id_count;
 288  *      zone_id value[zone_id_count];
 289  *    }
 290  *
 291  *    zone_id {
 292  *      u1      byte_length;      // byte length of id
 293  *      u1      id[byte_length];  // zone name string
 294  *    }
 295  * </pre>
 296  * </blockquote>
 297  *
 298  * <p><strong>2.2 <code>raw_offset_table</code> structure</strong><p>
 299  * <br>
 300  * <blockquote>
 301  * <pre>
 302  *    raw_offset_table {
 303  *      u1      tag;              // 0x41 : constant
 304  *      u2      length;           // byte length of whole values
 305  *      s4      value[length/4];  // raw GMT offset in milliseconds
 306  *   }
 307  * </pre>
 308  * </blockquote>
 309  *
 310  * <p><strong>2.3 <code>raw_offset_index_table</code> structure</strong><p>
 311  * <br>
 312  * <blockquote>
 313  * <pre>
 314  *    raw_offset_index_table {
 315  *      u1      tag;              // 0x42 : constant
 316  *      u2      length;           // byte length of whole values
 317  *      u1      value[length];
 318  *    }
 319  * </pre>
 320  * </blockquote>
 321  *
 322  * <p><strong>2.4 <code>alias_table</code> structure</strong><p>
 323  * <br>
 324  * <blockquote>
 325  * <pre>
 326  *   alias_table {
 327  *      u1      tag;              // 0x43 : constant
 328  *      u2      length;           // byte length of whole values
 329  *      u2      nentries;         // number of id-pairs
 330  *      id_pair value[nentries];
 331  *   }
 332  *
 333  *   id_pair {
 334  *      zone_id aliasname;
 335  *      zone_id ID;
 336  *   }
 337  * </pre>
 338  * </blockquote>
 339  *
 340  * <p><strong>2.5 <code>versionName</code> structure</strong><p>
 341  * <br>
 342  * <blockquote>
 343  * <pre>
 344  *   versionName {
 345  *      u1      tag;              // 0x44 : constant
 346  *      u2      length;           // byte length of whole values
 347  *      u1      value[length];
 348  *   }
 349  * </pre>
 350  * </blockquote>
 351  *
 352  * <p><strong>2.6 <code>excludeList</code> structure</strong><p>
 353  * The list of zone IDs whose zones will change their GMT offsets
 354  * (a.k.a. raw offsets) some time in the future. Those IDs must be
 355  * added to the list of zone IDs for getAvailableIDs(). Also they must
 356  * be examined for getAvailableIDs(int) to determine the
 357  * <em>current</em> GMT offsets.
 358  * <br>
 359  * <blockquote>
 360  * <pre>
 361  *   excluded_list {
 362  *      u1      tag;              // 0x45 : constant
 363  *      u2      length;           // byte length of whole values
 364  *      u2      nentries;         // number of zone_ids
 365  *      zone_id value[nentries];  // excluded zone IDs
 366  *   }
 367  * </pre>
 368  * </blockquote>
 369  *
 370  * @since 1.4
 371  */
 372 
 373 public class ZoneInfoFile {
 374 
 375     /**
 376      * The magic number for the ZoneInfo data file format.
 377      */
 378     public static final byte[]  JAVAZI_LABEL = {
 379         (byte)'j', (byte)'a', (byte)'v', (byte)'a', (byte)'z', (byte)'i', (byte)'\0'
 380     };
 381     private static final int    JAVAZI_LABEL_LENGTH = JAVAZI_LABEL.length;
 382 
 383     /**
 384      * The ZoneInfo data file format version number. Must increase
 385      * one when any incompatible change has been made.
 386      */
 387     public static final byte    JAVAZI_VERSION = 0x01;
 388 
 389     /**
 390      * Raw offset data item tag.
 391      */
 392     public static final byte    TAG_RawOffset = 1;
 393 
 394     /**
 395      * Known last Daylight Saving Time save value data item tag.
 396      */
 397     public static final byte    TAG_LastDSTSaving = 2;
 398 
 399     /**
 400      * Checksum data item tag.
 401      */
 402     public static final byte    TAG_CRC32 = 3;
 403 
 404     /**
 405      * Transition data item tag.
 406      */
 407     public static final byte    TAG_Transition = 4;
 408 
 409     /**
 410      * Offset table data item tag.
 411      */
 412     public static final byte    TAG_Offset = 5;
 413 
 414     /**
 415      * SimpleTimeZone parameters data item tag.
 416      */
 417     public static final byte    TAG_SimpleTimeZone = 6;
 418 
 419     /**
 420      * Raw GMT offset will change in the future.
 421      */
 422     public static final byte    TAG_GMTOffsetWillChange = 7;
 423 
 424 
 425     /**
 426      * The ZoneInfoMappings file name.
 427      */
 428     public static final String  JAVAZM_FILE_NAME = "ZoneInfoMappings";
 429 
 430     /**
 431      * The magic number for the ZoneInfoMappings file format.
 432      */
 433     public static final byte[]  JAVAZM_LABEL = {
 434         (byte)'j', (byte)'a', (byte)'v', (byte)'a', (byte)'z', (byte)'m', (byte)'\0'
 435     };
 436     private static final int    JAVAZM_LABEL_LENGTH = JAVAZM_LABEL.length;
 437 
 438     /**
 439      * The ZoneInfoMappings file format version number. Must increase
 440      * one when any incompatible change has been made.
 441      */
 442     public static final byte    JAVAZM_VERSION = 0x01;
 443 
 444     /**
 445      * Time zone IDs data item tag.
 446      */
 447     public static final byte    TAG_ZoneIDs = 64;
 448 
 449     /**
 450      * Raw GMT offsets table data item tag.
 451      */
 452     public static final byte    TAG_RawOffsets = 65;


 453 
 454     /**
 455      * Indices to the raw GMT offset table data item tag.





 456      */
 457     public static final byte    TAG_RawOffsetIndices = 66;









 458 
 459     /**
 460      * Time zone aliases table data item tag.
 461      */
 462     public static final byte    TAG_ZoneAliases = 67;

 463 
 464     /**
 465      * Olson's public zone information version tag.
 466      */
 467     public static final byte    TAG_TZDataVersion = 68;

 468 
 469     /**
 470      * Excluded zones item tag. (Added in Mustang)



 471      */
 472     public static final byte    TAG_ExcludedZones = 69;
 473 
 474     private static Map<String, ZoneInfo> zoneInfoObjects = null;
 475 
 476     private static final ZoneInfo GMT = new ZoneInfo("GMT", 0);
 477 
 478     private static final String ziDir = AccessController.doPrivileged(
 479         new PrivilegedAction<String>() {
 480             public String run() {
 481                 String zi = System.getProperty("java.home") +
 482                     File.separator + "lib" + File.separator + "zi";
 483                 try {
 484                     zi = FileSystems.getDefault().getPath(zi).toRealPath().toString();
 485                 } catch(Exception e) {
 486                 }
 487                 return zi;
 488             }
 489         });
 490 
 491     /**
 492      * Converts the given time zone ID to a platform dependent path
 493      * name. For example, "America/Los_Angeles" is converted to
 494      * "America\Los_Angeles" on Win32.
 495      * @return a modified ID replacing '/' with {@link
 496      * java.io.File#separatorChar File.separatorChar} if needed.
 497      */
 498     public static String getFileName(String ID) {
 499         if (File.separatorChar == '/') {
 500             return ID;
 501         }
 502         return ID.replace('/', File.separatorChar);
 503     }
 504 
 505     /**
 506      * Gets a ZoneInfo with the given GMT offset. The object
 507      * has its ID in the format of GMT{+|-}hh:mm.
 508      *
 509      * @param originalId the given custom id (before normalized such as "GMT+9")
 510      * @param gmtOffset GMT offset <em>in milliseconds</em>
 511      * @return a ZoneInfo constructed with the given GMT offset
 512      */
 513     public static ZoneInfo getCustomTimeZone(String originalId, int gmtOffset) {
 514         String id = toCustomID(gmtOffset);
 515 

 516         ZoneInfo zi = getFromCache(id);
 517         if (zi == null) {
 518             zi = new ZoneInfo(id, gmtOffset);
 519             zi = addToCache(id, zi);
 520             if (!id.equals(originalId)) {
 521                 zi = addToCache(originalId, zi);
 522             }
 523         }
 524         return (ZoneInfo) zi.clone();

 525     }
 526 
 527     public static String toCustomID(int gmtOffset) {
 528         char sign;
 529         int offset = gmtOffset / 60000;
 530 
 531         if (offset >= 0) {
 532             sign = '+';
 533         } else {
 534             sign = '-';
 535             offset = -offset;
 536         }
 537         int hh = offset / 60;
 538         int mm = offset % 60;
 539 
 540         char[] buf = new char[] { 'G', 'M', 'T', sign, '0', '0', ':', '0', '0' };
 541         if (hh >= 10) {
 542             buf[4] += hh / 10;
 543         }
 544         buf[5] += hh % 10;
 545         if (mm != 0) {
 546             buf[7] += mm / 10;
 547             buf[8] += mm % 10;
 548         }
 549         return new String(buf);
 550     }
 551 
 552     /**
 553      * @return a ZoneInfo instance created for the specified id, or
 554      * null if there is no time zone data file found for the specified
 555      * id.
 556      */
 557     public static ZoneInfo getZoneInfo(String id) {
 558         //treat GMT zone as special
 559         if ("GMT".equals(id))
 560             return (ZoneInfo) GMT.clone();
 561         ZoneInfo zi = getFromCache(id);
 562         if (zi == null) {
 563             Map<String, String> aliases = ZoneInfo.getCachedAliasTable();
 564             if (aliases != null && aliases.get(id) != null) {
 565                 return null;
 566             }
 567             zi = createZoneInfo(id);
 568             if (zi == null) {
 569                 return null;
 570             }
 571             zi = addToCache(id, zi);
 572         }
 573         return (ZoneInfo) zi.clone();
 574     }
 575 
 576     synchronized static ZoneInfo getFromCache(String id) {
 577         if (zoneInfoObjects == null) {
 578             return null;
 579         }
 580         return zoneInfoObjects.get(id);
 581     }
 582 
 583     synchronized static ZoneInfo addToCache(String id, ZoneInfo zi) {
 584         if (zoneInfoObjects == null) {
 585             zoneInfoObjects = new HashMap<>();
 586         } else {
 587             ZoneInfo zone = zoneInfoObjects.get(id);
 588             if (zone != null) {
 589                 return zone;
 590             }
 591         }
 592         zoneInfoObjects.put(id, zi);
 593         return zi;
 594     }
 595 
 596     private static ZoneInfo createZoneInfo(String id) {
 597         byte[] buf = readZoneInfoFile(getFileName(id));
 598         if (buf == null) {
 599             return null;
 600         }
 601 
 602         int index = 0;
 603         int filesize = buf.length;
 604         int rawOffset = 0;
 605         int dstSavings = 0;
 606         int checksum = 0;
 607         boolean willGMTOffsetChange = false;
 608         long[] transitions = null;
 609         int[] offsets = null;
 610         int[] simpleTimeZoneParams = null;
 611 

 612         try {
 613             for (index = 0; index < JAVAZI_LABEL.length; index++) {
 614                 if (buf[index] != JAVAZI_LABEL[index]) {
 615                     System.err.println("ZoneInfo: wrong magic number: " + id);
 616                     return null;
 617                 }
 618             }
 619             if (buf[index++] > JAVAZI_VERSION) {
 620                 System.err.println("ZoneInfo: incompatible version ("
 621                                    + buf[index - 1] + "): " + id);
 622                 return null;
 623             }
 624 
 625             while (index < filesize) {
 626                 byte tag = buf[index++];
 627                 int  len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
 628 
 629                 if (filesize < index+len) {
 630                     break;





 631                 }
 632 
 633                 switch (tag) {
 634                 case TAG_CRC32:
 635                     {
 636                         int val = buf[index++] & 0xff;
 637                         val = (val << 8) + (buf[index++] & 0xff);
 638                         val = (val << 8) + (buf[index++] & 0xff);
 639                         val = (val << 8) + (buf[index++] & 0xff);
 640                         checksum = val;
 641                     }
 642                     break;
 643 
 644                 case TAG_LastDSTSaving:
 645                     {
 646                         short val = (short)(buf[index++] & 0xff);
 647                         val = (short)((val << 8) + (buf[index++] & 0xff));
 648                         dstSavings = val * 1000;
 649                     }
 650                     break;
 651 
 652                 case TAG_RawOffset:
 653                     {
 654                         int val = buf[index++] & 0xff;
 655                         val = (val << 8) + (buf[index++] & 0xff);
 656                         val = (val << 8) + (buf[index++] & 0xff);
 657                         val = (val << 8) + (buf[index++] & 0xff);
 658                         rawOffset = val;
 659                     }
 660                     break;
 661 
 662                 case TAG_Transition:
 663                     {
 664                         int n = len / 8;
 665                         transitions = new long[n];
 666                         for (int i = 0; i < n; i ++) {
 667                             long val = buf[index++] & 0xff;
 668                             val = (val << 8) + (buf[index++] & 0xff);
 669                             val = (val << 8) + (buf[index++] & 0xff);
 670                             val = (val << 8) + (buf[index++] & 0xff);
 671                             val = (val << 8) + (buf[index++] & 0xff);
 672                             val = (val << 8) + (buf[index++] & 0xff);
 673                             val = (val << 8) + (buf[index++] & 0xff);
 674                             val = (val << 8) + (buf[index++] & 0xff);
 675                             transitions[i] = val;
 676                         }
 677                     }
 678                     break;
 679 
 680                 case TAG_Offset:
 681                     {
 682                         int n = len / 4;
 683                         offsets = new int[n];
 684                         for (int i = 0; i < n; i ++) {
 685                             int val = buf[index++] & 0xff;
 686                             val = (val << 8) + (buf[index++] & 0xff);
 687                             val = (val << 8) + (buf[index++] & 0xff);
 688                             val = (val << 8) + (buf[index++] & 0xff);
 689                             offsets[i] = val;
 690                         }


 691                     }
 692                     break;
 693 
 694                 case TAG_SimpleTimeZone:
 695                     {
 696                         if (len != 32 && len != 40) {
 697                             System.err.println("ZoneInfo: wrong SimpleTimeZone parameter size");
 698                             return null;
 699                         }
 700                         int n = len / 4;
 701                         simpleTimeZoneParams = new int[n];
 702                         for (int i = 0; i < n; i++) {
 703                             int val = buf[index++] & 0xff;
 704                             val = (val << 8) + (buf[index++] & 0xff);
 705                             val = (val << 8) + (buf[index++] & 0xff);
 706                             val = (val << 8) + (buf[index++] & 0xff);
 707                             simpleTimeZoneParams[i] = val;
 708                         }
 709                     }
 710                     break;
 711 
 712                 case TAG_GMTOffsetWillChange:
 713                     {
 714                         if (len != 1) {
 715                             System.err.println("ZoneInfo: wrong byte length for TAG_GMTOffsetWillChange");
 716                         }
 717                         willGMTOffsetChange = buf[index++] == 1;
 718                     }
 719                     break;
 720 
 721                 default:
 722                     System.err.println("ZoneInfo: unknown tag < " + tag + ">. ignored.");
 723                     index += len;
 724                     break;
 725                 }
 726             }
 727         } catch (Exception e) {
 728             System.err.println("ZoneInfo: corrupted zoneinfo file: " + id);
 729             return null;
 730         }
 731 
 732         if (index != filesize) {
 733             System.err.println("ZoneInfo: wrong file size: " + id);
 734             return null;
 735         }
 736 
 737         return new ZoneInfo(id, rawOffset, dstSavings, checksum,
 738                             transitions, offsets, simpleTimeZoneParams,
 739                             willGMTOffsetChange);
 740     }
 741 
 742     private volatile static SoftReference<List<String>> zoneIDs = null;
 743 
 744     static List<String> getZoneIDs() {
 745         List<String> ids = null;
 746 
 747         SoftReference<List<String>> cache = zoneIDs;
 748         if (cache != null) {
 749             ids = cache.get();
 750             if (ids != null) {
 751                 return ids;
 752             }
























 753         }
 754 
 755         byte[] buf = null;
 756         buf = getZoneInfoMappings();
 757         int index = JAVAZM_LABEL_LENGTH + 1;
 758         int filesize = buf.length;
 759 
 760         try {
 761         loop:
 762             while (index < filesize) {
 763                 byte tag = buf[index++];
 764                 int     len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
 765 
 766                 switch (tag) {
 767                 case TAG_ZoneIDs:
 768                     {
 769                         int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
 770                         ids = new ArrayList<>(n);
 771 
 772                         for (int i = 0; i < n; i++) {
 773                             byte m = buf[index++];
 774                             ids.add(new String(buf, index, m, "UTF-8"));
 775                             index += m;
 776                         }




 777                     }
 778                     break loop;
 779 
 780                 default:
 781                     index += len;
 782                     break;
 783                 }



 784             }
 785         } catch (Exception e) {
 786             System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME);
 787         }
 788 
 789         zoneIDs = new SoftReference<>(ids);
 790         return ids;
 791     }
 792 
 793     /**
 794      * @return an alias table in HashMap where a key is an alias ID
 795      * (e.g., "PST") and its value is a real time zone ID (e.g.,
 796      * "America/Los_Angeles").

 797      */
 798     static Map<String, String> getZoneAliases() {
 799         byte[] buf = getZoneInfoMappings();
 800         int index = JAVAZM_LABEL_LENGTH + 1;
 801         int filesize = buf.length;
 802         Map<String, String> aliases = null;
 803 
 804         try {
 805         loop:
 806             while (index < filesize) {
 807                 byte tag = buf[index++];
 808                 int     len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
 809 
 810                 switch (tag) {
 811                 case TAG_ZoneAliases:
 812                     {
 813                         int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
 814                         aliases = new HashMap<>(n);
 815                         for (int i = 0; i < n; i++) {
 816                             byte m = buf[index++];
 817                             String name = new String(buf, index, m, "UTF-8");
 818                             index += m;
 819                             m = buf[index++];
 820                             String realName = new String(buf, index, m, "UTF-8");
 821                             index += m;
 822                             aliases.put(name, realName);
 823                         }




 824                     }
 825                     break loop;



 826 
 827                 default:
 828                     index += len;
 829                     break;
 830                 }





 831             }
 832         } catch (Exception e) {
 833             System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME);
 834             return null;




 835         }
 836         return aliases;







 837     }
 838 
 839     private volatile static SoftReference<List<String>> excludedIDs = null;
 840     private volatile static boolean hasNoExcludeList = false;
 841 
 842     /**
 843      * @return a List of zone IDs for zones that will change their GMT
 844      * offsets in some future time.
 845      *
 846      * @since 1.6
 847      */
 848     static List<String> getExcludedZones() {
 849         if (hasNoExcludeList) {
 850             return null;
 851         }
 852 
 853         List<String> excludeList = null;
 854 
 855         SoftReference<List<String>> cache = excludedIDs;
 856         if (cache != null) {
 857             excludeList = cache.get();
 858             if (excludeList != null) {
 859                 return excludeList;


 860             }
 861         }
 862 
 863         byte[] buf = getZoneInfoMappings();
 864         int index = JAVAZM_LABEL_LENGTH + 1;
 865         int filesize = buf.length;
 866 
 867         try {
 868           loop:
 869             while (index < filesize) {
 870                 byte tag = buf[index++];
 871                 int     len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
 872 
 873                 switch (tag) {
 874                 case TAG_ExcludedZones:
 875                     {
 876                         int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
 877                         excludeList = new ArrayList<>();
 878                         for (int i = 0; i < n; i++) {
 879                             byte m = buf[index++];
 880                             String name = new String(buf, index, m, "UTF-8");
 881                             index += m;
 882                             excludeList.add(name);
 883                         }
 884                     }
 885                     break loop;
 886 
 887                 default:
 888                     index += len;
 889                     break;





 890                 }



 891             }
 892         } catch (Exception e) {
 893             System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME);
 894             return null;

 895         }
 896 
 897         if (excludeList != null) {
 898             excludedIDs = new SoftReference<>(excludeList);
 899         } else {
 900             hasNoExcludeList = true;
 901         }
 902         return excludeList;
 903     }
 904 
 905     private volatile static SoftReference<byte[]> rawOffsetIndices = null;
 906 
 907     static byte[] getRawOffsetIndices() {
 908         byte[] indices = null;
 909 
 910         SoftReference<byte[]> cache = rawOffsetIndices;
 911         if (cache != null) {
 912             indices = cache.get();
 913             if (indices != null) {
 914                 return indices;
 915             }

 916         }
 917 
 918         byte[] buf = getZoneInfoMappings();
 919         int index = JAVAZM_LABEL_LENGTH + 1;
 920         int filesize = buf.length;
 921 
 922         try {
 923         loop:
 924             while (index < filesize) {
 925                 byte tag = buf[index++];
 926                 int     len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
 927 
 928                 switch (tag) {
 929                 case TAG_RawOffsetIndices:
 930                     {
 931                         indices = new byte[len];
 932                         for (int i = 0; i < len; i++) {
 933                             indices[i] = buf[index++];
 934                         }
 935                     }
 936                     break loop;
 937 
 938                 default:
 939                     index += len;
 940                     break;
 941                 }





 942             }
 943         } catch (ArrayIndexOutOfBoundsException e) {
 944             System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME);
 945         }
 946 
 947         rawOffsetIndices = new SoftReference<>(indices);
 948         return indices;
 949     }










































 950 
 951     private volatile static SoftReference<int[]> rawOffsets = null;




 952 
 953     static int[] getRawOffsets() {

 954         int[] offsets = null;


 955 
 956         SoftReference<int[]> cache = rawOffsets;
 957         if (cache != null) {
 958             offsets = cache.get();
 959             if (offsets != null) {
 960                 return offsets;
 961             }
 962         }
 963 
 964         byte[] buf = getZoneInfoMappings();
 965         int index = JAVAZM_LABEL_LENGTH + 1;
 966         int filesize = buf.length;
 967 
 968         try {
 969         loop:
 970             while (index < filesize) {
 971                 byte tag = buf[index++];
 972                 int     len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
 973 
 974                 switch (tag) {
 975                 case TAG_RawOffsets:
 976                     {
 977                         int n = len/4;
 978                         offsets = new int[n];
 979                         for (int i = 0; i < n; i++) {
 980                             int val = buf[index++] & 0xff;
 981                             val = (val << 8) + (buf[index++] & 0xff);
 982                             val = (val << 8) + (buf[index++] & 0xff);
 983                             val = (val << 8) + (buf[index++] & 0xff);
 984                             offsets[i] = val;
 985                         }
 986                     }
 987                     break loop;
 988 
 989                 default:
 990                     index += len;
 991                     break;

































































































































 992                 }
 993             }
 994         } catch (ArrayIndexOutOfBoundsException e) {
 995             System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME);



































































 996         }
 997 
 998         rawOffsets = new SoftReference<>(offsets);
 999         return offsets;










1000     }
1001 
1002     private volatile static SoftReference<byte[]> zoneInfoMappings = null;
1003 
1004     private static byte[] getZoneInfoMappings() {
1005         byte[] data;
1006 
1007         SoftReference<byte[]> cache = zoneInfoMappings;
1008         if (cache != null) {
1009             data = cache.get();
1010             if (data != null) {
1011                 return data;

1012             }
1013         }
1014 
1015         data = readZoneInfoFile(JAVAZM_FILE_NAME);
1016 
1017         if (data == null) {
1018             return null;
1019         }
1020 
1021         int index;
1022         for (index = 0; index < JAVAZM_LABEL.length; index++) {
1023             if (data[index] != JAVAZM_LABEL[index]) {
1024                 System.err.println("ZoneInfo: wrong magic number: " + JAVAZM_FILE_NAME);
1025                 return null;
1026             }
1027         }
1028         if (data[index++] > JAVAZM_VERSION) {
1029             System.err.println("ZoneInfo: incompatible version ("
1030                                + data[index - 1] + "): " + JAVAZM_FILE_NAME);
1031             return null;

1032         }
1033 
1034         zoneInfoMappings = new SoftReference<>(data);
1035         return data;
1036     }














































1037 
1038     /**
1039      * Reads the specified file under &lt;java.home&gt;/lib/zi into a buffer.
1040      * @return the buffer, or null if any I/O error occurred.
1041      */
1042     private static byte[] readZoneInfoFile(final String fileName) {
1043         if (fileName.indexOf("..") >= 0) {
1044             return null;
1045         }
1046         byte[] buffer = null;
1047 
1048         try {
1049             buffer = AccessController.doPrivileged(new PrivilegedExceptionAction<byte[]>() {
1050                 public byte[] run() throws IOException {
1051                     File file = new File(ziDir, fileName);
1052                     byte[] buf = null;
1053                     int filesize = (int)file.length();
1054                     if (filesize > 0) {
1055                         FileInputStream fis = new FileInputStream(file);
1056                         buf = new byte[filesize];
1057                         try {
1058                             if (fis.read(buf) != filesize) {
1059                                 throw new IOException("read error on " + fileName);
1060                             }
1061                         } finally {
1062                             fis.close();











































1063                         }
1064                     }
1065                     return buf;
1066                 }
1067             });
1068         } catch (PrivilegedActionException e) {
1069             Exception ex = e.getException();
1070             if (!(ex instanceof FileNotFoundException) || JAVAZM_FILE_NAME.equals(fileName)) {
1071                 System.err.println("ZoneInfo: " + ex.getMessage());
1072             }
1073         }
1074         return buffer;
1075     }
1076 
1077     private ZoneInfoFile() {
1078     }
1079 }
   1 /*
   2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.util.calendar;
  27 
  28 import java.io.ByteArrayInputStream;
  29 import java.io.DataInput;
  30 import java.io.DataInputStream;
  31 import java.io.File;


  32 import java.io.IOException;
  33 import java.io.StreamCorruptedException;

  34 import java.security.AccessController;
  35 import java.security.PrivilegedAction;
  36 import java.time.DayOfWeek;
  37 import java.time.LocalDateTime;
  38 import java.time.LocalTime;
  39 import java.time.Month;
  40 import java.time.OffsetDateTime;
  41 import java.time.ZoneOffset;
  42 import java.time.zone.ZoneRules;
  43 import java.time.zone.ZoneOffsetTransition;
  44 import java.time.zone.ZoneOffsetTransitionRule;
  45 import java.time.zone.ZoneOffsetTransitionRule.TimeDefinition;
  46 
  47 import java.util.ArrayList;
  48 import java.util.Arrays;
  49 import java.util.Calendar;
  50 import java.util.Collections;
  51 import java.util.HashMap;
  52 import java.util.List;
  53 import java.util.Locale;
  54 import java.util.Map;
  55 import java.util.Map.Entry;
  56 import java.util.Objects;
  57 import java.util.Set;
  58 import java.util.SimpleTimeZone;
  59 import java.util.concurrent.ConcurrentHashMap;
  60 import java.util.zip.CRC32;
  61 import java.util.zip.ZipFile;
  62 
  63 /**
  64  * Loads TZDB time-zone rules for j.u.TimeZone
































































































































































































































  65  * <p>
  66  * @since 1.8




























































































































































  67  */
  68 public final class ZoneInfoFile {
  69 
  70     /**
  71      * Gets all available IDs supported in the Java run-time.
  72      *
  73      * @return a set of time zone IDs.

















  74      */
  75     public static Set<String> getZoneIds() {
  76         return zones.keySet();
  77     }
  78 
  79     /**
  80      * Gets all available IDs that have the same value as the
  81      * specified raw GMT offset.
  82      *
  83      * @param rawOffset  the GMT offset in milliseconds. This
  84      *                   value should not include any daylight saving time.
  85      * @return an array of time zone IDs.
  86      */
  87     public static String[] getZoneIds(int rawOffset) {
  88         List<String> ids = new ArrayList<>();
  89         for (String id : zones.keySet()) {
  90             ZoneInfo zi = getZoneInfo0(id);
  91             if (zi.getRawOffset() == rawOffset) {
  92                 ids.add(id);
  93             }
  94         }
  95         return ids.toArray(new String[ids.size()]);
  96     }
  97 
  98     public static ZoneInfo getZoneInfo(String zoneId) {
  99         if (!zones.containsKey(zoneId)) {
 100             return null;
 101         }
 102         // ZoneInfo is mutable, return the copy
 103 
 104         ZoneInfo zi = getZoneInfo0(zoneId);
 105         zi = (ZoneInfo)zi.clone();
 106         zi.setID(zoneId);
 107         return zi;
 108     }
 109 
 110     /**
 111      * Returns a Map from alias time zone IDs to their standard
 112      * time zone IDs.
 113      *
 114      * @return an unmodified alias mapping
 115      */
 116     public static Map<String, String> getAliasMap() {
 117         return aliases;














 118     }

 119 
 120     /**
 121      * Gets the version of this tz data.
 122      *
 123      * @return the tzdb version


 124      */
 125     public static String getVersion() {
 126         return versionId;



 127     }
 128 
 129     /**
 130      * Gets a ZoneInfo with the given GMT offset. The object
 131      * has its ID in the format of GMT{+|-}hh:mm.
 132      *
 133      * @param originalId  the given custom id (before normalized such as "GMT+9")
 134      * @param gmtOffset   GMT offset <em>in milliseconds</em>
 135      * @return a ZoneInfo constructed with the given GMT offset
 136      */
 137     public static ZoneInfo getCustomTimeZone(String originalId, int gmtOffset) {
 138         String id = toCustomID(gmtOffset);
 139         return new ZoneInfo(id, gmtOffset);
 140 /*
 141         ZoneInfo zi = getFromCache(id);
 142         if (zi == null) {
 143             zi = new ZoneInfo(id, gmtOffset);
 144             zi = addToCache(id, zi);
 145             if (!id.equals(originalId)) {
 146                 zi = addToCache(originalId, zi);
 147             }
 148         }
 149         return (ZoneInfo) zi.clone();
 150 */
 151     }
 152 
 153     public static String toCustomID(int gmtOffset) {
 154         char sign;
 155         int offset = gmtOffset / 60000;
 156 
 157         if (offset >= 0) {
 158             sign = '+';
 159         } else {
 160             sign = '-';
 161             offset = -offset;
 162         }
 163         int hh = offset / 60;
 164         int mm = offset % 60;
 165 
 166         char[] buf = new char[] { 'G', 'M', 'T', sign, '0', '0', ':', '0', '0' };
 167         if (hh >= 10) {
 168             buf[4] += hh / 10;
 169         }
 170         buf[5] += hh % 10;
 171         if (mm != 0) {
 172             buf[7] += mm / 10;
 173             buf[8] += mm % 10;
 174         }
 175         return new String(buf);
 176     }
 177 























 178 
 179     ///////////////////////////////////////////////////////////


































 180 
 181     private static ZoneInfo getZoneInfo0(String zoneId) {
 182         try {











 183 
 184             Object obj = zones.get(zoneId);
 185             if (obj instanceof byte[]) {
 186                 byte[] bytes = (byte[]) obj;
 187                 DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
 188                 obj = getZoneInfo(dis, zoneId);
 189                 zones.put(zoneId, obj);
 190             }
 191             return (ZoneInfo)obj;
 192         } catch (Exception ex) {
 193             throw new RuntimeException("Invalid binary time-zone data: TZDB:" +
 194                 zoneId + ", version: " + versionId, ex);
 195         }

















 196     }

 197 
 198     private ZoneInfoFile() {






 199     }

 200 
 201     private static String versionId;
 202     private final static Map<String, Object> zones = new ConcurrentHashMap<>();
 203     private static Map<String, String> aliases = new HashMap<>();
 204 
 205     // Flag for supporting JDK backward compatible IDs, such as "EST".
 206     private static final boolean USE_OLDMAPPING;
 207 
 208     static {
 209         String oldmapping = AccessController.doPrivileged(
 210             new sun.security.action.GetPropertyAction("sun.timezone.ids.oldmapping", "false")).toLowerCase(Locale.ROOT);
 211         USE_OLDMAPPING = (oldmapping.equals("yes") || oldmapping.equals("true"));
 212         AccessController.doPrivileged(new PrivilegedAction<Object>() {
 213             public Object run() {
 214                 try {



 215 
 216                     String libDir = System.getProperty("java.home") + File.separator + "lib";
 217                     File tzdbJar = new File(libDir, "tzdb.jar");
 218                     try (ZipFile zf = new ZipFile(tzdbJar);
 219                         DataInputStream dis = new DataInputStream(
 220                             zf.getInputStream(zf.getEntry("TZDB.dat")))) {
 221                         load(dis);




 222                     }
 223                 } catch (Exception x) {
 224                     throw new Error(x);
 225                 }






 226                 return null;
 227             }
 228         });







































 229     }
 230 
 231     // Must be invoked after loading in all data
 232     private static void addOldMapping() {
 233         String[][] oldMappings = new String[][] {
 234             { "ACT", "Australia/Darwin" },
 235             { "AET", "Australia/Sydney" },
 236             { "AGT", "America/Argentina/Buenos_Aires" },
 237             { "ART", "Africa/Cairo" },
 238             { "AST", "America/Anchorage" },
 239             { "BET", "America/Sao_Paulo" },
 240             { "BST", "Asia/Dhaka" },
 241             { "CAT", "Africa/Harare" },
 242             { "CNT", "America/St_Johns" },
 243             { "CST", "America/Chicago" },
 244             { "CTT", "Asia/Shanghai" },
 245             { "EAT", "Africa/Addis_Ababa" },
 246             { "ECT", "Europe/Paris" },
 247             { "IET", "America/Indiana/Indianapolis" },
 248             { "IST", "Asia/Kolkata" },
 249             { "JST", "Asia/Tokyo" },
 250             { "MIT", "Pacific/Apia" },
 251             { "NET", "Asia/Yerevan" },
 252             { "NST", "Pacific/Auckland" },
 253             { "PLT", "Asia/Karachi" },
 254             { "PNT", "America/Phoenix" },
 255             { "PRT", "America/Puerto_Rico" },
 256             { "PST", "America/Los_Angeles" },
 257             { "SST", "Pacific/Guadalcanal" },
 258             { "VST", "Asia/Ho_Chi_Minh" },
 259         };
 260         for (String[] alias : oldMappings) {
 261             String k = alias[0];
 262             String v = alias[1];
 263             if (zones.containsKey(v)) {  // make sure we do have the data
 264                 aliases.put(k, v);
 265                 zones.put(k, zones.get(v));
 266             }






















 267         }
 268         if (USE_OLDMAPPING) {
 269             if (zones.containsKey("America/New_York")) {
 270                 aliases.put("EST", "America/New_York");
 271                 zones.put("EST", zones.get("America/New_York"));
 272             }
 273             if (zones.containsKey("America/Denver")) {
 274                 aliases.put("MST", "America/Denver");
 275                 zones.put("MST", zones.get("America/Denver"));


 276             }
 277             if (zones.containsKey("Pacific/Honolulu")) {
 278                 aliases.put("HST", "Pacific/Honolulu");
 279                 zones.put("HST", zones.get("Pacific/Honolulu"));
 280             }


 281         }



 282     }
 283 
 284     /**
 285      * Loads the rules from a DateInputStream
 286      *
 287      * @param dis  the DateInputStream to load, not null
 288      * @throws Exception if an error occurs
 289      */
 290     private static void load(DataInputStream dis) throws ClassNotFoundException, IOException {
 291         if (dis.readByte() != 1) {
 292             throw new StreamCorruptedException("File format not recognised");






















 293         }
 294         // group
 295         String groupId = dis.readUTF();
 296         if ("TZDB".equals(groupId) == false) {
 297             throw new StreamCorruptedException("File format not recognised");
 298         }
 299         // versions, only keep the last one
 300         int versionCount = dis.readShort();
 301         for (int i = 0; i < versionCount; i++) {
 302             versionId = dis.readUTF();
 303 



 304         }
 305         // regions
 306         int regionCount = dis.readShort();
 307         String[] regionArray = new String[regionCount];
 308         for (int i = 0; i < regionCount; i++) {
 309             regionArray[i] = dis.readUTF();
 310         }
 311         // rules
 312         int ruleCount = dis.readShort();
 313         Object[] ruleArray = new Object[ruleCount];
 314         for (int i = 0; i < ruleCount; i++) {
 315             byte[] bytes = new byte[dis.readShort()];
 316             dis.readFully(bytes);
 317             ruleArray[i] = bytes;
 318         }
 319         // link version-region-rules, only keep the last version, if more than one
 320         for (int i = 0; i < versionCount; i++) {
 321             regionCount = dis.readShort();
 322             zones.clear();
 323             for (int j = 0; j < regionCount; j++) {
 324                 String region = regionArray[dis.readShort()];
 325                 Object rule = ruleArray[dis.readShort() & 0xffff];
 326                 zones.put(region, rule);
 327             }













 328         }
 329         // remove the following ids from the map, they
 330         // are exclued from the "old" ZoneInfo
 331         zones.remove("ROC");
 332         for (int i = 0; i < versionCount; i++) {
 333             int aliasCount = dis.readShort();
 334             aliases.clear();
 335             for (int j = 0; j < aliasCount; j++) {
 336                 String alias = regionArray[dis.readShort()];
 337                 String region = regionArray[dis.readShort()];
 338                 aliases.put(alias, region);
 339             }
 340         }
 341         // old us time-zone names
 342         addOldMapping();
 343         aliases = Collections.unmodifiableMap(aliases);


















 344     }


 345 
 346     /////////////////////////Ser/////////////////////////////////
 347     public static ZoneInfo getZoneInfo(DataInput in, String zoneId) throws Exception {
 348         byte type = in.readByte();
 349         // TBD: assert ZRULES:
 350         int stdSize = in.readInt();
 351         long[] stdTrans = new long[stdSize];
 352         for (int i = 0; i < stdSize; i++) {
 353             stdTrans[i] = readEpochSec(in);
 354         }
 355         int [] stdOffsets = new int[stdSize + 1];
 356         for (int i = 0; i < stdOffsets.length; i++) {
 357             stdOffsets[i] = readOffset(in);
 358         }
 359         int savSize = in.readInt();
 360         long[] savTrans = new long[savSize];
 361         for (int i = 0; i < savSize; i++) {
 362             savTrans[i] = readEpochSec(in);
 363         }
 364         int[] savOffsets = new int[savSize + 1];
 365         for (int i = 0; i < savOffsets.length; i++) {
 366             savOffsets[i] = readOffset(in);


 367         }
 368         int ruleSize = in.readByte();
 369         ZoneOffsetTransitionRule[] rules = new ZoneOffsetTransitionRule[ruleSize];
 370         for (int i = 0; i < ruleSize; i++) {
 371             rules[i] = readZOTRule(in);









 372         }
 373         return getZoneInfo(zoneId, stdTrans, stdOffsets, savTrans, savOffsets, rules);
 374     }
 375 
 376     public static int readOffset(DataInput in) throws IOException {
 377         int offsetByte = in.readByte();
 378         return offsetByte == 127 ? in.readInt() : offsetByte * 900;













 379     }


 380 
 381     static long readEpochSec(DataInput in) throws IOException {
 382         int hiByte = in.readByte() & 255;
 383         if (hiByte == 255) {
 384             return in.readLong();
 385         } else {
 386             int midByte = in.readByte() & 255;
 387             int loByte = in.readByte() & 255;
 388             long tot = ((hiByte << 16) + (midByte << 8) + loByte);
 389             return (tot * 900) - 4575744000L;
 390         }


 391     }
 392 
 393     static ZoneOffsetTransitionRule readZOTRule(DataInput in) throws IOException {
 394         int data = in.readInt();
 395         Month month = Month.of(data >>> 28);
 396         int dom = ((data & (63 << 22)) >>> 22) - 32;
 397         int dowByte = (data & (7 << 19)) >>> 19;
 398         DayOfWeek dow = dowByte == 0 ? null : DayOfWeek.of(dowByte);
 399         int timeByte = (data & (31 << 14)) >>> 14;
 400         TimeDefinition defn = TimeDefinition.values()[(data & (3 << 12)) >>> 12];
 401         int stdByte = (data & (255 << 4)) >>> 4;
 402         int beforeByte = (data & (3 << 2)) >>> 2;
 403         int afterByte = (data & 3);
 404         LocalTime time = (timeByte == 31 ? LocalTime.ofSecondOfDay(in.readInt()) : LocalTime.of(timeByte % 24, 0));
 405         ZoneOffset std = (stdByte == 255 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds((stdByte - 128) * 900));
 406         ZoneOffset before = (beforeByte == 3 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds(std.getTotalSeconds() + beforeByte * 1800));
 407         ZoneOffset after = (afterByte == 3 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds(std.getTotalSeconds() + afterByte * 1800));
 408         return ZoneOffsetTransitionRule.of(month, dom, dow, time, timeByte == 24, defn, std, before, after);
 409     }
 410 
 411     /////////////////////////ZoneRules --> ZoneInfo/////////////////////////////////
 412 
 413     // ZoneInfo starts with UTC1900
 414     private static final long UTC1900 = -2208988800L;
 415     // ZoneInfo ends with   UTC2037
 416     private static final long UTC2037 =
 417         LocalDateTime.of(2038, 1, 1, 0, 0, 0).toEpochSecond(ZoneOffset.UTC) - 1;
 418 
 419     /* Get a ZoneInfo instance.
 420      *
 421      * @param standardTransitions  the standard transitions, not null
 422      * @param standardOffsets  the standard offsets, not null
 423      * @param savingsInstantTransitions  the standard transitions, not null
 424      * @param wallOffsets  the wall offsets, not null
 425      * @param lastRules  the recurring last rules, size 15 or less, not null
 426      */
 427     private static ZoneInfo getZoneInfo(String zoneId,
 428                                         long[] standardTransitions,
 429                                         int[] standardOffsets,
 430                                         long[] savingsInstantTransitions,
 431                                         int[] wallOffsets,
 432                                         ZoneOffsetTransitionRule[] lastRules) {
 433         int rawOffset = 0;
 434         int dstSavings = 0;
 435         int checksum = 0;
 436         int[] params = null;
 437         boolean willGMTOffsetChange = false;
 438 
 439         // rawOffset, pick the last one
 440         if (standardTransitions.length > 0)
 441             rawOffset = standardOffsets[standardOffsets.length - 1] * 1000;
 442         else
 443             rawOffset = standardOffsets[0] * 1000;
 444 
 445         // transitions, offsets;
 446         long[] transitions = null;
 447         int[]  offsets = null;
 448         int    nOffsets = 0;
 449         int    nTrans = 0;
 450 
 451         if (savingsInstantTransitions.length != 0) {
 452             transitions = new long[250];
 453             offsets = new int[100];    // TBD: ZoneInfo actually can't handle
 454                                        // offsets.length > 16 (4-bit index limit)
 455             // last year in trans table
 456             // It should not matter to use before or after offset for year
 457             int lastyear = LocalDateTime.ofEpochSecond(
 458                 savingsInstantTransitions[savingsInstantTransitions.length - 1], 0,
 459                 ZoneOffset.ofTotalSeconds(wallOffsets[savingsInstantTransitions.length - 1])).getYear();
 460             // int lastyear = savingsLocalTransitions[savingsLocalTransitions.length - 1].getYear();
 461 
 462             int i = 0, k = 1;
 463             while (i < savingsInstantTransitions.length &&
 464                    savingsInstantTransitions[i] < UTC1900) {
 465                  i++;     // skip any date before UTC1900
 466             }
 467             if (i < savingsInstantTransitions.length) {
 468                 // javazic writes the last GMT offset into index 0!
 469                 if (i < savingsInstantTransitions.length) {
 470                     offsets[0] = standardOffsets[standardOffsets.length - 1] * 1000;
 471                     nOffsets = 1;
 472                 }
 473                 // ZoneInfo has a beginning entry for 1900.
 474                 // Only add it if this is not the only one in table
 475                 nOffsets = addTrans(transitions, nTrans++, offsets, nOffsets,
 476                                     UTC1900,
 477                                     wallOffsets[i],
 478                                     getStandardOffset(standardTransitions, standardOffsets, UTC1900));
 479             }
 480             for (; i < savingsInstantTransitions.length; i++) {
 481                 //if (savingsLocalTransitions[i * 2].getYear() > LASTYEAR) {
 482                 if (savingsInstantTransitions[i] > UTC2037) {
 483                     // no trans beyond LASTYEAR
 484                     lastyear = LASTYEAR;
 485                     break;
 486                 }
 487                 long trans = savingsInstantTransitions[i];
 488                 while (k < standardTransitions.length) {
 489                     // some standard offset transitions don't exist in
 490                     // savingInstantTrans, if the offset "change" doesn't
 491                     // really change the "effectiveWallOffset". For example
 492                     // the 1999/2000 pair in Zone Arg/Buenos_Aires, in which
 493                     // the daylightsaving "happened" but it actually does
 494                     //  not result in the timezone switch. ZoneInfo however
 495                     // needs them in its transitions table
 496                     long trans_s = standardTransitions[k];
 497                     if (trans_s >= UTC1900) {
 498                         if (trans_s > trans)
 499                             break;
 500                         if (trans_s < trans) {
 501                             if (nOffsets + 2 >= offsets.length) {
 502                                 offsets = Arrays.copyOf(offsets, offsets.length + 100);
 503                             }
 504                             if (nTrans + 1 >= transitions.length) {
 505                                 transitions = Arrays.copyOf(transitions, transitions.length + 100);
 506                             }
 507                             nOffsets = addTrans(transitions, nTrans++, offsets, nOffsets,
 508                                                 trans_s,
 509                                                 wallOffsets[i],
 510                                                 standardOffsets[k+1]);
 511                         }
 512                     }
 513                     k++;
 514                 }
 515                 if (nOffsets + 2 >= offsets.length) {
 516                     offsets = Arrays.copyOf(offsets, offsets.length + 100);
 517                 }
 518                 if (nTrans + 1 >= transitions.length) {
 519                     transitions = Arrays.copyOf(transitions, transitions.length + 100);
 520                 }
 521                 nOffsets = addTrans(transitions, nTrans++, offsets, nOffsets,
 522                                     trans,
 523                                     wallOffsets[i + 1],
 524                                     getStandardOffset(standardTransitions, standardOffsets, trans));
 525             }
 526             // append any leftover standard trans
 527             while (k < standardTransitions.length) {
 528                 long trans = standardTransitions[k];
 529                 if (trans >= UTC1900) {
 530                     int offset = wallOffsets[i];
 531                     int offsetIndex = indexOf(offsets, 0, nOffsets, offset);
 532                     if (offsetIndex == nOffsets)
 533                         nOffsets++;
 534                     transitions[nTrans++] = ((trans * 1000) << TRANSITION_NSHIFT) |
 535                                             (offsetIndex & OFFSET_MASK);
 536                 }
 537                 k++;
 538             }
 539             if (lastRules.length > 1) {
 540                 // fill the gap between the last trans until LASTYEAR
 541                 while (lastyear++ < LASTYEAR) {
 542                     for (ZoneOffsetTransitionRule zotr : lastRules) {
 543                         ZoneOffsetTransition zot = zotr.createTransition(lastyear);
 544                         //long trans = zot.getDateTimeBefore().toEpochSecond();
 545                         long trans = zot.toEpochSecond();
 546                         if (nOffsets + 2 >= offsets.length) {
 547                             offsets = Arrays.copyOf(offsets, offsets.length + 100);
 548                         }
 549                         if (nTrans + 1 >= transitions.length) {
 550                             transitions = Arrays.copyOf(transitions, transitions.length + 100);
 551                         }
 552                         nOffsets = addTrans(transitions, nTrans++, offsets, nOffsets,
 553                                             trans,
 554                                             zot.getOffsetAfter().getTotalSeconds(),
 555                                             getStandardOffset(standardTransitions, standardOffsets, trans));
 556                     }
 557                 }
 558                 ZoneOffsetTransitionRule startRule =  lastRules[lastRules.length - 2];
 559                 ZoneOffsetTransitionRule endRule =  lastRules[lastRules.length - 1];
 560                 params = new int[10];
 561                 if (startRule.getOffsetBefore().compareTo(startRule.getOffsetAfter()) < 0 &&
 562                     endRule.getOffsetBefore().compareTo(endRule.getOffsetAfter()) > 0) {
 563                     ZoneOffsetTransitionRule tmp;
 564                     tmp = startRule;
 565                     startRule = endRule;
 566                     endRule = tmp;
 567                 }
 568                 params[0] = startRule.getMonth().getValue() - 1;
 569                 // params[1] = startRule.getDayOfMonthIndicator();
 570                 // params[2] = toCalendarDOW[startRule.getDayOfWeek().getValue()];
 571                 int       dom = startRule.getDayOfMonthIndicator();
 572                 DayOfWeek dow = startRule.getDayOfWeek();
 573                 if (dow == null) {
 574                     params[1] = startRule.getDayOfMonthIndicator();
 575                     params[2] = 0;
 576                 } else {
 577                     // ZoneRulesBuilder adjusts < 0 case (-1, for last, don't have
 578                     // "<=" case yet) to positive value if not February (it appears
 579                     // we don't have February cutoff in tzdata table yet)
 580                     // Ideally, if JSR310 can just pass in the nagative and
 581                     // we can then pass in the dom = -1, dow > 0 into ZoneInfo
 582                     //
 583                     // hacking, assume the >=24 is the result of ZRB optimization for
 584                     // "last", it works for now.
 585                     if (dom < 0 || dom >= 24) {
 586                         params[1] = -1;
 587                         params[2] = toCalendarDOW[dow.getValue()];
 588                     } else {
 589                         params[1] = dom;
 590                         // To specify a day of week on or after an exact day of month,
 591                         // set the month to an exact month value, day-of-month to the
 592                         // day on or after which the rule is applied, and day-of-week
 593                         // to a negative Calendar.DAY_OF_WEEK DAY_OF_WEEK field value.
 594                         params[2] = -toCalendarDOW[dow.getValue()];
 595                     }
 596                 }
 597                 params[3] = startRule.getLocalTime().toSecondOfDay() * 1000;
 598                 params[4] = toSTZTime[startRule.getTimeDefinition().ordinal()];
 599 
 600                 params[5] = endRule.getMonth().getValue() - 1;
 601                 // params[6] = endRule.getDayOfMonthIndicator();
 602                 // params[7] = toCalendarDOW[endRule.getDayOfWeek().getValue()];
 603                 dom = endRule.getDayOfMonthIndicator();
 604                 dow = endRule.getDayOfWeek();
 605                 if (dow == null) {
 606                     params[6] = dom;
 607                     params[7] = 0;
 608                 } else {
 609                     // hacking: see comment above
 610                     if (dom < 0 || dom >= 24) {
 611                         params[6] = -1;
 612                         params[7] = toCalendarDOW[dow.getValue()];
 613                     } else {
 614                         params[6] = dom;
 615                         params[7] = -toCalendarDOW[dow.getValue()];
 616                     }
 617                 }
 618                 params[8] = endRule.getLocalTime().toSecondOfDay() * 1000;
 619                 params[9] = toSTZTime[endRule.getTimeDefinition().ordinal()];
 620                 dstSavings = (startRule.getOffsetAfter().getTotalSeconds()
 621                              - startRule.getOffsetBefore().getTotalSeconds()) * 1000;
 622                 // Note: known mismatching -> Asia/Amman
 623                 // ZoneInfo :      startDayOfWeek=5     <= Thursday
 624                 //                 startTime=86400000   <= 24 hours
 625                 // This:           startDayOfWeek=6
 626                 //                 startTime=0
 627                 // Below is the workaround, it probably slows down everyone a little
 628                 if (params[2] == 6 && params[3] == 0 && zoneId.equals("Asia/Amman")) {
 629                     params[2] = 5;
 630                     params[3] = 86400000;
 631                 }
 632             } else if (nTrans > 0) {  // only do this if there is something in table already
 633                 if (lastyear < LASTYEAR) {
 634                     // ZoneInfo has an ending entry for 2037
 635                     long trans = OffsetDateTime.of(LASTYEAR, Month.JANUARY.getValue(), 1, 0, 0, 0, 0,
 636                                                    ZoneOffset.ofTotalSeconds(rawOffset/1000))
 637                                                .toEpochSecond();
 638                     int offsetIndex = indexOf(offsets, 0, nOffsets, rawOffset/1000);
 639                     if (offsetIndex == nOffsets)
 640                         nOffsets++;
 641                     transitions[nTrans++] = (trans * 1000) << TRANSITION_NSHIFT |
 642                                        (offsetIndex & OFFSET_MASK);
 643                 } else if (savingsInstantTransitions.length > 2) {
 644                     // Workaround: create the params based on the last pair for
 645                     // zones like Israel and Iran which have trans defined
 646                     // up until 2037, but no "transition rule" defined
 647                     //
 648                     // Note: Known mismatching for Israel, Asia/Jerusalem/Tel Aviv
 649                     // ZoneInfo:        startMode=3
 650                     //                  startMonth=2
 651                     //                  startDay=26
 652                     //                  startDayOfWeek=6
 653                     //
 654                     // This:            startMode=1
 655                     //                  startMonth=2
 656                     //                  startDay=27
 657                     //                  startDayOfWeek=0
 658                     // these two are actually the same for 2037, the SimpleTimeZone
 659                     // for the last "known" year
 660                     int m = savingsInstantTransitions.length;
 661                     long startTrans = savingsInstantTransitions[m - 2];
 662                     int startOffset = wallOffsets[m - 2 + 1];
 663                     int startStd = getStandardOffset(standardTransitions, standardOffsets, startTrans);
 664                     long endTrans =  savingsInstantTransitions[m - 1];
 665                     int endOffset = wallOffsets[m - 1 + 1];
 666                     int endStd = getStandardOffset(standardTransitions, standardOffsets, endTrans);
 667 
 668                     if (startOffset > startStd && endOffset == endStd) {
 669                         /*
 670                         m = savingsLocalTransitions.length;
 671                         LocalDateTime startLDT = savingsLocalTransitions[m -4];  //gap
 672                         LocalDateTime endLDT = savingsLocalTransitions[m - 1];   //over
 673                          */
 674                         // last - 1 trans
 675                         m = savingsInstantTransitions.length - 2;
 676                         ZoneOffset before = ZoneOffset.ofTotalSeconds(wallOffsets[m]);
 677                         ZoneOffset after = ZoneOffset.ofTotalSeconds(wallOffsets[m + 1]);
 678                         ZoneOffsetTransition trans = ZoneOffsetTransition.of(
 679                             LocalDateTime.ofEpochSecond(savingsInstantTransitions[m], 0, before),
 680                             before,
 681                             after);
 682                         LocalDateTime startLDT;
 683                         if (trans.isGap()) {
 684                             startLDT = trans.getDateTimeBefore();
 685                         } else {
 686                             startLDT = trans.getDateTimeAfter();
 687                         }
 688                         // last trans
 689                         m = savingsInstantTransitions.length - 1;
 690                         before = ZoneOffset.ofTotalSeconds(wallOffsets[m]);
 691                         after = ZoneOffset.ofTotalSeconds(wallOffsets[m + 1]);
 692                         trans = ZoneOffsetTransition.of(
 693                             LocalDateTime.ofEpochSecond(savingsInstantTransitions[m], 0, before),
 694                             before,
 695                             after);
 696                         LocalDateTime endLDT;
 697                         if (trans.isGap()) {
 698                             endLDT = trans.getDateTimeAfter();
 699                         } else {
 700                             endLDT = trans.getDateTimeBefore();
 701                         }
 702                         params = new int[10];
 703                         params[0] = startLDT.getMonthValue() - 1;
 704                         params[1] = startLDT.getDayOfMonth();
 705                         params[2] = 0;
 706                         params[3] = startLDT.toLocalTime().toSecondOfDay() * 1000;
 707                         params[4] = SimpleTimeZone.WALL_TIME;
 708                         params[5] = endLDT.getMonthValue() - 1;
 709                         params[6] = endLDT.getDayOfMonth();
 710                         params[7] = 0;
 711                         params[8] = endLDT.toLocalTime().toSecondOfDay() * 1000;
 712                         params[9] = SimpleTimeZone.WALL_TIME;
 713                         dstSavings = (startOffset - startStd) * 1000;
 714                     }
 715                 }





 716             }
 717             if (transitions != null && transitions.length != nTrans) {
 718                 if (nTrans == 0) {
 719                    transitions = null;
 720                 } else {
 721                     transitions = Arrays.copyOf(transitions, nTrans);

 722                 }
 723             }
 724             if (offsets != null && offsets.length != nOffsets) {
 725                 if (nOffsets == 0) {
 726                    offsets = null;
 727                 } else {
 728                     offsets = Arrays.copyOf(offsets, nOffsets);
 729                 }



 730             }
 731             if (transitions != null) {
 732                 Checksum sum = new Checksum();
 733                 for (i = 0; i < transitions.length; i++) {
 734                     long val = transitions[i];
 735                     int dst = (int)((val >>> DST_NSHIFT) & 0xfL);
 736                     int saving = (dst == 0) ? 0 : offsets[dst];
 737                     int index = (int)(val & OFFSET_MASK);
 738                     int offset = offsets[index];
 739                     long second = (val >> TRANSITION_NSHIFT);
 740                     // javazic uses "index of the offset in offsets",
 741                     // instead of the real offset value itself to
 742                     // calculate the checksum. Have to keep doing
 743                     // the same thing, checksum is part of the
 744                     // ZoneInfo serialization form.
 745                     sum.update(second + index);
 746                     sum.update(index);
 747                     sum.update(dst == 0 ? -1 : dst);
 748                 }
 749                 checksum = (int)sum.getValue();
 750             }
 751         }
 752         return new ZoneInfo(zoneId, rawOffset, dstSavings, checksum, transitions,
 753                             offsets, params, willGMTOffsetChange);
 754     }
 755 
 756     private static int getStandardOffset(long[] standardTransitions,
 757                                          int[] standardOffsets,
 758                                          long epochSec) {
 759         int index  = Arrays.binarySearch(standardTransitions, epochSec);
 760         if (index < 0) {
 761             // switch negative insert position to start of matched range
 762             index = -index - 2;
 763         }
 764         return standardOffsets[index + 1];
 765     }
 766 
 767     private static int toCalendarDOW[] = new int[] {
 768         -1,
 769         Calendar.MONDAY,
 770         Calendar.TUESDAY,
 771         Calendar.WEDNESDAY,
 772         Calendar.THURSDAY,
 773         Calendar.FRIDAY,
 774         Calendar.SATURDAY,
 775         Calendar.SUNDAY
 776     };
 777 
 778     private static int toSTZTime[] = new int[] {
 779         SimpleTimeZone.UTC_TIME,
 780         SimpleTimeZone.WALL_TIME,
 781         SimpleTimeZone.STANDARD_TIME,
 782     };




 783 
 784     private static final long OFFSET_MASK = 0x0fL;
 785     private static final long DST_MASK = 0xf0L;
 786     private static final int  DST_NSHIFT = 4;
 787     private static final int  TRANSITION_NSHIFT = 12;
 788     private static final int  LASTYEAR = 2037;
 789 
 790     // from: 0 for offset lookup, 1 for dstsvings lookup
 791     private static int indexOf(int[] offsets, int from, int nOffsets, int offset) {
 792         offset *= 1000;
 793         for (; from < nOffsets; from++) {
 794             if (offsets[from] == offset)
 795                 return from;
 796         }
 797         offsets[from] = offset;
 798         return from;
 799     }
 800 
 801     // return updated nOffsets
 802     private static int addTrans(long transitions[], int nTrans,
 803                                 int offsets[], int nOffsets,
 804                                 long trans, int offset, int stdOffset) {
 805         int offsetIndex = indexOf(offsets, 0, nOffsets, offset);
 806         if (offsetIndex == nOffsets)
 807             nOffsets++;
 808         int dstIndex = 0;
 809         if (offset != stdOffset) {
 810             dstIndex = indexOf(offsets, 1, nOffsets, offset - stdOffset);
 811             if (dstIndex == nOffsets)
 812                 nOffsets++;
 813         }
 814         transitions[nTrans] = ((trans * 1000) << TRANSITION_NSHIFT) |
 815                               ((dstIndex << DST_NSHIFT) & DST_MASK) |
 816                               (offsetIndex & OFFSET_MASK);
 817         return nOffsets;
 818     }
 819 
 820     /////////////////////////////////////////////////////////////
 821     // ZoneInfo checksum, copy/pasted from javazic
 822     private static class Checksum extends CRC32 {
 823         public void update(int val) {
 824             byte[] b = new byte[4];
 825             b[0] = (byte)((val >>> 24) & 0xff);
 826             b[1] = (byte)((val >>> 16) & 0xff);
 827             b[2] = (byte)((val >>> 8) & 0xff);
 828             b[3] = (byte)(val & 0xff);
 829             update(b);
 830         }
 831         void update(long val) {
 832             byte[] b = new byte[8];
 833             b[0] = (byte)((val >>> 56) & 0xff);
 834             b[1] = (byte)((val >>> 48) & 0xff);
 835             b[2] = (byte)((val >>> 40) & 0xff);
 836             b[3] = (byte)((val >>> 32) & 0xff);
 837             b[4] = (byte)((val >>> 24) & 0xff);
 838             b[5] = (byte)((val >>> 16) & 0xff);
 839             b[6] = (byte)((val >>> 8) & 0xff);
 840             b[7] = (byte)(val & 0xff);
 841             update(b);
 842         }
 843     }














 844 }