/* * 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. */ 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.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import sun.util.calendar.*; /** * ZoneInfoFile reads Zone information files in the * <java.home>/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. * *

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 {@link #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 {@link ZoneInfo#transitions 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 {@link ZoneInfo#simpleTimeZoneParams ZoneInfo.simpleTimeZoneParams} * about the value. *

*
 *    simpletimezone {
 *      u1      tag;              // 0x06 : constant
 *      u2      length;           // byte length of whole values
 *      s4      value[length/4];  // SimpleTimeZone parameters
 *    }
 * 
*
* See {@link ZoneInfo#offsets 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 {@link ZoneInfo#rawOffset 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 {@link ZoneInfo#dstSavings 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 {@link ZoneInfo#checksum ZoneInfo.checksum}. * *

1.7 gmtoffsetwillchange structure

* 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. *

*
 *    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 {@link #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 */ public class ZoneInfoFile { /** * 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. */ public static final byte JAVAZI_VERSION = 0x01; /** * Raw offset data item tag. */ public static final byte TAG_RawOffset = 1; /** * Known last Daylight Saving Time save value data item tag. */ public static final byte TAG_LastDSTSaving = 2; /** * Checksum data item tag. */ public static final byte TAG_CRC32 = 3; /** * Transition data item tag. */ public static final byte TAG_Transition = 4; /** * Offset table data item tag. */ public static final byte TAG_Offset = 5; /** * SimpleTimeZone parameters data item tag. */ public static final byte TAG_SimpleTimeZone = 6; /** * Raw GMT offset will change in the future. */ public static final byte TAG_GMTOffsetWillChange = 7; /** * The ZoneInfoMappings file name. */ public static final String JAVAZM_FILE_NAME = "ZoneInfoMappings"; /** * 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. */ public static final byte JAVAZM_VERSION = 0x01; /** * Time zone IDs data item tag. */ public static final byte TAG_ZoneIDs = 64; /** * Raw GMT offsets table data item tag. */ public static final byte TAG_RawOffsets = 65; /** * Indices to the raw GMT offset table data item tag. */ public static final byte TAG_RawOffsetIndices = 66; /** * Time zone aliases table data item tag. */ public static final byte TAG_ZoneAliases = 67; /** * Olson's public zone information version tag. */ public static final byte TAG_TZDataVersion = 68; /** * Excluded zones item tag. (Added in Mustang) */ public static final byte TAG_ExcludedZones = 69; private static Map zoneInfoObjects = null; private static final ZoneInfoOld GMT = new ZoneInfoOld("GMT", 0); static String ziDir; /** * 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. * * @param originalId the given custom id (before normalized such as "GMT+9") * @param gmtOffset GMT offset in milliseconds * @return a ZoneInfo constructed with the given GMT offset */ public static ZoneInfoOld getCustomTimeZone(String originalId, int gmtOffset) { String id = toCustomID(gmtOffset); ZoneInfoOld zi = getFromCache(id); if (zi == null) { zi = new ZoneInfoOld(id, gmtOffset); zi = addToCache(id, zi); if (!id.equals(originalId)) { zi = addToCache(originalId, zi); } } return (ZoneInfoOld) 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); } /** * @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 ZoneInfoOld getZoneInfoOld(String id) { //treat GMT zone as special if ("GMT".equals(id)) return (ZoneInfoOld) GMT.clone(); ZoneInfoOld zi = getFromCache(id); if (zi == null) { Map aliases = ZoneInfoOld.getCachedAliasTable(); if (aliases != null && aliases.get(id) != null) { return null; } zi = createZoneInfoOld(id); if (zi == null) { return null; } zi = addToCache(id, zi); } return (ZoneInfoOld) zi.clone(); } synchronized static ZoneInfoOld getFromCache(String id) { if (zoneInfoObjects == null) { return null; } return zoneInfoObjects.get(id); } synchronized static ZoneInfoOld addToCache(String id, ZoneInfoOld zi) { if (zoneInfoObjects == null) { zoneInfoObjects = new HashMap<>(); } else { ZoneInfoOld zone = zoneInfoObjects.get(id); if (zone != null) { return zone; } } zoneInfoObjects.put(id, zi); return zi; } private static ZoneInfoOld createZoneInfoOld(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("ZoneInfoOld: 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 ZoneInfoOld(id, rawOffset, dstSavings, checksum, transitions, offsets, simpleTimeZoneParams, willGMTOffsetChange); } private volatile static SoftReference> zoneIDs = null; static List getZoneIDs() { List ids = null; SoftReference> cache = zoneIDs; if (cache != null) { ids = cache.get(); if (ids != null) { return ids; } } byte[] buf = null; buf = getZoneInfoOldMappings(); 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("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME); } zoneIDs = new SoftReference<>(ids); return ids; } /** * @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 getZoneAliases() { byte[] buf = getZoneInfoOldMappings(); int index = JAVAZM_LABEL_LENGTH + 1; int filesize = buf.length; Map 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("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME); return null; } return aliases; } private volatile static SoftReference> excludedIDs = null; private volatile static boolean hasNoExcludeList = false; /** * @return a List of zone IDs for zones that will change their GMT * offsets in some future time. * * @since 1.6 */ static List getExcludedZones() { if (hasNoExcludeList) { return null; } List excludeList = null; SoftReference> cache = excludedIDs; if (cache != null) { excludeList = cache.get(); if (excludeList != null) { return excludeList; } } byte[] buf = getZoneInfoOldMappings(); 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("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME); return null; } if (excludeList != null) { excludedIDs = new SoftReference<>(excludeList); } else { hasNoExcludeList = true; } return excludeList; } private volatile static SoftReference rawOffsetIndices = null; static byte[] getRawOffsetIndices() { byte[] indices = null; SoftReference cache = rawOffsetIndices; if (cache != null) { indices = cache.get(); if (indices != null) { return indices; } } byte[] buf = getZoneInfoOldMappings(); 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("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME); } rawOffsetIndices = new SoftReference<>(indices); return indices; } private volatile static SoftReference rawOffsets = null; static int[] getRawOffsets() { int[] offsets = null; SoftReference cache = rawOffsets; if (cache != null) { offsets = cache.get(); if (offsets != null) { return offsets; } } byte[] buf = getZoneInfoOldMappings(); 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("ZoneInfoOld: corrupted " + JAVAZM_FILE_NAME); } rawOffsets = new SoftReference<>(offsets); return offsets; } private volatile static SoftReference zoneInfoMappings = null; private static byte[] getZoneInfoOldMappings() { byte[] data; SoftReference cache = zoneInfoMappings; if (cache != null) { data = cache.get(); if (data != null) { return data; } } data = readZoneInfoFile(JAVAZM_FILE_NAME); if (data == null) { throw new RuntimeException("ZoneInfoOldMapping " + JAVAZM_FILE_NAME + " either doesn't exist or doesn't have data"); } int index; for (index = 0; index < JAVAZM_LABEL.length; index++) { if (data[index] != JAVAZM_LABEL[index]) { System.err.println("ZoneInfoOld: wrong magic number: " + JAVAZM_FILE_NAME); return null; } } if (data[index++] > JAVAZM_VERSION) { System.err.println("ZoneInfoOld: 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. * @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; File file = new File(ziDir, fileName); try { int filesize = (int)file.length(); if (filesize > 0) { FileInputStream fis = new FileInputStream(file); buffer = new byte[filesize]; try { if (fis.read(buffer) != filesize) { throw new IOException("read error on " + fileName); } } finally { fis.close(); } } } catch (Exception ex) { if (!(ex instanceof FileNotFoundException) || JAVAZM_FILE_NAME.equals(fileName)) { System.err.println("ZoneInfoOld: " + ex.getMessage()); } } return buffer; } private ZoneInfoFile() { } }