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

package sun.util.calendar;

import  java.io.File;
import  java.io.FileInputStream;
import  java.io.FileNotFoundException;
import  java.io.IOException;
import  java.lang.ref.SoftReference;
import  java.nio.file.FileSystems;
import  java.security.AccessController;
import  java.security.PrivilegedAction;
import  java.security.PrivilegedActionException;
import  java.security.PrivilegedExceptionAction;
import  java.util.ArrayList;
import  java.util.HashMap;
import  java.util.List;
import  java.util.Map;

ZoneInfoFile reads Zone information files in the <java.home>/lib/zi directory and provides time zone information in the form of a ZoneInfo object. Also, it reads the ZoneInfoMappings file to obtain time zone IDs information that is used by the ZoneInfo class. The directory layout and data file formats are as follows.

Directory layout

All zone data files and ZoneInfoMappings are put under the <java.home>/lib/zi directory. A path name for a given time zone ID is a concatenation of <java.home>/lib/zi/ and the time zone ID. (The file separator is replaced with the platform dependent value. e.g., '\' for Win32.) An example layout will look like as follows.

<java.home>/lib/zi/Africa/Addis_Ababa
                  /Africa/Dakar
                  /America/Los_Angeles
                  /Asia/Singapore
                  /EET
                  /Europe/Oslo
                  /GMT
                  /Pacific/Galapagos
                      ...
                  /ZoneInfoMappings
A zone data file has specific information of each zone. ZoneInfoMappings has global information of zone IDs so that the information can be obtained without instantiating all time zones.

File format

Two binary-file formats based on a simple Tag-Length-Value format are used to describe TimeZone information. The generic format of a data file is:

   DataFile {
     u1              magic[7];
     u1              version;
     data_item       data[];
   }
where magic is a magic number identifying a file format, version is the format version number, and data is one or more data_items. The data_item structure is:
   data_item {
     u1              tag;
     u2              length;
     u1              value[length];
   }
where tag indicates the data type of the item, length is a byte count of the following value that is the content of item data.

All data is stored in the big-endian order. There is no boundary alignment between date items.

1. ZoneInfo data file

Each ZoneInfo data file consists of the following members.

   ZoneInfoDataFile {
     u1              magic[7];
     u1              version;
     SET OF1 {
       transition            transitions2;
       offset_table          offsets2;
       simpletimezone        stzparams2;
       raw_offset            rawoffset;
       dstsaving             dst;
       checksum              crc32;
       gmtoffsetwillchange   gmtflag2;
     }
  }
  1: an unordered collection of zero or one occurrences of each item
  2: optional item
magic is a byte-string constant identifying the ZoneInfo data file. This field must be "javazi\0" defined as JAVAZI_LABEL.

version is the version number of the file format. This will be used for compatibility check. This field must be 0x01 in this version.

transition, offset_table and simpletimezone have information of time transition from the past to the future. Therefore, these structures don't exist if the zone didn't change zone names and haven't applied DST in the past, and haven't planned to apply it. (e.g. Asia/Tokyo zone)

raw_offset, dstsaving and checksum exist in every zoneinfo file. They are used by TimeZone.class indirectly.

1.1 transition structure

   transition {
     u1      tag;              // 0x04 : constant
     u2      length;           // byte length of whole values
     s8      value[length/8];  // transitions in `long'
   }
See ZoneInfo.transitions about the value.

1.2 offset_table structure

   offset_table {
     u1      tag;              // 0x05 : constant
     u2      length;           // byte length of whole values
     s4      value[length/4];  // offset values in `int'
   }

1.3 simpletimezone structure

See ZoneInfo.simpleTimeZoneParams about the value.

   simpletimezone {
     u1      tag;              // 0x06 : constant
     u2      length;           // byte length of whole values
     s4      value[length/4];  // SimpleTimeZone parameters
   }
See ZoneInfo.offsets about the value.

1.4 raw_offset structure

   raw_offset {
     u1      tag;              // 0x01 : constant
     u2      length;           // must be 4.
     s4      value;            // raw GMT offset [millisecond]
   }
See ZoneInfo.rawOffset about the value.

1.5 dstsaving structure

Value has dstSaving in seconds.

   dstsaving {
     u1      tag;              // 0x02 : constant
     u2      length;           // must be 2.
     s2      value;            // DST save value [second]
   }
See ZoneInfo.dstSavings about value.

1.6 checksum structure

   checksum {
     u1      tag;              // 0x03 : constant
     u2      length;           // must be 4.
     s4      value;            // CRC32 value of transitions
   }
See ZoneInfo.checksum.

1.7 gmtoffsetwillchange structure

This record has a flag value for ZoneInfo.rawOffsetWillChange. If this record is not present in a zoneinfo file, 0 is assumed for the value.

   gmtoffsetwillchange {
     u1      tag;             // 0x07 : constant
     u2      length;          // must be 1.
     u1      value;           // 1: if the GMT raw offset will change
                              // in the future, 0, otherwise.
    }

2. ZoneInfoMappings file

The ZoneInfoMappings file consists of the following members.

   ZoneInfoMappings {
     u1      magic[7];
     u1      version;
     SET OF {
       versionName                   version;
       zone_id_table                 zoneIDs;
       raw_offset_table              rawoffsets;
       raw_offset_index_table        rawoffsetindices;
       alias_table                   aliases;
       excluded_list                 excludedList;
     }
  }
magic is a byte-string constant which has the file type. This field must be "javazm\0" defined as JAVAZM_LABEL.

version is the version number of this file format. This will be used for compatibility check. This field must be 0x01 in this version.

versionName shows which version of Olson's data has been used to generate this ZoneInfoMappings. (e.g. tzdata2000g)
This field is for trouble-shooting and isn't usually used in runtime.

zone_id_table, raw_offset_index_table and alias_table are general information of supported zones.

2.1 zone_id_table structure

The list of zone IDs included in the zi database. The list does not include zone IDs, if any, listed in excludedList.

   zone_id_table {
     u1      tag;              // 0x40 : constant
     u2      length;           // byte length of whole values
     u2      zone_id_count;
     zone_id value[zone_id_count];
   }
   zone_id {
     u1      byte_length;      // byte length of id
     u1      id[byte_length];  // zone name string
   }

2.2 raw_offset_table structure


   raw_offset_table {
     u1      tag;              // 0x41 : constant
     u2      length;           // byte length of whole values
     s4      value[length/4];  // raw GMT offset in milliseconds
  }

2.3 raw_offset_index_table structure


   raw_offset_index_table {
     u1      tag;              // 0x42 : constant
     u2      length;           // byte length of whole values
     u1      value[length];
   }

2.4 alias_table structure


  alias_table {
     u1      tag;              // 0x43 : constant
     u2      length;           // byte length of whole values
     u2      nentries;         // number of id-pairs
     id_pair value[nentries];
  }
  id_pair {
     zone_id aliasname;
     zone_id ID;
  }

2.5 versionName structure


  versionName {
     u1      tag;              // 0x44 : constant
     u2      length;           // byte length of whole values
     u1      value[length];
  }

2.6 excludeList structure

The list of zone IDs whose zones will change their GMT offsets (a.k.a. raw offsets) some time in the future. Those IDs must be added to the list of zone IDs for getAvailableIDs(). Also they must be examined for getAvailableIDs(int) to determine the current GMT offsets.

  excluded_list {
     u1      tag;              // 0x45 : constant
     u2      length;           // byte length of whole values
     u2      nentries;         // number of zone_ids
     zone_id value[nentries];  // excluded zone IDs
  }
Since:1.4
/** * <code>ZoneInfoFile</code> reads Zone information files in the * &lt;java.home&gt;/lib/zi directory and provides time zone * information in the form of a {@link ZoneInfo} object. Also, it * reads the ZoneInfoMappings file to obtain time zone IDs information * that is used by the {@link ZoneInfo} class. The directory layout * and data file formats are as follows. * * <p><strong>Directory layout</strong><p> * * All zone data files and ZoneInfoMappings are put under the * &lt;java.home&gt;/lib/zi directory. A path name for a given time * zone ID is a concatenation of &lt;java.home&gt;/lib/zi/ and the * time zone ID. (The file separator is replaced with the platform * dependent value. e.g., '\' for Win32.) An example layout will look * like as follows. * <blockquote> * <pre> * &lt;java.home&gt;/lib/zi/Africa/Addis_Ababa * /Africa/Dakar * /America/Los_Angeles * /Asia/Singapore * /EET * /Europe/Oslo * /GMT * /Pacific/Galapagos * ... * /ZoneInfoMappings * </pre> * </blockquote> * * A zone data file has specific information of each zone. * <code>ZoneInfoMappings</code> has global information of zone IDs so * that the information can be obtained without instantiating all time * zones. * * <p><strong>File format</strong><p> * * Two binary-file formats based on a simple Tag-Length-Value format are used * to describe TimeZone information. The generic format of a data file is: * <blockquote> * <pre> * DataFile { * u1 magic[7]; * u1 version; * data_item data[]; * } * </pre> * </blockquote> * where <code>magic</code> is a magic number identifying a file * format, <code>version</code> is the format version number, and * <code>data</code> is one or more <code>data_item</code>s. The * <code>data_item</code> structure is: * <blockquote> * <pre> * data_item { * u1 tag; * u2 length; * u1 value[length]; * } * </pre> * </blockquote> * where <code>tag</code> indicates the data type of the item, * <code>length</code> is a byte count of the following * <code>value</code> that is the content of item data. * <p> * All data is stored in the big-endian order. There is no boundary * alignment between date items. * * <p><strong>1. ZoneInfo data file</strong><p> * * Each ZoneInfo data file consists of the following members. * <br> * <blockquote> * <pre> * ZoneInfoDataFile { * u1 magic[7]; * u1 version; * SET OF<sup>1</sup> { * transition transitions<sup>2</sup>; * offset_table offsets<sup>2</sup>; * simpletimezone stzparams<sup>2</sup>; * raw_offset rawoffset; * dstsaving dst; * checksum crc32; * gmtoffsetwillchange gmtflag<sup>2</sup>; * } * } * 1: an unordered collection of zero or one occurrences of each item * 2: optional item * </pre> * </blockquote> * <code>magic</code> is a byte-string constant identifying the * ZoneInfo data file. This field must be <code>"javazi&#92;0"</code> * defined as {@link #JAVAZI_LABEL}. * <p> * <code>version</code> is the version number of the file format. This * will be used for compatibility check. This field must be * <code>0x01</code> in this version. * <p> * <code>transition</code>, <code>offset_table</code> and * <code>simpletimezone</code> have information of time transition * from the past to the future. Therefore, these structures don't * exist if the zone didn't change zone names and haven't applied DST in * the past, and haven't planned to apply it. (e.g. Asia/Tokyo zone) * <p> * <code>raw_offset</code>, <code>dstsaving</code> and <code>checksum</code> * exist in every zoneinfo file. They are used by TimeZone.class indirectly. * * <p><strong>1.1 <code>transition</code> structure</strong><p><a name="transition"></a> * <blockquote> * <pre> * transition { * u1 tag; // 0x04 : constant * u2 length; // byte length of whole values * s8 value[length/8]; // transitions in `long' * } * </pre> * </blockquote> * See {@link ZoneInfo#transitions ZoneInfo.transitions} about the value. * * <p><strong>1.2 <code>offset_table</code> structure</strong><p> * <blockquote> * <pre> * offset_table { * u1 tag; // 0x05 : constant * u2 length; // byte length of whole values * s4 value[length/4]; // offset values in `int' * } * </pre> * </blockquote> * * <p><strong>1.3 <code>simpletimezone</code> structure</strong><p> * See {@link ZoneInfo#simpleTimeZoneParams ZoneInfo.simpleTimeZoneParams} * about the value. * <blockquote> * <pre> * simpletimezone { * u1 tag; // 0x06 : constant * u2 length; // byte length of whole values * s4 value[length/4]; // SimpleTimeZone parameters * } * </pre> * </blockquote> * See {@link ZoneInfo#offsets ZoneInfo.offsets} about the value. * * <p><strong>1.4 <code>raw_offset</code> structure</strong><p> * <blockquote> * <pre> * raw_offset { * u1 tag; // 0x01 : constant * u2 length; // must be 4. * s4 value; // raw GMT offset [millisecond] * } * </pre> * </blockquote> * See {@link ZoneInfo#rawOffset ZoneInfo.rawOffset} about the value. * * <p><strong>1.5 <code>dstsaving</code> structure</strong><p> * Value has dstSaving in seconds. * <blockquote> * <pre> * dstsaving { * u1 tag; // 0x02 : constant * u2 length; // must be 2. * s2 value; // DST save value [second] * } * </pre> * </blockquote> * See {@link ZoneInfo#dstSavings ZoneInfo.dstSavings} about value. * * <p><strong>1.6 <code>checksum</code> structure</strong><p> * <blockquote> * <pre> * checksum { * u1 tag; // 0x03 : constant * u2 length; // must be 4. * s4 value; // CRC32 value of transitions * } * </pre> * </blockquote> * See {@link ZoneInfo#checksum ZoneInfo.checksum}. * * <p><strong>1.7 <code>gmtoffsetwillchange</code> structure</strong><p> * This record has a flag value for {@link ZoneInfo#rawOffsetWillChange}. * If this record is not present in a zoneinfo file, 0 is assumed for * the value. * <blockquote> * <pre> * gmtoffsetwillchange { * u1 tag; // 0x07 : constant * u2 length; // must be 1. * u1 value; // 1: if the GMT raw offset will change * // in the future, 0, otherwise. * } * </pre> * </blockquote> * * * <p><strong>2. ZoneInfoMappings file</strong><p> * * The ZoneInfoMappings file consists of the following members. * <br> * <blockquote> * <pre> * ZoneInfoMappings { * u1 magic[7]; * u1 version; * SET OF { * versionName version; * zone_id_table zoneIDs; * raw_offset_table rawoffsets; * raw_offset_index_table rawoffsetindices; * alias_table aliases; * excluded_list excludedList; * } * } * </pre> * </blockquote> * * <code>magic</code> is a byte-string constant which has the file type. * This field must be <code>"javazm&#92;0"</code> defined as {@link #JAVAZM_LABEL}. * <p> * <code>version</code> is the version number of this file * format. This will be used for compatibility check. This field must * be <code>0x01</code> in this version. * <p> * <code>versionName</code> shows which version of Olson's data has been used * to generate this ZoneInfoMappings. (e.g. <code>tzdata2000g</code>) <br> * This field is for trouble-shooting and isn't usually used in runtime. * <p> * <code>zone_id_table</code>, <code>raw_offset_index_table</code> and * <code>alias_table</code> are general information of supported * zones. * * <p><strong>2.1 <code>zone_id_table</code> structure</strong><p> * The list of zone IDs included in the zi database. The list does * <em>not</em> include zone IDs, if any, listed in excludedList. * <br> * <blockquote> * <pre> * zone_id_table { * u1 tag; // 0x40 : constant * u2 length; // byte length of whole values * u2 zone_id_count; * zone_id value[zone_id_count]; * } * * zone_id { * u1 byte_length; // byte length of id * u1 id[byte_length]; // zone name string * } * </pre> * </blockquote> * * <p><strong>2.2 <code>raw_offset_table</code> structure</strong><p> * <br> * <blockquote> * <pre> * raw_offset_table { * u1 tag; // 0x41 : constant * u2 length; // byte length of whole values * s4 value[length/4]; // raw GMT offset in milliseconds * } * </pre> * </blockquote> * * <p><strong>2.3 <code>raw_offset_index_table</code> structure</strong><p> * <br> * <blockquote> * <pre> * raw_offset_index_table { * u1 tag; // 0x42 : constant * u2 length; // byte length of whole values * u1 value[length]; * } * </pre> * </blockquote> * * <p><strong>2.4 <code>alias_table</code> structure</strong><p> * <br> * <blockquote> * <pre> * alias_table { * u1 tag; // 0x43 : constant * u2 length; // byte length of whole values * u2 nentries; // number of id-pairs * id_pair value[nentries]; * } * * id_pair { * zone_id aliasname; * zone_id ID; * } * </pre> * </blockquote> * * <p><strong>2.5 <code>versionName</code> structure</strong><p> * <br> * <blockquote> * <pre> * versionName { * u1 tag; // 0x44 : constant * u2 length; // byte length of whole values * u1 value[length]; * } * </pre> * </blockquote> * * <p><strong>2.6 <code>excludeList</code> structure</strong><p> * The list of zone IDs whose zones will change their GMT offsets * (a.k.a. raw offsets) some time in the future. Those IDs must be * added to the list of zone IDs for getAvailableIDs(). Also they must * be examined for getAvailableIDs(int) to determine the * <em>current</em> GMT offsets. * <br> * <blockquote> * <pre> * excluded_list { * u1 tag; // 0x45 : constant * u2 length; // byte length of whole values * u2 nentries; // number of zone_ids * zone_id value[nentries]; // excluded zone IDs * } * </pre> * </blockquote> * * @since 1.4 */
public class ZoneInfoFile {
The magic number for the ZoneInfo data file format.
/** * The magic number for the ZoneInfo data file format. */
public static final byte[] JAVAZI_LABEL = { (byte)'j', (byte)'a', (byte)'v', (byte)'a', (byte)'z', (byte)'i', (byte)'\0' }; private static final int JAVAZI_LABEL_LENGTH = JAVAZI_LABEL.length;
The ZoneInfo data file format version number. Must increase one when any incompatible change has been made.
/** * The ZoneInfo data file format version number. Must increase * one when any incompatible change has been made. */
public static final byte JAVAZI_VERSION = 0x01;
Raw offset data item tag.
/** * Raw offset data item tag. */
public static final byte TAG_RawOffset = 1;
Known last Daylight Saving Time save value data item tag.
/** * Known last Daylight Saving Time save value data item tag. */
public static final byte TAG_LastDSTSaving = 2;
Checksum data item tag.
/** * Checksum data item tag. */
public static final byte TAG_CRC32 = 3;
Transition data item tag.
/** * Transition data item tag. */
public static final byte TAG_Transition = 4;
Offset table data item tag.
/** * Offset table data item tag. */
public static final byte TAG_Offset = 5;
SimpleTimeZone parameters data item tag.
/** * SimpleTimeZone parameters data item tag. */
public static final byte TAG_SimpleTimeZone = 6;
Raw GMT offset will change in the future.
/** * Raw GMT offset will change in the future. */
public static final byte TAG_GMTOffsetWillChange = 7;
The ZoneInfoMappings file name.
/** * The ZoneInfoMappings file name. */
public static final String JAVAZM_FILE_NAME = "ZoneInfoMappings";
The magic number for the ZoneInfoMappings file format.
/** * The magic number for the ZoneInfoMappings file format. */
public static final byte[] JAVAZM_LABEL = { (byte)'j', (byte)'a', (byte)'v', (byte)'a', (byte)'z', (byte)'m', (byte)'\0' }; private static final int JAVAZM_LABEL_LENGTH = JAVAZM_LABEL.length;
The ZoneInfoMappings file format version number. Must increase one when any incompatible change has been made.
/** * The ZoneInfoMappings file format version number. Must increase * one when any incompatible change has been made. */
public static final byte JAVAZM_VERSION = 0x01;
Time zone IDs data item tag.
/** * Time zone IDs data item tag. */
public static final byte TAG_ZoneIDs = 64;
Raw GMT offsets table data item tag.
/** * Raw GMT offsets table data item tag. */
public static final byte TAG_RawOffsets = 65;
Indices to the raw GMT offset table data item tag.
/** * Indices to the raw GMT offset table data item tag. */
public static final byte TAG_RawOffsetIndices = 66;
Time zone aliases table data item tag.
/** * Time zone aliases table data item tag. */
public static final byte TAG_ZoneAliases = 67;
Olson's public zone information version tag.
/** * Olson's public zone information version tag. */
public static final byte TAG_TZDataVersion = 68;
Excluded zones item tag. (Added in Mustang)
/** * Excluded zones item tag. (Added in Mustang) */
public static final byte TAG_ExcludedZones = 69; private static Map<String, ZoneInfo> zoneInfoObjects = null; private static final ZoneInfo GMT = new ZoneInfo("GMT", 0); private static final String ziDir = AccessController.doPrivileged( new PrivilegedAction<String>() { public String run() { String zi = System.getProperty("java.home") + File.separator + "lib" + File.separator + "zi"; try { zi = FileSystems.getDefault().getPath(zi).toRealPath().toString(); } catch(Exception e) { } return zi; } });
Converts the given time zone ID to a platform dependent path name. For example, "America/Los_Angeles" is converted to "America\Los_Angeles" on Win32.
Returns:a modified ID replacing '/' with File.separatorChar if needed.
/** * Converts the given time zone ID to a platform dependent path * name. For example, "America/Los_Angeles" is converted to * "America\Los_Angeles" on Win32. * @return a modified ID replacing '/' with {@link * java.io.File#separatorChar File.separatorChar} if needed. */
public static String getFileName(String ID) { if (File.separatorChar == '/') { return ID; } return ID.replace('/', File.separatorChar); }
Gets a ZoneInfo with the given GMT offset. The object has its ID in the format of GMT{+|-}hh:mm.
Params:
  • originalId – the given custom id (before normalized such as "GMT+9")
  • gmtOffset – GMT offset in milliseconds
Returns:a ZoneInfo constructed with the given GMT offset
/** * Gets a ZoneInfo with the given GMT offset. The object * has its ID in the format of GMT{+|-}hh:mm. * * @param originalId the given custom id (before normalized such as "GMT+9") * @param gmtOffset GMT offset <em>in milliseconds</em> * @return a ZoneInfo constructed with the given GMT offset */
public static ZoneInfo getCustomTimeZone(String originalId, int gmtOffset) { String id = toCustomID(gmtOffset); ZoneInfo zi = getFromCache(id); if (zi == null) { zi = new ZoneInfo(id, gmtOffset); zi = addToCache(id, zi); if (!id.equals(originalId)) { zi = addToCache(originalId, zi); } } return (ZoneInfo) zi.clone(); } public static String toCustomID(int gmtOffset) { char sign; int offset = gmtOffset / 60000; if (offset >= 0) { sign = '+'; } else { sign = '-'; offset = -offset; } int hh = offset / 60; int mm = offset % 60; char[] buf = new char[] { 'G', 'M', 'T', sign, '0', '0', ':', '0', '0' }; if (hh >= 10) { buf[4] += hh / 10; } buf[5] += hh % 10; if (mm != 0) { buf[7] += mm / 10; buf[8] += mm % 10; } return new String(buf); }
Returns:a ZoneInfo instance created for the specified id, or null if there is no time zone data file found for the specified id.
/** * @return a ZoneInfo instance created for the specified id, or * null if there is no time zone data file found for the specified * id. */
public static ZoneInfo getZoneInfo(String id) { //treat GMT zone as special if ("GMT".equals(id)) return (ZoneInfo) GMT.clone(); ZoneInfo zi = getFromCache(id); if (zi == null) { Map<String, String> aliases = ZoneInfo.getCachedAliasTable(); if (aliases != null && aliases.get(id) != null) { return null; } zi = createZoneInfo(id); if (zi == null) { return null; } zi = addToCache(id, zi); } return (ZoneInfo) zi.clone(); } synchronized static ZoneInfo getFromCache(String id) { if (zoneInfoObjects == null) { return null; } return zoneInfoObjects.get(id); } synchronized static ZoneInfo addToCache(String id, ZoneInfo zi) { if (zoneInfoObjects == null) { zoneInfoObjects = new HashMap<>(); } else { ZoneInfo zone = zoneInfoObjects.get(id); if (zone != null) { return zone; } } zoneInfoObjects.put(id, zi); return zi; } private static ZoneInfo createZoneInfo(String id) { byte[] buf = readZoneInfoFile(getFileName(id)); if (buf == null) { return null; } int index = 0; int filesize = buf.length; int rawOffset = 0; int dstSavings = 0; int checksum = 0; boolean willGMTOffsetChange = false; long[] transitions = null; int[] offsets = null; int[] simpleTimeZoneParams = null; try { for (index = 0; index < JAVAZI_LABEL.length; index++) { if (buf[index] != JAVAZI_LABEL[index]) { System.err.println("ZoneInfo: wrong magic number: " + id); return null; } } if (buf[index++] > JAVAZI_VERSION) { System.err.println("ZoneInfo: incompatible version (" + buf[index - 1] + "): " + id); return null; } while (index < filesize) { byte tag = buf[index++]; int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF); if (filesize < index+len) { break; } switch (tag) { case TAG_CRC32: { int val = buf[index++] & 0xff; val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); checksum = val; } break; case TAG_LastDSTSaving: { short val = (short)(buf[index++] & 0xff); val = (short)((val << 8) + (buf[index++] & 0xff)); dstSavings = val * 1000; } break; case TAG_RawOffset: { int val = buf[index++] & 0xff; val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); rawOffset = val; } break; case TAG_Transition: { int n = len / 8; transitions = new long[n]; for (int i = 0; i < n; i ++) { long val = buf[index++] & 0xff; val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); transitions[i] = val; } } break; case TAG_Offset: { int n = len / 4; offsets = new int[n]; for (int i = 0; i < n; i ++) { int val = buf[index++] & 0xff; val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); offsets[i] = val; } } break; case TAG_SimpleTimeZone: { if (len != 32 && len != 40) { System.err.println("ZoneInfo: wrong SimpleTimeZone parameter size"); return null; } int n = len / 4; simpleTimeZoneParams = new int[n]; for (int i = 0; i < n; i++) { int val = buf[index++] & 0xff; val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); simpleTimeZoneParams[i] = val; } } break; case TAG_GMTOffsetWillChange: { if (len != 1) { System.err.println("ZoneInfo: wrong byte length for TAG_GMTOffsetWillChange"); } willGMTOffsetChange = buf[index++] == 1; } break; default: System.err.println("ZoneInfo: unknown tag < " + tag + ">. ignored."); index += len; break; } } } catch (Exception e) { System.err.println("ZoneInfo: corrupted zoneinfo file: " + id); return null; } if (index != filesize) { System.err.println("ZoneInfo: wrong file size: " + id); return null; } return new ZoneInfo(id, rawOffset, dstSavings, checksum, transitions, offsets, simpleTimeZoneParams, willGMTOffsetChange); } private volatile static SoftReference<List<String>> zoneIDs = null; static List<String> getZoneIDs() { List<String> ids = null; SoftReference<List<String>> cache = zoneIDs; if (cache != null) { ids = cache.get(); if (ids != null) { return ids; } } byte[] buf = null; buf = getZoneInfoMappings(); int index = JAVAZM_LABEL_LENGTH + 1; int filesize = buf.length; try { loop: while (index < filesize) { byte tag = buf[index++]; int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF); switch (tag) { case TAG_ZoneIDs: { int n = (buf[index++] << 8) + (buf[index++] & 0xFF); ids = new ArrayList<>(n); for (int i = 0; i < n; i++) { byte m = buf[index++]; ids.add(new String(buf, index, m, "UTF-8")); index += m; } } break loop; default: index += len; break; } } } catch (Exception e) { System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME); } zoneIDs = new SoftReference<>(ids); return ids; }
Returns:an alias table in HashMap where a key is an alias ID (e.g., "PST") and its value is a real time zone ID (e.g., "America/Los_Angeles").
/** * @return an alias table in HashMap where a key is an alias ID * (e.g., "PST") and its value is a real time zone ID (e.g., * "America/Los_Angeles"). */
static Map<String, String> getZoneAliases() { byte[] buf = getZoneInfoMappings(); int index = JAVAZM_LABEL_LENGTH + 1; int filesize = buf.length; Map<String, String> aliases = null; try { loop: while (index < filesize) { byte tag = buf[index++]; int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF); switch (tag) { case TAG_ZoneAliases: { int n = (buf[index++] << 8) + (buf[index++] & 0xFF); aliases = new HashMap<>(n); for (int i = 0; i < n; i++) { byte m = buf[index++]; String name = new String(buf, index, m, "UTF-8"); index += m; m = buf[index++]; String realName = new String(buf, index, m, "UTF-8"); index += m; aliases.put(name, realName); } } break loop; default: index += len; break; } } } catch (Exception e) { System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME); return null; } return aliases; } private volatile static SoftReference<List<String>> excludedIDs = null; private volatile static boolean hasNoExcludeList = false;
Returns:a List of zone IDs for zones that will change their GMT offsets in some future time.
Since:1.6
/** * @return a List of zone IDs for zones that will change their GMT * offsets in some future time. * * @since 1.6 */
static List<String> getExcludedZones() { if (hasNoExcludeList) { return null; } List<String> excludeList = null; SoftReference<List<String>> cache = excludedIDs; if (cache != null) { excludeList = cache.get(); if (excludeList != null) { return excludeList; } } byte[] buf = getZoneInfoMappings(); int index = JAVAZM_LABEL_LENGTH + 1; int filesize = buf.length; try { loop: while (index < filesize) { byte tag = buf[index++]; int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF); switch (tag) { case TAG_ExcludedZones: { int n = (buf[index++] << 8) + (buf[index++] & 0xFF); excludeList = new ArrayList<>(); for (int i = 0; i < n; i++) { byte m = buf[index++]; String name = new String(buf, index, m, "UTF-8"); index += m; excludeList.add(name); } } break loop; default: index += len; break; } } } catch (Exception e) { System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME); return null; } if (excludeList != null) { excludedIDs = new SoftReference<>(excludeList); } else { hasNoExcludeList = true; } return excludeList; } private volatile static SoftReference<byte[]> rawOffsetIndices = null; static byte[] getRawOffsetIndices() { byte[] indices = null; SoftReference<byte[]> cache = rawOffsetIndices; if (cache != null) { indices = cache.get(); if (indices != null) { return indices; } } byte[] buf = getZoneInfoMappings(); int index = JAVAZM_LABEL_LENGTH + 1; int filesize = buf.length; try { loop: while (index < filesize) { byte tag = buf[index++]; int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF); switch (tag) { case TAG_RawOffsetIndices: { indices = new byte[len]; for (int i = 0; i < len; i++) { indices[i] = buf[index++]; } } break loop; default: index += len; break; } } } catch (ArrayIndexOutOfBoundsException e) { System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME); } rawOffsetIndices = new SoftReference<>(indices); return indices; } private volatile static SoftReference<int[]> rawOffsets = null; static int[] getRawOffsets() { int[] offsets = null; SoftReference<int[]> cache = rawOffsets; if (cache != null) { offsets = cache.get(); if (offsets != null) { return offsets; } } byte[] buf = getZoneInfoMappings(); int index = JAVAZM_LABEL_LENGTH + 1; int filesize = buf.length; try { loop: while (index < filesize) { byte tag = buf[index++]; int len = ((buf[index++] & 0xFF) << 8) + (buf[index++] & 0xFF); switch (tag) { case TAG_RawOffsets: { int n = len/4; offsets = new int[n]; for (int i = 0; i < n; i++) { int val = buf[index++] & 0xff; val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); val = (val << 8) + (buf[index++] & 0xff); offsets[i] = val; } } break loop; default: index += len; break; } } } catch (ArrayIndexOutOfBoundsException e) { System.err.println("ZoneInfo: corrupted " + JAVAZM_FILE_NAME); } rawOffsets = new SoftReference<>(offsets); return offsets; } private volatile static SoftReference<byte[]> zoneInfoMappings = null; private static byte[] getZoneInfoMappings() { byte[] data; SoftReference<byte[]> cache = zoneInfoMappings; if (cache != null) { data = cache.get(); if (data != null) { return data; } } data = readZoneInfoFile(JAVAZM_FILE_NAME); if (data == null) { return null; } int index; for (index = 0; index < JAVAZM_LABEL.length; index++) { if (data[index] != JAVAZM_LABEL[index]) { System.err.println("ZoneInfo: wrong magic number: " + JAVAZM_FILE_NAME); return null; } } if (data[index++] > JAVAZM_VERSION) { System.err.println("ZoneInfo: incompatible version (" + data[index - 1] + "): " + JAVAZM_FILE_NAME); return null; } zoneInfoMappings = new SoftReference<>(data); return data; }
Reads the specified file under <java.home>/lib/zi into a buffer.
Returns:the buffer, or null if any I/O error occurred.
/** * Reads the specified file under &lt;java.home&gt;/lib/zi into a buffer. * @return the buffer, or null if any I/O error occurred. */
private static byte[] readZoneInfoFile(final String fileName) { if (fileName.indexOf("..") >= 0) { return null; } byte[] buffer = null; try { buffer = AccessController.doPrivileged(new PrivilegedExceptionAction<byte[]>() { public byte[] run() throws IOException { File file = new File(ziDir, fileName); byte[] buf = null; int filesize = (int)file.length(); if (filesize > 0) { FileInputStream fis = new FileInputStream(file); buf = new byte[filesize]; try { if (fis.read(buf) != filesize) { throw new IOException("read error on " + fileName); } } finally { fis.close(); } } return buf; } }); } catch (PrivilegedActionException e) { Exception ex = e.getException(); if (!(ex instanceof FileNotFoundException) || JAVAZM_FILE_NAME.equals(fileName)) { System.err.println("ZoneInfo: " + ex.getMessage()); } } return buffer; } private ZoneInfoFile() { } }