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