KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ibm > icu > impl > OlsonTimeZone


1  /*
2   *******************************************************************************
3   * Copyright (C) 2005-2006, International Business Machines Corporation and *
4   * others. All Rights Reserved. *
5   *******************************************************************************
6   */

7 package com.ibm.icu.impl;
8
9 import java.util.Date JavaDoc;
10
11 import com.ibm.icu.util.Calendar;
12 import com.ibm.icu.util.GregorianCalendar;
13 import com.ibm.icu.util.SimpleTimeZone;
14 import com.ibm.icu.util.TimeZone;
15
16 /**
17  * A time zone based on the Olson database. Olson time zones change
18  * behavior over time. The raw offset, rules, presence or absence of
19  * daylight savings time, and even the daylight savings amount can all
20  * vary.
21  *
22  * This class uses a resource bundle named "zoneinfo". Zoneinfo is a
23  * table containing different kinds of resources. In several places,
24  * zones are referred to using integers. A zone's integer is a number
25  * from 0..n-1, where n is the number of zones, with the zones sorted
26  * in lexicographic order.
27  *
28  * 1. Zones. These have keys corresponding to the Olson IDs, e.g.,
29  * "Asia/Shanghai". Each resource describes the behavior of the given
30  * zone. Zones come in several formats, which are differentiated
31  * based on length.
32  *
33  * a. Alias (int, length 1). An alias zone is an int resource. The
34  * integer is the zone number of the target zone. The key of this
35  * resource is an alternate name for the target zone. Aliases
36  * represent Olson links and ICU compatibility IDs.
37  *
38  * b. Simple zone (array, length 3). The three subelements are:
39  *
40  * i. An intvector of transitions. These are given in epoch
41  * seconds. This may be an empty invector (length 0). If the
42  * transtions list is empty, then the zone's behavior is fixed and
43  * given by the offset list, which will contain exactly one pair.
44  * Otherwise each transtion indicates a time after which (inclusive)
45  * the associated offset pair is in effect.
46  *
47  * ii. An intvector of offsets. These are in pairs of raw offset /
48  * DST offset, in units of seconds. There will be at least one pair
49  * (length >= 2 && length % 2 == 0).
50  *
51  * iii. A binary resource. This is of the same length as the
52  * transitions vector, so length may be zero. Each unsigned byte
53  * corresponds to one transition, and has a value of 0..n-1, where n
54  * is the number of pairs in the offset vector. This forms a map
55  * between transitions and offset pairs.
56  *
57  * c. Simple zone with aliases (array, length 4). This is like a
58  * simple zone, but also contains a fourth element:
59  *
60  * iv. An intvector of aliases. This list includes this zone
61  * itself, and lists all aliases of this zone.
62  *
63  * d. Complex zone (array, length 5). This is like a simple zone,
64  * but contains two more elements:
65  *
66  * iv. A string, giving the name of a rule. This is the "final
67  * rule", which governs the zone's behavior beginning in the "final
68  * year". The rule ID is given without leading underscore, e.g.,
69  * "EU".
70  *
71  * v. An intvector of length 2, containing the raw offset for the
72  * final rule (in seconds), and the final year. The final rule
73  * takes effect for years >= the final year.
74  *
75  * e. Complex zone with aliases (array, length 6). This is like a
76  * complex zone, but also contains a sixth element:
77  *
78  * vi. An intvector of aliases. This list includes this zone
79  * itself, and lists all aliases of this zone.
80  *
81  * 2. Rules. These have keys corresponding to the Olson rule IDs,
82  * with an underscore prepended, e.g., "_EU". Each resource describes
83  * the behavior of the given rule using an intvector, containing the
84  * onset list, the cessation list, and the DST savings. The onset and
85  * cessation lists consist of the month, dowim, dow, time, and time
86  * mode. The end result is that the 11 integers describing the rule
87  * can be passed directly into the SimpleTimeZone 13-argument
88  * constructor (the other two arguments will be the raw offset, taken
89  * from the complex zone element 5, and the ID string, which is not
90  * used), with the times and the DST savings multiplied by 1000 to
91  * scale from seconds to milliseconds.
92  *
93  * 3. Countries. These have keys corresponding to the 2-letter ISO
94  * country codes, with a percent sign prepended, e.g., "%US". Each
95  * resource is an intvector listing the zones associated with the
96  * given country. The special entry "%" corresponds to "no country",
97  * that is, the category of zones assigned to no country in the Olson
98  * DB.
99  *
100  * 4. Metadata. Metadata is stored under the key "_". It is an
101  * intvector of length three containing the number of zones resources,
102  * rule resources, and country resources. For the purposes of this
103  * count, the metadata entry itself is considered a rule resource,
104  * since its key begins with an underscore.
105  */

106 public class OlsonTimeZone extends TimeZone {
107
108     // Generated by serialver from JDK 1.4.1_01
109
static final long serialVersionUID = -6281977362477515376L;
110
111     private static final boolean ASSERT = false;
112
113     /* (non-Javadoc)
114      * @see com.ibm.icu.util.TimeZone#getOffset(int, int, int, int, int, int)
115      */

116     public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) {
117         if (month < Calendar.JANUARY || month > Calendar.DECEMBER) {
118             throw new IllegalArgumentException JavaDoc("Month is not in the legal range: " +month);
119         } else {
120             return getOffset(era, year, month, day, dayOfWeek, milliseconds,MONTH_LENGTH[month + (isLeapYear(year)?12:0)]);
121         }
122     }
123
124     /**
125      * TimeZone API.
126      */

127     public int getOffset(int era, int year, int month,int dom, int dow, int millis, int monthLength){
128
129         if ((era != GregorianCalendar.AD && era != GregorianCalendar.BC)
130             || month < Calendar.JANUARY
131             || month > Calendar.DECEMBER
132             || dom < 1
133             || dom > monthLength
134             || dow < Calendar.SUNDAY
135             || dow > Calendar.SATURDAY
136             || millis < 0
137             || millis >= MILLIS_PER_DAY
138             || monthLength < 28
139             || monthLength > 31) {
140             throw new IllegalArgumentException JavaDoc();
141         }
142
143         if (era == GregorianCalendar.BC) {
144             year = -year;
145         }
146
147         if (year > finalYear) { // [sic] >, not >=; see above
148
if (ASSERT) Assert.assrt("(finalZone != null)", finalZone != null);
149             return finalZone.getOffset(era, year, month, dom, dow,
150                                         millis, monthLength);
151         }
152
153         // Compute local epoch seconds from input fields
154
double time = fieldsToDay(year, month, dom) * SECONDS_PER_DAY +
155             Math.floor(millis / (double) MILLIS_PER_SECOND);
156
157         int[] offsets = new int[2];
158         getHistoricalOffset(time, true, offsets);
159         return offsets[0] + offsets[1];
160     }
161     /* (non-Javadoc)
162      * @see com.ibm.icu.util.TimeZone#setRawOffset(int)
163      */

164     public void setRawOffset(int offsetMillis) {
165         finalZone.setRawOffset(offsetMillis);
166     }
167     public Object JavaDoc clone() {
168         OlsonTimeZone other = (OlsonTimeZone) super.clone();
169         if(finalZone!=null){
170             finalZone.setID(getID());
171             other.finalZone = (SimpleTimeZone)finalZone.clone();
172         }
173         other.transitionTimes = (int[])transitionTimes.clone();
174         other.typeData = (byte[])typeData.clone();
175         other.typeOffsets = (int[])typeOffsets.clone();
176         return other;
177     }
178     /**
179      * TimeZone API.
180      */

181     public void getOffset(long date, boolean local, int[] offsets) {
182         int rawoff, dstoff;
183         // The check against finalMillis will suffice most of the time, except
184
// for the case in which finalMillis == DBL_MAX, date == DBL_MAX,
185
// and finalZone == 0. For this case we add "&& finalZone != 0".
186
if (date >= finalMillis && finalZone != null) {
187             double[] doub = floorDivide(date, (double)MILLIS_PER_DAY);
188             double millis=doub[1];
189             double days=doub[0];
190             int[] temp = dayToFields(days);
191             int year=temp[0], month=temp[1], dom=temp[2], dow=temp[3];
192             rawoff = finalZone.getRawOffset();
193
194             if (!local) {
195                 // Adjust from GMT to local
196
date += rawoff;
197                 doub = floorDivide(date, (double)MILLIS_PER_DAY);
198                 double days2 = doub[0];
199                 millis = doub[1];
200                 if (days2 != days) {
201                     temp = dayToFields(days2);
202                     year=temp[0];
203                     month=temp[1];
204                     dom=temp[2];
205                     dow=temp[3];
206                 }
207             }
208
209             dstoff = finalZone.getOffset(GregorianCalendar.AD, year, month, dom,
210                                          dow, (int)millis)
211                     - rawoff;
212             offsets[0]=rawoff;
213             offsets[1]=dstoff;
214             return;
215         }
216
217         double secs = Math.floor(date / MILLIS_PER_SECOND);
218         getHistoricalOffset(secs, local, offsets);
219         return;
220     }
221     double[] floorDivide(double dividend, double divisor) {
222         double remainder;
223         double[] ret = new double[2];
224         // Only designed to work for positive divisors
225
if (ASSERT) Assert.assrt("divisor > 0", divisor > 0);
226         double quotient = Math.floor(dividend/divisor);
227         remainder = dividend - (quotient * divisor);
228         // N.B. For certain large dividends, on certain platforms, there
229
// is a bug such that the quotient is off by one. If you doubt
230
// this to be true, set a breakpoint below and run cintltst.
231
if (remainder < 0 || remainder >= divisor) {
232             // E.g. 6.7317038241449352e+022 / 86400000.0 is wrong on my
233
// machine (too high by one). 4.1792057231752762e+024 /
234
// 86400000.0 is wrong the other way (too low).
235
double q = quotient;
236             quotient += (remainder < 0) ? -1 : +1;
237             if (q == quotient) {
238                 // For quotients > ~2^53, we won't be able to add or
239
// subtract one, since the LSB of the mantissa will be >
240
// 2^0; that is, the exponent (base 2) will be larger than
241
// the length, in bits, of the mantissa. In that case, we
242
// can't give a correct answer, so we set the remainder to
243
// zero. This has the desired effect of making extreme
244
// values give back an approximate answer rather than
245
// crashing. For example, UDate values above a ~10^25
246
// might all have a time of midnight.
247
remainder = 0;
248             } else {
249                 remainder = dividend - (quotient * divisor);
250             }
251         }
252         if (ASSERT) Assert.assrt("0 <= remainder && remainder < divisor", 0 <= remainder && remainder < divisor);
253         ret[0]=quotient;
254         ret[1]=remainder;
255         return ret;
256     }
257     /* (non-Javadoc)
258      * @see com.ibm.icu.util.TimeZone#getRawOffset()
259      */

260     public int getRawOffset() {
261         int[] ret = new int[2];
262         getOffset( System.currentTimeMillis(), false, ret);
263         return ret[0];
264     }
265
266     /* (non-Javadoc)
267      * @see com.ibm.icu.util.TimeZone#useDaylightTime()
268      */

269     public boolean useDaylightTime() {
270 // If DST was observed in 1942 (for example) but has never been
271
// observed from 1943 to the present, most clients will expect
272
// this method to return FALSE. This method determines whether
273
// DST is in use in the current year (at any point in the year)
274
// and returns TRUE if so.
275

276         double[] dt = floorDivide(System.currentTimeMillis(), (double)MILLIS_PER_DAY); // epoch days
277
int days = (int)dt[0];
278         int[] it = dayToFields(days);
279
280         int year=it[0]; /*, month=it[1], dom=it[2], dow=it[3]*/
281         if (year > finalYear) { // [sic] >, not >=; see above
282
if (ASSERT) Assert.assrt("finalZone != null && finalZone.useDaylightTime()", finalZone != null && finalZone.useDaylightTime());
283             return true;
284         }
285
286         // Find start of this year, and start of next year
287
int start = (int) fieldsToDay(year, 0, 1) * SECONDS_PER_DAY;
288         int limit = (int) fieldsToDay(year+1, 0, 1) * SECONDS_PER_DAY;
289
290         // Return TRUE if DST is observed at any time during the current
291
// year.
292
for (int i=0; i<transitionCount; ++i) {
293             if (transitionTimes[i] >= limit) {
294                 break;
295             }
296             if (transitionTimes[i] >= start &&
297                 dstOffset(typeData[i]) != 0) {
298                 return true;
299             }
300         }
301         return false;
302     }
303     /**
304      * TimeZone API
305      * Returns the amount of time to be added to local standard time
306      * to get local wall clock time.
307      */

308     public int getDSTSavings() {
309         if(finalZone!=null){
310             return finalZone.getDSTSavings();
311         }
312         return super.getDSTSavings();
313         
314     }
315     /* (non-Javadoc)
316      * @see com.ibm.icu.util.TimeZone#inDaylightTime(java.util.Date)
317      */

318     public boolean inDaylightTime(Date JavaDoc date) {
319         int[] temp = new int[2];
320         getOffset(date.getTime(), false, temp);
321         return temp[1] != 0;
322     }
323     
324     /**
325      * Construct a GMT+0 zone with no transitions. This is done when a
326      * constructor fails so the resultant object is well-behaved.
327      */

328     private void constructEmpty(){
329         transitionCount = 0;
330         typeCount = 1;
331         transitionTimes = typeOffsets = new int[]{0,0};
332         typeData = new byte[2];
333         
334     }
335     /**
336      * Construct from a resource bundle
337      * @param top the top-level zoneinfo resource bundle. This is used
338      * to lookup the rule that `res' may refer to, if there is one.
339      * @param res the resource bundle of the zone to be constructed
340      */

341     public OlsonTimeZone(ICUResourceBundle top, ICUResourceBundle res){
342         construct(top, res);
343     }
344     private void construct(ICUResourceBundle top, ICUResourceBundle res){
345         
346         if ((top == null || res == null)) {
347             throw new IllegalArgumentException JavaDoc();
348         }
349         if(DEBUG) System.out.println("OlsonTimeZone(" + res.getKey() +")");
350
351
352         // TODO -- clean up -- Doesn't work if res points to an alias
353
// // TODO remove nonconst casts below when ures_* API is fixed
354
// setID(ures_getKey((UResourceBundle*) res)); // cast away const
355

356         // Size 1 is an alias TO another zone (int)
357
// HOWEVER, the caller should dereference this and never pass it in to us
358
// Size 3 is a purely historical zone (no final rules)
359
// Size 4 is like size 3, but with an alias list at the end
360
// Size 5 is a hybrid zone, with historical and final elements
361
// Size 6 is like size 5, but with an alias list at the end
362
int size = res.getSize();
363         if (size < 3 || size > 6) {
364            // ec = U_INVALID_FORMAT_ERROR;
365
throw new IllegalArgumentException JavaDoc("Invalid Format");
366         }
367
368         // Transitions list may be empty
369
ICUResourceBundle r = res.get(0);
370         transitionTimes = r.getIntVector();
371         
372         if ((transitionTimes.length<0 || transitionTimes.length>0x7FFF) ) {
373             throw new IllegalArgumentException JavaDoc("Invalid Format");
374         }
375         transitionCount = (int) transitionTimes.length;
376         
377         // Type offsets list must be of even size, with size >= 2
378
r = res.get( 1);
379         typeOffsets = r.getIntVector();
380         if ((typeOffsets.length<2 || typeOffsets.length>0x7FFE || ((typeOffsets.length&1)!=0))) {
381             throw new IllegalArgumentException JavaDoc("Invalid Format");
382         }
383         typeCount = (int) typeOffsets.length >> 1;
384
385         // Type data must be of the same size as the transitions list
386
r = res.get(2);
387         typeData = r.getBinary().array();
388         if (typeData.length != transitionCount) {
389             throw new IllegalArgumentException JavaDoc("Invalid Format");
390         }
391
392         // Process final rule and data, if any
393
if (size >= 5) {
394             String JavaDoc ruleid = res.getString(3);
395             r = res.get(4);
396             int[] data = r.getIntVector();
397
398             if (data != null && data.length == 2) {
399                 int rawOffset = data[0] * MILLIS_PER_SECOND;
400                 // Subtract one from the actual final year; we
401
// actually store final year - 1, and compare
402
// using > rather than >=. This allows us to use
403
// INT32_MAX as an exclusive upper limit for all
404
// years, including INT32_MAX.
405
if (ASSERT) Assert.assrt("data[1] > Integer.MIN_VALUE", data[1] > Integer.MIN_VALUE);
406                 finalYear = data[1] - 1;
407                 // Also compute the millis for Jan 1, 0:00 GMT of the
408
// finalYear. This reduces runtime computations.
409
finalMillis = fieldsToDay(data[1], 0, 1) * TimeZone.MILLIS_PER_DAY;
410                 //U_DEBUG_TZ_MSG(("zone%s|%s: {%d,%d}, finalYear%d, finalMillis%.1lf\n",
411
// zKey,rKey, data[0], data[1], finalYear, finalMillis));
412
r = loadRule(top, ruleid);
413
414                 // 3, 1, -1, 7200, 0, 9, -31, -1, 7200, 0, 3600
415
data = r.getIntVector();
416                 if ( data.length == 11) {
417                     //U_DEBUG_TZ_MSG(("zone%s, rule%s: {%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d}", zKey, ures_getKey(r),
418
// data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10]));
419
finalZone = new SimpleTimeZone(rawOffset, "",
420                         data[0], data[1], data[2],
421                         data[3] * MILLIS_PER_SECOND,
422                         data[4],
423                         data[5], data[6], data[7],
424                         data[8] * MILLIS_PER_SECOND,
425                         data[9],
426                         data[10] * MILLIS_PER_SECOND);
427                 } else {
428                     throw new IllegalArgumentException JavaDoc("Invalid Format");
429                 }
430             } else {
431                 throw new IllegalArgumentException JavaDoc("Invalid Format");
432             }
433         }
434     }
435     public OlsonTimeZone(){
436        /*
437         *
438         finalYear = Integer.MAX_VALUE;
439         finalMillis = Double.MAX_VALUE;
440         finalZone = null;
441         */

442         constructEmpty();
443     }
444
445
446     public OlsonTimeZone(String JavaDoc id){
447         ICUResourceBundle top = (ICUResourceBundle)ICUResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "zoneinfo", ICUResourceBundle.ICU_DATA_CLASS_LOADER);
448         ICUResourceBundle res = ZoneMeta.openOlsonResource(id);
449         construct(top, res);
450         if(finalZone!=null){
451             finalZone.setID(id);
452         }
453         super.setID(id);
454     }
455     public void setID(String JavaDoc id){
456         if(finalZone!= null){
457             finalZone.setID(id);
458         }
459         super.setID(id);
460     }
461     private static final int UNSIGNED_BYTE_MASK =0xFF;
462     private int getInt(byte val){
463         return (int)(UNSIGNED_BYTE_MASK & val);
464     }
465     private void getHistoricalOffset(double time, boolean local, int[] offsets) {
466         if (transitionCount != 0) {
467             // Linear search from the end is the fastest approach, since
468
// most lookups will happen at/near the end.
469
int i = 0;
470             for (i = transitionCount - 1; i > 0; --i) {
471                 int transition = transitionTimes[i];
472                 if (local) {
473                     int zoneOffsetPrev = zoneOffset(getInt(typeData[i-1]));
474                     int zoneOffsetCurr = zoneOffset(getInt(typeData[i]));
475                     if(zoneOffsetPrev < zoneOffsetCurr) {
476                         transition += zoneOffsetPrev;
477                     } else {
478                         transition += zoneOffsetCurr;
479                     }
480                 }
481                 if (time >= transition) {
482                     break;
483                 }
484             }
485
486             if (ASSERT) Assert.assrt("i>=0 && i<transitionCount", i>=0 && i<transitionCount);
487
488             // Check invariants for GMT times; if these pass for GMT times
489
// the local logic should be working too.
490
if (ASSERT) {
491                 Assert.assrt("local || time < transitionTimes[0] || time >= transitionTimes[i]",
492                         local || time < transitionTimes[0] || time >= transitionTimes[i]);
493                 Assert.assrt("local || i == transitionCount-1 || time < transitionTimes[i+1]",
494                         local || i == transitionCount-1 || time < transitionTimes[i+1]);
495             }
496             if (i == 0) {
497                 // Check if the given time is before the very first transition
498
int firstTransition = transitionTimes[0];
499                 int initialRawOffset = rawOffset(getInt(typeData[0]));
500                 if (local) {
501                     firstTransition += initialRawOffset;
502                 }
503                 if (time >= firstTransition) {
504                     // The given time is between the first and the second transition
505
offsets[0] = initialRawOffset * MILLIS_PER_SECOND;
506                     offsets[1] = dstOffset(getInt(typeData[0])) * MILLIS_PER_SECOND;
507                 } else {
508                     // The given time is before the first transition
509
offsets[0] = initialRawOffset * MILLIS_PER_SECOND;
510                     offsets[1] = 0;
511                 }
512             } else {
513                 int index = getInt(typeData[i]);
514                 offsets[0] = rawOffset(index) * MILLIS_PER_SECOND;
515                 offsets[1] = dstOffset(index) * MILLIS_PER_SECOND;
516             }
517         } else {
518             // No transitions, single pair of offsets only
519
offsets[0] = rawOffset(0) * MILLIS_PER_SECOND;
520             offsets[1] = dstOffset(0) * MILLIS_PER_SECOND;
521         }
522     }
523     private int zoneOffset(int index){
524         index=index << 1;
525         return typeOffsets[index] + typeOffsets[index+1];
526     }
527     private int rawOffset(int index){
528         return typeOffsets[(int)(index << 1)];
529     }
530     private int dstOffset(int index){
531         return typeOffsets[(int)((index << 1) + 1)];
532     }
533     
534     // temp
535
public String JavaDoc toString() {
536         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
537         buf.append(super.toString());
538         buf.append('[');
539         buf.append("transitionCount=" + transitionCount);
540         buf.append(",typeCount=" + typeCount);
541         buf.append(",transitionTimes=");
542         if (transitionTimes != null) {
543             buf.append('[');
544             for (int i = 0; i < transitionTimes.length; ++i) {
545                 if (i > 0) {
546                     buf.append(',');
547                 }
548                 buf.append(Integer.toString(transitionTimes[i]));
549             }
550             buf.append(']');
551         } else {
552             buf.append("null");
553         }
554         buf.append(",typeOffsets=");
555         if (typeOffsets != null) {
556             buf.append('[');
557             for (int i = 0; i < typeOffsets.length; ++i) {
558                 if (i > 0) {
559                     buf.append(',');
560                 }
561                 buf.append(Integer.toString(typeOffsets[i]));
562             }
563             buf.append(']');
564         } else {
565             buf.append("null");
566         }
567         buf.append(",finalYear=" + finalYear);
568         buf.append(",finalMillis=" + finalMillis);
569         buf.append(",finalZone=" + finalZone);
570         buf.append(']');
571         
572         return buf.toString();
573     }
574     /**
575      * Number of transitions, 0..~370
576      */

577     private int transitionCount;
578
579     /**
580      * Number of types, 1..255
581      */

582     private int typeCount;
583
584     /**
585      * Time of each transition in seconds from 1970 epoch.
586      * Length is transitionCount int32_t's.
587      */

588     private int[] transitionTimes; // alias into res; do not delete
589

590     /**
591      * Offset from GMT in seconds for each type.
592      * Length is typeCount int32_t's.
593      */

594     private int[] typeOffsets; // alias into res; do not delete
595

596     /**
597      * Type description data, consisting of transitionCount uint8_t
598      * type indices (from 0..typeCount-1).
599      * Length is transitionCount int8_t's.
600      */

601     private byte[] typeData; // alias into res; do not delete
602

603     /**
604      * The last year for which the transitions data are to be used
605      * rather than the finalZone. If there is no finalZone, then this
606      * is set to INT32_MAX. NOTE: This corresponds to the year _before_
607      * the one indicated by finalMillis.
608      */

609     private int finalYear = Integer.MAX_VALUE;
610
611     /**
612      * The millis for the start of the first year for which finalZone
613      * is to be used, or DBL_MAX if finalZone is 0. NOTE: This is
614      * 0:00 GMT Jan 1, <finalYear + 1> (not <finalMillis>).
615      */

616     private double finalMillis = Double.MAX_VALUE;
617
618     /**
619      * A SimpleTimeZone that governs the behavior for years > finalYear.
620      * If and only if finalYear == INT32_MAX then finalZone == 0.
621      */

622     private SimpleTimeZone finalZone = null; // owned, may be NULL
623

624     private static final boolean DEBUG = ICUDebug.enabled("olson");
625     private static final int[] DAYS_BEFORE = new int[] {0,31,59,90,120,151,181,212,243,273,304,334,
626                                            0,31,60,91,121,152,182,213,244,274,305,335};
627
628     private static final int[] MONTH_LENGTH = new int[]{31,28,31,30,31,30,31,31,30,31,30,31,
629                                         31,29,31,30,31,30,31,31,30,31,30,31};
630     private static final int JULIAN_1_CE = 1721426; // January 1, 1 CE Gregorian
631
private static final int JULIAN_1970_CE = 2440588; // January 1, 1970 CE Gregorian
632
private static final int MILLIS_PER_SECOND = 1000;
633     private static final int SECONDS_PER_DAY = 24*60*60;
634     
635     private static final double fieldsToDay(int year, int month, int dom) {
636         int y = year - 1;
637         double julian = 365 * y + myFloorDivide(y, 4) + (JULIAN_1_CE - 3) + // Julian cal
638
myFloorDivide(y, 400) - myFloorDivide(y, 100) + 2 + // => Gregorian cal
639
DAYS_BEFORE[month + (isLeapYear(year) ? 12 : 0)] + dom; // => month/dom
640

641         return julian - JULIAN_1970_CE; // JD => epoch day
642
}
643     private static final boolean isLeapYear(int year) {
644         // year&0x3 == year%4
645
return ((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0));
646     }
647
648     private static ICUResourceBundle loadRule(ICUResourceBundle top, String JavaDoc ruleid) {
649         ICUResourceBundle r = top.get("Rules");
650         r = r.get(ruleid);
651         return r;
652     }
653     /**
654      * Divide two long integers, returning the floor of the quotient.
655      * <p>
656      * Unlike the built-in division, this is mathematically well-behaved.
657      * E.g., <code>-1/4</code> => 0
658      * but <code>floorDivide(-1,4)</code> => -1.
659      * @param numerator the numerator
660      * @param denominator a divisor which must be > 0
661      * @return the floor of the quotient.
662      * @stable ICU 2.0
663      */

664     private static final long myFloorDivide(long numerator, long denominator) {
665         // We do this computation in order to handle
666
// a numerator of Long.MIN_VALUE correctly
667
return (numerator >= 0) ?
668             numerator / denominator :
669             ((numerator + 1) / denominator) - 1;
670     }
671     int[] dayToFields(double day) {
672          int year, month, dom, dow;
673          double doy;
674          int[] ret = new int[5];
675          
676         // Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
677
day += JULIAN_1970_CE - JULIAN_1_CE;
678         
679         // Convert from the day number to the multiple radix
680
// representation. We use 400-year, 100-year, and 4-year cycles.
681
// For example, the 4-year cycle has 4 years + 1 leap day; giving
682
// 1461 == 365*4 + 1 days.
683
double[]temp = floorDivide(day, 146097); // 400-year cycle length
684
double n400 = temp[0];
685         doy = temp[1];
686         temp = floorDivide(doy, 36524); // 100-year cycle length
687
double n100 = temp[0];
688         doy = temp[1];
689         temp = floorDivide(doy, 1461); // 4-year cycle length
690
double n4 = temp[0];
691         doy = temp[1];
692         temp = floorDivide(doy, 365);
693         double n1 = temp[0];
694         doy = temp[1];
695         year = (int)( 400*n400 + 100*n100 + 4*n4 + n1);
696         if (n100 == 4 || n1 == 4) {
697             doy = 365; // Dec 31 at end of 4- or 400-year cycle
698
} else {
699             ++year;
700         }
701     
702         boolean isLeap = isLeapYear(year);
703         
704         // Gregorian day zero is a Monday.
705
dow = (int) ((day + 1) % 7);
706         dow += (dow < 0) ? (Calendar.SUNDAY + 7) : Calendar.SUNDAY;
707         
708         // Common Julian/Gregorian calculation
709
int correction = 0;
710         int march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
711
if (doy >= march1) {
712             correction = isLeap ? 1 : 2;
713         }
714         month = (int)((12 * (doy + correction) + 6) / 367); // zero-based month
715
dom = (int)(doy - DAYS_BEFORE[month + (isLeap ? 12 : 0)] + 1); // one-based DOM
716
doy++; // one-based doy
717
ret[0]=year;
718         ret[1]=month;
719         ret[2]=dom;
720         ret[3]=dow;
721         ret[4]=(int)doy;
722         return ret;
723     }
724     public boolean equals(Object JavaDoc obj){
725         if (!super.equals(obj)) return false; // super does class check
726

727         OlsonTimeZone z = (OlsonTimeZone) obj;
728
729         return (Utility.arrayEquals(typeData, z.typeData) ||
730                  // If the pointers are not equal, the zones may still
731
// be equal if their rules and transitions are equal
732
(finalYear == z.finalYear &&
733                   // Don't compare finalMillis; if finalYear is ==, so is finalMillis
734
((finalZone == null && z.finalZone == null) ||
735                    (finalZone != null && z.finalZone != null &&
736                     finalZone.equals(z.finalZone)) &&
737                   transitionCount == z.transitionCount &&
738                   typeCount == z.typeCount &&
739                   Utility.arrayEquals(transitionTimes, z.transitionTimes) &&
740                   Utility.arrayEquals(typeOffsets, z.typeOffsets) &&
741                   Utility.arrayEquals(typeData, z.typeData)
742                   )));
743
744     }
745     public int hashCode(){
746         int ret = (int) (finalYear ^ (finalYear>>>4) +
747                    transitionCount ^ (transitionCount>>>6) +
748                    typeCount ^ (typeCount>>>8) +
749                    Double.doubleToLongBits(finalMillis)+
750                    (finalZone == null ? 0 : finalZone.hashCode()) +
751                    super.hashCode());
752         for(int i=0; i<transitionTimes.length; i++){
753             ret+=transitionTimes[i]^(transitionTimes[i]>>>8);
754         }
755         for(int i=0; i<typeOffsets.length; i++){
756             ret+=typeOffsets[i]^(typeOffsets[i]>>>8);
757         }
758         for(int i=0; i<typeData.length; i++){
759             ret+=typeData[i] & UNSIGNED_BYTE_MASK;
760         }
761         return ret;
762     }
763     /*
764     private void readObject(ObjectInputStream s) throws IOException {
765         s.defaultReadObject();
766         // customized deserialization code
767        
768         // followed by code to update the object, if necessary
769     }
770     */

771 }
772
773
Popular Tags