1 /*
2 * Copyright (c) 2000, 2010, 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().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 }