001: /*
002: * @(#)Timezone.java 1.6 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package sun.tools.javazic;
028:
029: import java.util.ArrayList;
030:
031: /**
032: * Timezone represents all information of a single point of time to
033: * generate its time zone database.
034: *
035: * @since 1.4
036: */
037: class Timezone {
038: /**
039: * zone name of this time zone
040: */
041: private String name;
042:
043: /**
044: * transition time values in UTC (millisecond)
045: */
046: private ArrayList transitions;
047:
048: /**
049: * All offset values in millisecond
050: * @see sun.util.calendar.ZoneInfo
051: */
052: private ArrayList offsets;
053:
054: /**
055: * Indices of GMT offset values (both raw and raw+saving)
056: * at transitions
057: */
058: private ArrayList gmtOffsets;
059:
060: /**
061: * Indices of regular or "direct" saving time values
062: * at transitions
063: */
064: private ArrayList dstOffsets;
065:
066: /**
067: * Zone records of this time zone
068: */
069: private ArrayList usedZoneRecs;
070:
071: /**
072: * Rule records referred to by this time zone
073: */
074: private ArrayList usedRuleRecs;
075:
076: /**
077: * Type of DST rules in this time zone
078: */
079: private int dstType;
080: static final int UNDEF_DST = 0; // DST type not set yet
081: static final int NO_DST = 1; // never observed DST
082: static final int LAST_DST = 2; // last rule ends in DST (all year round DST-only)
083: static final int X_DST = 3; // used to observe DST
084: static final int DST = 4; // observing DST regularly
085:
086: /**
087: * Raw GMT offset of this time zone in the last rule
088: */
089: private int rawOffset;
090:
091: /**
092: * The CRC32 value of the transitions data
093: */
094: private int crc32;
095:
096: /**
097: * The last ZoneRec
098: */
099: private ZoneRec lastZoneRec;
100:
101: /**
102: * The last DST rules. lastRules[0] is the DST start
103: * rule. lastRules[1] is the DST end rules.
104: */
105: private ArrayList lastRules;
106:
107: /**
108: * The amount of DST saving value (millisecond) in the last DST
109: * rule.
110: */
111: private int lastSaving;
112:
113: /**
114: * true if the raw offset will change in the future time.
115: */
116: private boolean willRawOffsetChange = false;
117:
118: /**
119: * Constracts a Timezone object with the given zone name.
120: * @param name the zone name
121: */
122: Timezone(String name) {
123: this .name = name;
124: }
125:
126: /**
127: * @return the number of transitions
128: */
129: int getNTransitions() {
130: if (transitions == null) {
131: return 0;
132: }
133: return transitions.size();
134: }
135:
136: /**
137: * @return the zone name
138: */
139: String getName() {
140: return name;
141: }
142:
143: /**
144: * Returns the list of all rule records that have been referred to
145: * by this time zone.
146: * @return the rule records list
147: */
148: ArrayList getRules() {
149: return usedRuleRecs;
150: }
151:
152: /**
153: * Returns the list of all zone records that have been referred to
154: * by this time zone.
155: * @return the zone records list
156: */
157: ArrayList getZones() {
158: return usedZoneRecs;
159: }
160:
161: /**
162: * @return the transition table (list)
163: */
164: ArrayList getTransitions() {
165: return transitions;
166: }
167:
168: /**
169: * @return the offsets list
170: */
171: ArrayList getOffsets() {
172: return offsets;
173: }
174:
175: /**
176: * @return the DST saving offsets list
177: */
178: ArrayList getDstOffsets() {
179: return dstOffsets;
180: }
181:
182: /**
183: * @return the GMT offsets list
184: */
185: ArrayList getGmtOffsets() {
186: return gmtOffsets;
187: }
188:
189: /**
190: * @return the checksum (crc32) value of the trasition table
191: */
192: int getCRC32() {
193: return crc32;
194: }
195:
196: /**
197: * @return true if the GMT offset of this time zone would change
198: * after the time zone database has been generated, false, otherwise.
199: */
200: boolean willGMTOffsetChange() {
201: return willRawOffsetChange;
202: }
203:
204: /**
205: * @return the last known GMT offset value in milliseconds
206: */
207: int getRawOffset() {
208: return rawOffset;
209: }
210:
211: /**
212: * Sets time zone's GMT offset to <code>offset</code>.
213: * @param offset the GMT offset value in milliseconds
214: */
215: void setRawOffset(int offset) {
216: rawOffset = offset;
217: }
218:
219: /**
220: * Sets time zone's GMT offset value to <code>offset</code>. If
221: * <code>startTime</code> is future time, then the {@link
222: * #willRawOffsetChange} value is set to true.
223: * @param offset the GMT offset value in milliseconds
224: * @param startTime the UTC time at which the GMT offset is in effective
225: */
226: void setRawOffset(int offset, long startTime) {
227: // if this rawOffset is for the future time, let the run-time
228: // look for the current GMT offset.
229: if (startTime > Time.getCurrentTime()) {
230: willRawOffsetChange = true;
231: }
232: setRawOffset(offset);
233: }
234:
235: /**
236: * Adds the specified transition information to the end of the transition table.
237: * @param time the UTC time at which this transition happens
238: * @param offset the total amount of the offset from GMT in milliseconds
239: * @param dstOffset the amount of time in milliseconds saved at this transition
240: */
241: void addTransition(long time, int offset, int dstOffset) {
242: if (transitions == null) {
243: transitions = new ArrayList();
244: offsets = new ArrayList();
245: dstOffsets = new ArrayList();
246: }
247: transitions.add(new Long(time));
248: offsets.add(new Integer(offset));
249: dstOffsets.add(new Integer(dstOffset));
250: }
251:
252: /**
253: * Sets the type of historical daylight saving time
254: * observation. For example, China used to observed daylight
255: * saving time, but it no longer does. Then, X_DST is set to the
256: * China time zone.
257: * @param type the type of daylight saving time
258: */
259: void setDSTType(int type) {
260: dstType = type;
261: }
262:
263: /**
264: * @return the type of historical daylight saving time
265: * observation.
266: */
267: int getDSTType() {
268: return dstType;
269: }
270:
271: /**
272: * Addds the spcified zone record to the zone records list.
273: * @param rec the zone record
274: */
275: void addUsedRec(ZoneRec rec) {
276: if (usedZoneRecs == null) {
277: usedZoneRecs = new ArrayList();
278: }
279: usedZoneRecs.add(rec);
280: }
281:
282: /**
283: * Adds the specified rule record to the rule records list.
284: * @param rec the rule record
285: */
286: void addUsedRec(RuleRec rec) {
287: if (usedRuleRecs == null) {
288: usedRuleRecs = new ArrayList();
289: }
290: // if the last used rec is the same as the given rec, avoid
291: // putting the same rule.
292: int n = usedRuleRecs.size();
293: for (int i = 0; i < n; i++) {
294: if (usedRuleRecs.get(i).equals(rec)) {
295: return;
296: }
297: }
298: usedRuleRecs.add(rec);
299: }
300:
301: /**
302: * Sets the last zone record for this time zone.
303: * @param the last zone record
304: */
305: void setLastZoneRec(ZoneRec zrec) {
306: lastZoneRec = zrec;
307: }
308:
309: /**
310: * @return the last zone record for this time zone.
311: */
312: ZoneRec getLastZoneRec() {
313: return lastZoneRec;
314: }
315:
316: /**
317: * Sets the last rule records for this time zone. Those are used
318: * for generating SimpleTimeZone parameters.
319: * @param rules the last rule records
320: */
321: void setLastRules(ArrayList rules) {
322: int n = rules.size();
323: if (n > 0) {
324: lastRules = rules;
325: RuleRec rec = (RuleRec) rules.get(0);
326: int offset = rec.getSave();
327: if (offset > 0) {
328: setLastDSTSaving(offset);
329: } else {
330: System.err
331: .println("\t No DST starting rule in the last rules.");
332: }
333: }
334: }
335:
336: /**
337: * @return the last rule records for this time zone.
338: */
339: ArrayList getLastRules() {
340: return lastRules;
341: }
342:
343: /**
344: * Sets the last daylight saving amount.
345: * @param the daylight saving amount
346: */
347: void setLastDSTSaving(int offset) {
348: lastSaving = offset;
349: }
350:
351: /**
352: * @return the last daylight saving amount.
353: */
354: int getLastDSTSaving() {
355: return lastSaving;
356: }
357:
358: /**
359: * Calculates the CRC32 value from the transition table and sets
360: * the value to <code>crc32</code>.
361: */
362: void checksum() {
363: if (transitions == null) {
364: crc32 = 0;
365: return;
366: }
367: Checksum sum = new Checksum();
368: for (int i = 0; i < transitions.size(); i++) {
369: int offset = ((Integer) offsets.get(i)).intValue();
370: // adjust back to make the transition in local time
371: sum
372: .update(((Long) transitions.get(i)).longValue()
373: + offset);
374: sum.update(offset);
375: sum.update(((Integer) dstOffsets.get(i)).intValue());
376: }
377: crc32 = (int) sum.getValue();
378: }
379:
380: /**
381: * Removes unnecessary transitions for Java time zone support.
382: */
383: void optimize() {
384: // if there is only one offset, delete all transitions. This
385: // could happen if only time zone abbreviations changed.
386: if (gmtOffsets.size() == 1) {
387: transitions = null;
388: usedRuleRecs = null;
389: setDSTType(NO_DST);
390: return;
391: }
392: for (int i = 0; i < (transitions.size() - 2); i++) { // don't remove the last one
393: if (((Long) transitions.get(i)).longValue() == ((Long) transitions
394: .get(i + 1)).longValue()) {
395: transitions.remove(i);
396: offsets.remove(i);
397: dstOffsets.remove(i);
398: i--;
399: }
400: }
401:
402: for (int i = 0; i < (transitions.size() - 2); i++) { // don't remove the last one
403: if (((Integer) offsets.get(i)).intValue() == ((Integer) offsets
404: .get(i + 1)).intValue()
405: && ((Integer) dstOffsets.get(i)).intValue() == ((Integer) dstOffsets
406: .get(i + 1)).intValue()) {
407: transitions.remove(i + 1);
408: offsets.remove(i + 1);
409: dstOffsets.remove(i + 1);
410: i--;
411: }
412: }
413: }
414:
415: /**
416: * Stores the specified offset value from GMT in the GMT offsets
417: * table and returns its index. The offset value includes the base
418: * GMT offset and any additional daylight saving if applicable. If
419: * the same value as the specified offset is already in the table,
420: * its index is returned.
421: * @param offset the offset value in milliseconds
422: * @return the index to the offset value in the GMT offsets table.
423: */
424: int getOffsetIndex(int offset) {
425: return getOffsetIndex(offset, 0);
426: }
427:
428: /**
429: * Stores the specified daylight saving value in the GMT offsets
430: * table and returns its index. If the same value as the specified
431: * offset is already in the table, its index is returned. If 0 is
432: * specified, it's not stored in the table and -1 is returned.
433: * @param offset the offset value in milliseconds
434: * @return the index to the specified offset value in the GMT
435: * offsets table, or -1 if 0 is specified.
436: */
437: int getDstOffsetIndex(int offset) {
438: if (offset == 0) {
439: return -1;
440: }
441: return getOffsetIndex(offset, 1);
442: }
443:
444: private int getOffsetIndex(int offset, int index) {
445: if (gmtOffsets == null) {
446: gmtOffsets = new ArrayList();
447: }
448: for (int i = index; i < gmtOffsets.size(); i++) {
449: if (offset == ((Integer) gmtOffsets.get(i)).intValue()) {
450: return i;
451: }
452: }
453: if (gmtOffsets.size() < index) {
454: gmtOffsets.add(new Integer(0));
455: }
456: gmtOffsets.add(new Integer(offset));
457: return gmtOffsets.size() - 1;
458: }
459: }
|