1 /*
   2  * Copyright (c) 2000, 2018, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.File;
  25 import java.io.FileInputStream;
  26 import java.io.FileNotFoundException;
  27 import java.io.IOException;
  28 import java.lang.ref.SoftReference;
  29 import java.nio.file.FileSystems;
  30 import java.util.ArrayList;
  31 import java.util.HashMap;
  32 import java.util.List;
  33 import java.util.Map;
  34 
  35 import sun.util.calendar.*;
  36 
  37 /**
  38  * <code>ZoneInfoFile</code> reads Zone information files in the
  39  * &lt;java.home&gt;/lib/zi directory and provides time zone
  40  * information in the form of a {@link ZoneInfo} object. Also, it
  41  * reads the ZoneInfoMappings file to obtain time zone IDs information
  42  * that is used by the {@link ZoneInfo} class. The directory layout
  43  * and data file formats are as follows.
  44  *
  45  * <p><strong>Directory layout</strong><p>
  46  *
  47  * All zone data files and ZoneInfoMappings are put under the
  48  * &lt;java.home&gt;/lib/zi directory. A path name for a given time
  49  * zone ID is a concatenation of &lt;java.home&gt;/lib/zi/ and the
  50  * time zone ID. (The file separator is replaced with the platform
  51  * dependent value. e.g., '\' for Win32.) An example layout will look
  52  * like as follows.
  53  * <blockquote>
  54  * <pre>
  55  * &lt;java.home&gt;/lib/zi/Africa/Addis_Ababa
  56  *                   /Africa/Dakar
  57  *                   /America/Los_Angeles
  58  *                   /Asia/Singapore
  59  *                   /EET
  60  *                   /Europe/Oslo
  61  *                   /GMT
  62  *                   /Pacific/Galapagos
  63  *                       ...
  64  *                   /ZoneInfoMappings
  65  * </pre>
  66  * </blockquote>
  67  *
  68  * A zone data file has specific information of each zone.
  69  * <code>ZoneInfoMappings</code> has global information of zone IDs so
  70  * that the information can be obtained without instantiating all time
  71  * zones.
  72  *
  73  * <p><strong>File format</strong><p>
  74  *
  75  * Two binary-file formats based on a simple Tag-Length-Value format are used
  76  * to describe TimeZone information. The generic format of a data file is:
  77  * <blockquote>
  78  * <pre>
  79  *    DataFile {
  80  *      u1              magic[7];
  81  *      u1              version;
  82  *      data_item       data[];
  83  *    }
  84  * </pre>
  85  * </blockquote>
  86  * where <code>magic</code> is a magic number identifying a file
  87  * format, <code>version</code> is the format version number, and
  88  * <code>data</code> is one or more <code>data_item</code>s. The
  89  * <code>data_item</code> structure is:
  90  * <blockquote>
  91  * <pre>
  92  *    data_item {
  93  *      u1              tag;
  94  *      u2              length;
  95  *      u1              value[length];
  96  *    }
  97  * </pre>
  98  * </blockquote>
  99  * where <code>tag</code> indicates the data type of the item,
 100  * <code>length</code> is a byte count of the following
 101  * <code>value</code> that is the content of item data.
 102  * <p>
 103  * All data is stored in the big-endian order. There is no boundary
 104  * alignment between date items.
 105  *
 106  * <p><strong>1. ZoneInfo data file</strong><p>
 107  *
 108  * Each ZoneInfo data file consists of the following members.
 109  * <br>
 110  * <blockquote>
 111  * <pre>
 112  *    ZoneInfoDataFile {
 113  *      u1              magic[7];
 114  *      u1              version;
 115  *      SET OF<sup>1</sup> {
 116  *        transition            transitions<sup>2</sup>;
 117  *        offset_table          offsets<sup>2</sup>;
 118  *        simpletimezone        stzparams<sup>2</sup>;
 119  *        raw_offset            rawoffset;
 120  *        dstsaving             dst;
 121  *        checksum              crc32;
 122  *        gmtoffsetwillchange   gmtflag<sup>2</sup>;
 123  *      }
 124  *   }
 125  *   1: an unordered collection of zero or one occurrences of each item
 126  *   2: optional item
 127  * </pre>
 128  * </blockquote>
 129  * <code>magic</code> is a byte-string constant identifying the
 130  * ZoneInfo data file.  This field must be <code>"javazi\0"</code>
 131  * defined as {@link #JAVAZI_LABEL}.
 132  * <p>
 133  * <code>version</code> is the version number of the file format. This
 134  * will be used for compatibility check. This field must be
 135  * <code>0x01</code> in this version.
 136  * <p>
 137  * <code>transition</code>, <code>offset_table</code> and
 138  * <code>simpletimezone</code> have information of time transition
 139  * from the past to the future.  Therefore, these structures don't
 140  * exist if the zone didn't change zone names and haven't applied DST in
 141  * the past, and haven't planned to apply it.  (e.g. Asia/Tokyo zone)
 142  * <p>
 143  * <code>raw_offset</code>, <code>dstsaving</code> and <code>checksum</code>
 144  * exist in every zoneinfo file. They are used by TimeZone.class indirectly.
 145  *
 146  * <p><strong>1.1 <code>transition</code> structure</strong><p><a name="transition"></a>
 147  * <blockquote>
 148  * <pre>
 149  *    transition {
 150  *      u1      tag;              // 0x04 : constant
 151  *      u2      length;           // byte length of whole values
 152  *      s8      value[length/8];  // transitions in `long'
 153  *    }
 154  * </pre>
 155  * </blockquote>
 156  * See {@link ZoneInfo#transitions ZoneInfo.transitions} about the value.
 157  *
 158  * <p><strong>1.2 <code>offset_table</code> structure</strong><p>
 159  * <blockquote>
 160  * <pre>
 161  *    offset_table {
 162  *      u1      tag;              // 0x05 : constant
 163  *      u2      length;           // byte length of whole values
 164  *      s4      value[length/4];  // offset values in `int'
 165  *    }
 166  * </pre>
 167  * </blockquote>
 168  *
 169  * <p><strong>1.3 <code>simpletimezone</code> structure</strong><p>
 170  * See {@link ZoneInfo#simpleTimeZoneParams ZoneInfo.simpleTimeZoneParams}
 171  * about the value.
 172  * <blockquote>
 173  * <pre>
 174  *    simpletimezone {
 175  *      u1      tag;              // 0x06 : constant
 176  *      u2      length;           // byte length of whole values
 177  *      s4      value[length/4];  // SimpleTimeZone parameters
 178  *    }
 179  * </pre>
 180  * </blockquote>
 181  * See {@link ZoneInfo#offsets ZoneInfo.offsets} about the value.
 182  *
 183  * <p><strong>1.4 <code>raw_offset</code> structure</strong><p>
 184  * <blockquote>
 185  * <pre>
 186  *    raw_offset {
 187  *      u1      tag;              // 0x01 : constant
 188  *      u2      length;           // must be 4.
 189  *      s4      value;            // raw GMT offset [millisecond]
 190  *    }
 191  * </pre>
 192  * </blockquote>
 193  * See {@link ZoneInfo#rawOffset ZoneInfo.rawOffset} about the value.
 194  *
 195  * <p><strong>1.5 <code>dstsaving</code> structure</strong><p>
 196  * Value has dstSaving in seconds.
 197  * <blockquote>
 198  * <pre>
 199  *    dstsaving {
 200  *      u1      tag;              // 0x02 : constant
 201  *      u2      length;           // must be 2.
 202  *      s2      value;            // DST save value [second]
 203  *    }
 204  * </pre>
 205  * </blockquote>
 206  * See {@link ZoneInfo#dstSavings ZoneInfo.dstSavings} about value.
 207  *
 208  * <p><strong>1.6 <code>checksum</code> structure</strong><p>
 209  * <blockquote>
 210  * <pre>
 211  *    checksum {
 212  *      u1      tag;              // 0x03 : constant
 213  *      u2      length;           // must be 4.
 214  *      s4      value;            // CRC32 value of transitions
 215  *    }
 216  * </pre>
 217  * </blockquote>
 218  * See {@link ZoneInfo#checksum ZoneInfo.checksum}.
 219  *
 220  * <p><strong>1.7 <code>gmtoffsetwillchange</code> structure</strong><p>
 221  * This record has a flag value for {@link ZoneInfo#rawOffsetWillChange}.
 222  * If this record is not present in a zoneinfo file, 0 is assumed for
 223  * the value.
 224  * <blockquote>
 225  * <pre>
 226  *    gmtoffsetwillchange {
 227  *      u1      tag;             // 0x07 : constant
 228  *      u2      length;          // must be 1.
 229  *      u1      value;           // 1: if the GMT raw offset will change
 230  *                               // in the future, 0, otherwise.
 231  *     }
 232  * </pre>
 233  * </blockquote>
 234  *
 235  *
 236  * <p><strong>2. ZoneInfoMappings file</strong><p>
 237  *
 238  * The ZoneInfoMappings file consists of the following members.
 239  * <br>
 240  * <blockquote>
 241  * <pre>
 242  *    ZoneInfoMappings {
 243  *      u1      magic[7];
 244  *      u1      version;
 245  *      SET OF {
 246  *        versionName                   version;
 247  *        zone_id_table                 zoneIDs;
 248  *        raw_offset_table              rawoffsets;
 249  *        raw_offset_index_table        rawoffsetindices;
 250  *        alias_table                   aliases;
 251  *        excluded_list                 excludedList;
 252  *      }
 253  *   }
 254  * </pre>
 255  * </blockquote>
 256  *
 257  * <code>magic</code> is a byte-string constant which has the file type.
 258  * This field must be <code>"javazm\0"</code> defined as {@link #JAVAZM_LABEL}.
 259  * <p>
 260  * <code>version</code> is the version number of this file
 261  * format. This will be used for compatibility check. This field must
 262  * be <code>0x01</code> in this version.
 263  * <p>
 264  * <code>versionName</code> shows which version of Olson's data has been used
 265  * to generate this ZoneInfoMappings. (e.g. <code>tzdata2000g</code>) <br>
 266  * This field is for trouble-shooting and isn't usually used in runtime.
 267  * <p>
 268  * <code>zone_id_table</code>, <code>raw_offset_index_table</code> and
 269  * <code>alias_table</code> are general information of supported
 270  * zones.
 271  *
 272  * <p><strong>2.1 <code>zone_id_table</code> structure</strong><p>
 273  * The list of zone IDs included in the zi database. The list does
 274  * <em>not</em> include zone IDs, if any, listed in excludedList.
 275  * <br>
 276  * <blockquote>
 277  * <pre>
 278  *    zone_id_table {
 279  *      u1      tag;              // 0x40 : constant
 280  *      u2      length;           // byte length of whole values
 281  *      u2      zone_id_count;
 282  *      zone_id value[zone_id_count];
 283  *    }
 284  *
 285  *    zone_id {
 286  *      u1      byte_length;      // byte length of id
 287  *      u1      id[byte_length];  // zone name string
 288  *    }
 289  * </pre>
 290  * </blockquote>
 291  *
 292  * <p><strong>2.2 <code>raw_offset_table</code> structure</strong><p>
 293  * <br>
 294  * <blockquote>
 295  * <pre>
 296  *    raw_offset_table {
 297  *      u1      tag;              // 0x41 : constant
 298  *      u2      length;           // byte length of whole values
 299  *      s4      value[length/4];  // raw GMT offset in milliseconds
 300  *   }
 301  * </pre>
 302  * </blockquote>
 303  *
 304  * <p><strong>2.3 <code>raw_offset_index_table</code> structure</strong><p>
 305  * <br>
 306  * <blockquote>
 307  * <pre>
 308  *    raw_offset_index_table {
 309  *      u1      tag;              // 0x42 : constant
 310  *      u2      length;           // byte length of whole values
 311  *      u1      value[length];
 312  *    }
 313  * </pre>
 314  * </blockquote>
 315  *
 316  * <p><strong>2.4 <code>alias_table</code> structure</strong><p>
 317  * <br>
 318  * <blockquote>
 319  * <pre>
 320  *   alias_table {
 321  *      u1      tag;              // 0x43 : constant
 322  *      u2      length;           // byte length of whole values
 323  *      u2      nentries;         // number of id-pairs
 324  *      id_pair value[nentries];
 325  *   }
 326  *
 327  *   id_pair {
 328  *      zone_id aliasname;
 329  *      zone_id ID;
 330  *   }
 331  * </pre>
 332  * </blockquote>
 333  *
 334  * <p><strong>2.5 <code>versionName</code> structure</strong><p>
 335  * <br>
 336  * <blockquote>
 337  * <pre>
 338  *   versionName {
 339  *      u1      tag;              // 0x44 : constant
 340  *      u2      length;           // byte length of whole values
 341  *      u1      value[length];
 342  *   }
 343  * </pre>
 344  * </blockquote>
 345  *
 346  * <p><strong>2.6 <code>excludeList</code> structure</strong><p>
 347  * The list of zone IDs whose zones will change their GMT offsets
 348  * (a.k.a. raw offsets) some time in the future. Those IDs must be
 349  * added to the list of zone IDs for getAvailableIDs(). Also they must
 350  * be examined for getAvailableIDs(int) to determine the
 351  * <em>current</em> GMT offsets.
 352  * <br>
 353  * <blockquote>
 354  * <pre>
 355  *   excluded_list {
 356  *      u1      tag;              // 0x45 : constant
 357  *      u2      length;           // byte length of whole values
 358  *      u2      nentries;         // number of zone_ids
 359  *      zone_id value[nentries];  // excluded zone IDs
 360  *   }
 361  * </pre>
 362  * </blockquote>
 363  *
 364  * @since 1.4
 365  */
 366 
 367 public class ZoneInfoFile {
 368 
 369     /**
 370      * The magic number for the ZoneInfo data file format.
 371      */
 372     public static final byte[]  JAVAZI_LABEL = {
 373         (byte)'j', (byte)'a', (byte)'v', (byte)'a', (byte)'z', (byte)'i', (byte)'\0'
 374     };
 375     private static final int    JAVAZI_LABEL_LENGTH = JAVAZI_LABEL.length;
 376 
 377     /**
 378      * The ZoneInfo data file format version number. Must increase
 379      * one when any incompatible change has been made.
 380      */
 381     public static final byte    JAVAZI_VERSION = 0x01;
 382 
 383     /**
 384      * Raw offset data item tag.
 385      */
 386     public static final byte    TAG_RawOffset = 1;
 387 
 388     /**
 389      * Known last Daylight Saving Time save value data item tag.
 390      */
 391     public static final byte    TAG_LastDSTSaving = 2;
 392 
 393     /**
 394      * Checksum data item tag.
 395      */
 396     public static final byte    TAG_CRC32 = 3;
 397 
 398     /**
 399      * Transition data item tag.
 400      */
 401     public static final byte    TAG_Transition = 4;
 402 
 403     /**
 404      * Offset table data item tag.
 405      */
 406     public static final byte    TAG_Offset = 5;
 407 
 408     /**
 409      * SimpleTimeZone parameters data item tag.
 410      */
 411     public static final byte    TAG_SimpleTimeZone = 6;
 412 
 413     /**
 414      * Raw GMT offset will change in the future.
 415      */
 416     public static final byte    TAG_GMTOffsetWillChange = 7;
 417 
 418 
 419     /**
 420      * The ZoneInfoMappings file name.
 421      */
 422     public static final String  JAVAZM_FILE_NAME = "ZoneInfoMappings";
 423 
 424     /**
 425      * The magic number for the ZoneInfoMappings file format.
 426      */
 427     public static final byte[]  JAVAZM_LABEL = {
 428         (byte)'j', (byte)'a', (byte)'v', (byte)'a', (byte)'z', (byte)'m', (byte)'\0'
 429     };
 430     private static final int    JAVAZM_LABEL_LENGTH = JAVAZM_LABEL.length;
 431 
 432     /**
 433      * The ZoneInfoMappings file format version number. Must increase
 434      * one when any incompatible change has been made.
 435      */
 436     public static final byte    JAVAZM_VERSION = 0x01;
 437 
 438     /**
 439      * Time zone IDs data item tag.
 440      */
 441     public static final byte    TAG_ZoneIDs = 64;
 442 
 443     /**
 444      * Raw GMT offsets table data item tag.
 445      */
 446     public static final byte    TAG_RawOffsets = 65;
 447 
 448     /**
 449      * Indices to the raw GMT offset table data item tag.
 450      */
 451     public static final byte    TAG_RawOffsetIndices = 66;
 452 
 453     /**
 454      * Time zone aliases table data item tag.
 455      */
 456     public static final byte    TAG_ZoneAliases = 67;
 457 
 458     /**
 459      * Olson's public zone information version tag.
 460      */
 461     public static final byte    TAG_TZDataVersion = 68;
 462 
 463     /**
 464      * Excluded zones item tag. (Added in Mustang)
 465      */
 466     public static final byte    TAG_ExcludedZones = 69;
 467 
 468     private static Map<String, ZoneInfoOld> zoneInfoObjects = null;
 469 
 470     private static final ZoneInfoOld GMT = new ZoneInfoOld("GMT", 0);
 471 
 472     static String ziDir;
 473 
 474     /**
 475      * Converts the given time zone ID to a platform dependent path
 476      * name. For example, "America/Los_Angeles" is converted to
 477      * "America\Los_Angeles" on Win32.
 478      * @return a modified ID replacing '/' with {@link
 479      * java.io.File#separatorChar File.separatorChar} if needed.
 480      */
 481     public static String getFileName(String ID) {
 482         if (File.separatorChar == '/') {
 483             return ID;
 484         }
 485         return ID.replace('/', File.separatorChar);
 486     }
 487 
 488     /**
 489      * Gets a ZoneInfo with the given GMT offset. The object
 490      * has its ID in the format of GMT{+|-}hh:mm.
 491      *
 492      * @param originalId the given custom id (before normalized such as "GMT+9")
 493      * @param gmtOffset GMT offset <em>in milliseconds</em>
 494      * @return a ZoneInfo constructed with the given GMT offset
 495      */
 496     public static ZoneInfoOld getCustomTimeZone(String originalId, int gmtOffset) {
 497         String id = toCustomID(gmtOffset);
 498 
 499         ZoneInfoOld zi = getFromCache(id);
 500         if (zi == null) {
 501             zi = new ZoneInfoOld(id, gmtOffset);
 502             zi = addToCache(id, zi);
 503             if (!id.equals(originalId)) {
 504                 zi = addToCache(originalId, zi);
 505             }
 506         }
 507         return (ZoneInfoOld) zi.clone();
 508     }
 509 
 510     public static String toCustomID(int gmtOffset) {
 511         char sign;
 512         int offset = gmtOffset / 60000;
 513 
 514         if (offset >= 0) {
 515             sign = '+';
 516         } else {
 517             sign = '-';
 518             offset = -offset;
 519         }
 520         int hh = offset / 60;
 521         int mm = offset % 60;
 522 
 523         char[] buf = new char[] { 'G', 'M', 'T', sign, '0', '0', ':', '0', '0' };
 524         if (hh >= 10) {
 525             buf[4] += hh / 10;
 526         }
 527         buf[5] += hh % 10;
 528         if (mm != 0) {
 529             buf[7] += mm / 10;
 530             buf[8] += mm % 10;
 531         }
 532         return new String(buf);
 533     }
 534 
 535     /**
 536      * @return a ZoneInfo instance created for the specified id, or
 537      * null if there is no time zone data file found for the specified
 538      * id.
 539      */
 540     public static ZoneInfoOld getZoneInfoOld(String id) {
 541         //treat GMT zone as special
 542         if ("GMT".equals(id))
 543             return (ZoneInfoOld) GMT.clone();
 544         ZoneInfoOld zi = getFromCache(id);
 545         if (zi == null) {
 546             Map<String, String> aliases = ZoneInfoOld.getCachedAliasTable();
 547             if (aliases != null && aliases.get(id) != null) {
 548                 return null;
 549             }
 550             zi = createZoneInfoOld(id);
 551             if (zi == null) {
 552                 return null;
 553             }
 554             zi = addToCache(id, zi);
 555         }
 556         return (ZoneInfoOld) zi.clone();
 557     }
 558 
 559     synchronized static ZoneInfoOld getFromCache(String id) {
 560         if (zoneInfoObjects == null) {
 561             return null;
 562         }
 563         return zoneInfoObjects.get(id);
 564     }
 565 
 566     synchronized static ZoneInfoOld addToCache(String id, ZoneInfoOld zi) {
 567         if (zoneInfoObjects == null) {
 568             zoneInfoObjects = new HashMap<>();
 569         } else {
 570             ZoneInfoOld zone = zoneInfoObjects.get(id);
 571             if (zone != null) {
 572                 return zone;
 573             }
 574         }
 575         zoneInfoObjects.put(id, zi);
 576         return zi;
 577     }
 578 
 579     private static ZoneInfoOld createZoneInfoOld(String id) {
 580         byte[] buf = readZoneInfoFile(getFileName(id));
 581         if (buf == null) {
 582             return null;
 583         }
 584 
 585         int index = 0;
 586         int filesize = buf.length;
 587         int rawOffset = 0;
 588         int dstSavings = 0;
 589         int checksum = 0;
 590         boolean willGMTOffsetChange = false;
 591         long[] transitions = null;
 592         int[] offsets = null;
 593         int[] simpleTimeZoneParams = null;
 594 
 595         try {
 596             for (index = 0; index < JAVAZI_LABEL.length; index++) {
 597                 if (buf[index] != JAVAZI_LABEL[index]) {
 598                     System.err.println("ZoneInfoOld: wrong magic number: " + id);
 599                     return null;
 600                 }
 601             }
 602             if (buf[index++] > JAVAZI_VERSION) {
 603                 System.err.println("ZoneInfo: incompatible version ("
 604                                    + buf[index - 1] + "): " + id);
 605                 return null;
 606             }
 607 
 608             while (index < filesize) {
 609                 byte tag = buf[index++];
 610                 int  len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
 611 
 612                 if (filesize < index+len) {
 613                     break;
 614                 }
 615 
 616                 switch (tag) {
 617                 case TAG_CRC32:
 618                     {
 619                         int val = buf[index++] & 0xff;
 620                         val = (val << 8) + (buf[index++] & 0xff);
 621                         val = (val << 8) + (buf[index++] & 0xff);
 622                         val = (val << 8) + (buf[index++] & 0xff);
 623                         checksum = val;
 624                     }
 625                     break;
 626 
 627                 case TAG_LastDSTSaving:
 628                     {
 629                         short val = (short)(buf[index++] & 0xff);
 630                         val = (short)((val << 8) + (buf[index++] & 0xff));
 631                         dstSavings = val * 1000;
 632                     }
 633                     break;
 634 
 635                 case TAG_RawOffset:
 636                     {
 637                         int val = buf[index++] & 0xff;
 638                         val = (val << 8) + (buf[index++] & 0xff);
 639                         val = (val << 8) + (buf[index++] & 0xff);
 640                         val = (val << 8) + (buf[index++] & 0xff);
 641                         rawOffset = val;
 642                     }
 643                     break;
 644 
 645                 case TAG_Transition:
 646                     {
 647                         int n = len / 8;
 648                         transitions = new long[n];
 649                         for (int i = 0; i < n; i ++) {
 650                             long val = buf[index++] & 0xff;
 651                             val = (val << 8) + (buf[index++] & 0xff);
 652                             val = (val << 8) + (buf[index++] & 0xff);
 653                             val = (val << 8) + (buf[index++] & 0xff);
 654                             val = (val << 8) + (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                             transitions[i] = val;
 659                         }
 660                     }
 661                     break;
 662 
 663                 case TAG_Offset:
 664                     {
 665                         int n = len / 4;
 666                         offsets = new int[n];
 667                         for (int i = 0; i < n; i ++) {
 668                             int val = 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                             offsets[i] = val;
 673                         }
 674                     }
 675                     break;
 676 
 677                 case TAG_SimpleTimeZone:
 678                     {
 679                         if (len != 32 && len != 40) {
 680                             System.err.println("ZoneInfo: wrong SimpleTimeZone parameter size");
 681                             return null;
 682                         }
 683                         int n = len / 4;
 684                         simpleTimeZoneParams = new int[n];
 685                         for (int i = 0; i < n; i++) {
 686                             int val = buf[index++] & 0xff;
 687                             val = (val << 8) + (buf[index++] & 0xff);
 688                             val = (val << 8) + (buf[index++] & 0xff);
 689                             val = (val << 8) + (buf[index++] & 0xff);
 690                             simpleTimeZoneParams[i] = val;
 691                         }
 692                     }
 693                     break;
 694 
 695                 case TAG_GMTOffsetWillChange:
 696                     {
 697                         if (len != 1) {
 698                             System.err.println("ZoneInfo: wrong byte length for TAG_GMTOffsetWillChange");
 699                         }
 700                         willGMTOffsetChange = buf[index++] == 1;
 701                     }
 702                     break;
 703 
 704                 default:
 705                     System.err.println("ZoneInfo: unknown tag < " + tag + ">. ignored.");
 706                     index += len;
 707                     break;
 708                 }
 709             }
 710         } catch (Exception e) {
 711             System.err.println("ZoneInfo: corrupted zoneinfo file: " + id);
 712             return null;
 713         }
 714 
 715         if (index != filesize) {
 716             System.err.println("ZoneInfo: wrong file size: " + id);
 717             return null;
 718         }
 719 
 720         return new ZoneInfoOld(id, rawOffset, dstSavings, checksum,
 721                             transitions, offsets, simpleTimeZoneParams,
 722                             willGMTOffsetChange);
 723     }
 724 
 725     private volatile static SoftReference<List<String>> zoneIDs = null;
 726 
 727     static List<String> getZoneIDs() {
 728         List<String> ids = null;
 729         SoftReference<List<String>> cache = zoneIDs;
 730         if (cache != null) {
 731             ids = cache.get();
 732             if (ids != null) {
 733                 return ids;
 734             }
 735         }
 736         byte[] buf = null;
 737         buf = getZoneInfoOldMappings();
 738         int index = JAVAZM_LABEL_LENGTH + 1;
 739         int filesize = buf.length;
 740         try {
 741         loop:
 742             while (index < filesize) {
 743                 byte tag = buf[index++];
 744                 int     len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
 745 
 746                 switch (tag) {
 747                 case TAG_ZoneIDs:
 748                     {
 749                         int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
 750                         ids = new ArrayList<>(n);
 751 
 752                         for (int i = 0; i < n; i++) {
 753                             byte m = buf[index++];
 754                             ids.add(new String(buf, index, m, "UTF-8"));
 755                             index += m;
 756                         }
 757                     }
 758                     break loop;
 759 
 760                 default:
 761                     index += len;
 762                     break;
 763                 }
 764             }
 765         } catch (Exception e) {
 766             System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);
 767         }
 768 
 769         zoneIDs = new SoftReference<>(ids);
 770         return ids;
 771     }
 772 
 773     /**
 774      * @return an alias table in HashMap where a key is an alias ID
 775      * (e.g., "PST") and its value is a real time zone ID (e.g.,
 776      * "America/Los_Angeles").
 777      */
 778     static Map<String, String> getZoneAliases() {
 779         byte[] buf = getZoneInfoOldMappings();
 780         int index = JAVAZM_LABEL_LENGTH + 1;
 781         int filesize = buf.length;
 782         Map<String, String> aliases = null;
 783 
 784         try {
 785         loop:
 786             while (index < filesize) {
 787                 byte tag = buf[index++];
 788                 int     len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
 789 
 790                 switch (tag) {
 791                 case TAG_ZoneAliases:
 792                     {
 793                         int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
 794                         aliases = new HashMap<>(n);
 795                         for (int i = 0; i < n; i++) {
 796                             byte m = buf[index++];
 797                             String name = new String(buf, index, m, "UTF-8");
 798                             index += m;
 799                             m = buf[index++];
 800                             String realName = new String(buf, index, m, "UTF-8");
 801                             index += m;
 802                             aliases.put(name, realName);
 803                         }
 804                     }
 805                     break loop;
 806 
 807                 default:
 808                     index += len;
 809                     break;
 810                 }
 811             }
 812         } catch (Exception e) {
 813             System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);
 814             return null;
 815         }
 816         return aliases;
 817     }
 818 
 819     private volatile static SoftReference<List<String>> excludedIDs = null;
 820     private volatile static boolean hasNoExcludeList = false;
 821 
 822     /**
 823      * @return a List of zone IDs for zones that will change their GMT
 824      * offsets in some future time.
 825      *
 826      * @since 1.6
 827      */
 828     static List<String> getExcludedZones() {
 829         if (hasNoExcludeList) {
 830             return null;
 831         }
 832 
 833         List<String> excludeList = null;
 834 
 835         SoftReference<List<String>> cache = excludedIDs;
 836         if (cache != null) {
 837             excludeList = cache.get();
 838             if (excludeList != null) {
 839                 return excludeList;
 840             }
 841         }
 842 
 843         byte[] buf = getZoneInfoOldMappings();
 844         int index = JAVAZM_LABEL_LENGTH + 1;
 845         int filesize = buf.length;
 846 
 847         try {
 848           loop:
 849             while (index < filesize) {
 850                 byte tag = buf[index++];
 851                 int     len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
 852 
 853                 switch (tag) {
 854                 case TAG_ExcludedZones:
 855                     {
 856                         int n = (buf[index++] << 8) + (buf[index++] & 0xFF);
 857                         excludeList = new ArrayList<>();
 858                         for (int i = 0; i < n; i++) {
 859                             byte m = buf[index++];
 860                             String name = new String(buf, index, m, "UTF-8");
 861                             index += m;
 862                             excludeList.add(name);
 863                         }
 864                     }
 865                     break loop;
 866 
 867                 default:
 868                     index += len;
 869                     break;
 870                 }
 871             }
 872         } catch (Exception e) {
 873             System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);
 874             return null;
 875         }
 876 
 877         if (excludeList != null) {
 878             excludedIDs = new SoftReference<>(excludeList);
 879         } else {
 880             hasNoExcludeList = true;
 881         }
 882         return excludeList;
 883     }
 884 
 885     private volatile static SoftReference<byte[]> rawOffsetIndices = null;
 886 
 887     static byte[] getRawOffsetIndices() {
 888         byte[] indices = null;
 889 
 890         SoftReference<byte[]> cache = rawOffsetIndices;
 891         if (cache != null) {
 892             indices = cache.get();
 893             if (indices != null) {
 894                 return indices;
 895             }
 896         }
 897 
 898         byte[] buf = getZoneInfoOldMappings();
 899         int index = JAVAZM_LABEL_LENGTH + 1;
 900         int filesize = buf.length;
 901 
 902         try {
 903         loop:
 904             while (index < filesize) {
 905                 byte tag = buf[index++];
 906                 int     len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
 907 
 908                 switch (tag) {
 909                 case TAG_RawOffsetIndices:
 910                     {
 911                         indices = new byte[len];
 912                         for (int i = 0; i < len; i++) {
 913                             indices[i] = buf[index++];
 914                         }
 915                     }
 916                     break loop;
 917 
 918                 default:
 919                     index += len;
 920                     break;
 921                 }
 922             }
 923         } catch (ArrayIndexOutOfBoundsException e) {
 924             System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);
 925         }
 926 
 927         rawOffsetIndices = new SoftReference<>(indices);
 928         return indices;
 929     }
 930 
 931     private volatile static SoftReference<int[]> rawOffsets = null;
 932 
 933     static int[] getRawOffsets() {
 934         int[] offsets = null;
 935 
 936         SoftReference<int[]> cache = rawOffsets;
 937         if (cache != null) {
 938             offsets = cache.get();
 939             if (offsets != null) {
 940                 return offsets;
 941             }
 942         }
 943 
 944         byte[] buf = getZoneInfoOldMappings();
 945         int index = JAVAZM_LABEL_LENGTH + 1;
 946         int filesize = buf.length;
 947 
 948         try {
 949         loop:
 950             while (index < filesize) {
 951                 byte tag = buf[index++];
 952                 int     len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF);
 953 
 954                 switch (tag) {
 955                 case TAG_RawOffsets:
 956                     {
 957                         int n = len/4;
 958                         offsets = new int[n];
 959                         for (int i = 0; i < n; i++) {
 960                             int val = buf[index++] & 0xff;
 961                             val = (val << 8) + (buf[index++] & 0xff);
 962                             val = (val << 8) + (buf[index++] & 0xff);
 963                             val = (val << 8) + (buf[index++] & 0xff);
 964                             offsets[i] = val;
 965                         }
 966                     }
 967                     break loop;
 968 
 969                 default:
 970                     index += len;
 971                     break;
 972                 }
 973             }
 974         } catch (ArrayIndexOutOfBoundsException e) {
 975             System.err.println("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME);
 976         }
 977 
 978         rawOffsets = new SoftReference<>(offsets);
 979         return offsets;
 980     }
 981 
 982     private volatile static SoftReference<byte[]> zoneInfoMappings = null;
 983 
 984     private static byte[] getZoneInfoOldMappings() {
 985         byte[] data;
 986         SoftReference<byte[]> cache = zoneInfoMappings;
 987         if (cache != null) {
 988             data = cache.get();
 989             if (data != null) {
 990                 return data;
 991             }
 992         }
 993         data = readZoneInfoFile(JAVAZM_FILE_NAME);
 994         if (data == null) {
 995             throw new RuntimeException("ZoneInfoOldMapping " +
 996                 JAVAZM_FILE_NAME + " either doesn't exist or doesn't have data");
 997         }
 998 
 999         int index;
1000         for (index = 0; index < JAVAZM_LABEL.length; index++) {
1001             if (data[index] != JAVAZM_LABEL[index]) {
1002                 System.err.println("ZoneInfoOld: wrong magic number: " + JAVAZM_FILE_NAME);
1003                 return null;
1004             }
1005         }
1006         if (data[index++] > JAVAZM_VERSION) {
1007             System.err.println("ZoneInfoOld: incompatible version ("
1008                                + data[index - 1] + "): " + JAVAZM_FILE_NAME);
1009             return null;
1010         }
1011 
1012         zoneInfoMappings = new SoftReference<>(data);
1013         return data;
1014     }
1015 
1016     /**
1017      * Reads the specified file under &lt;java.home&gt;/lib/zi into a buffer.
1018      * @return the buffer, or null if any I/O error occurred.
1019      */
1020     private static byte[] readZoneInfoFile(final String fileName) {
1021         if (fileName.indexOf("..") >= 0) {
1022             return null;
1023         }
1024         byte[] buffer = null;
1025         File file = new File(ziDir, fileName);
1026         try {
1027             int filesize = (int)file.length();
1028             if (filesize > 0) {
1029                 FileInputStream fis = new FileInputStream(file);
1030                 buffer = new byte[filesize];
1031                 try {
1032                     if (fis.read(buffer) != filesize) {
1033                         throw new IOException("read error on " + fileName);
1034                     }
1035                 } finally {
1036                     fis.close();
1037                 }
1038             }
1039         } catch (Exception ex) {
1040             if (!(ex instanceof FileNotFoundException) || JAVAZM_FILE_NAME.equals(fileName)) {
1041                 System.err.println("ZoneInfoOld: " + ex.getMessage());
1042             }
1043         }
1044         return buffer;
1045     }
1046 
1047     private ZoneInfoFile() {
1048     }
1049 }