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 }