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