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