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