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