Source Code Cross Referenced for OlsonTimeZone.java in  » Internationalization-Localization » icu4j » com » ibm » icu » impl » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Internationalization Localization » icu4j » com.ibm.icu.impl 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         *******************************************************************************
003:         * Copyright (C) 2005-2006, International Business Machines Corporation and         *
004:         * others. All Rights Reserved.                                                *
005:         *******************************************************************************
006:         */
007:        package com.ibm.icu.impl;
008:
009:        import java.util.Date;
010:
011:        import com.ibm.icu.util.Calendar;
012:        import com.ibm.icu.util.GregorianCalendar;
013:        import com.ibm.icu.util.SimpleTimeZone;
014:        import com.ibm.icu.util.TimeZone;
015:
016:        /**
017:         * A time zone based on the Olson database.  Olson time zones change
018:         * behavior over time.  The raw offset, rules, presence or absence of
019:         * daylight savings time, and even the daylight savings amount can all
020:         * vary.
021:         *
022:         * This class uses a resource bundle named "zoneinfo".  Zoneinfo is a
023:         * table containing different kinds of resources.  In several places,
024:         * zones are referred to using integers.  A zone's integer is a number
025:         * from 0..n-1, where n is the number of zones, with the zones sorted
026:         * in lexicographic order.
027:         *
028:         * 1. Zones.  These have keys corresponding to the Olson IDs, e.g.,
029:         * "Asia/Shanghai".  Each resource describes the behavior of the given
030:         * zone.  Zones come in several formats, which are differentiated
031:         * based on length.
032:         *
033:         *  a. Alias (int, length 1).  An alias zone is an int resource.  The
034:         *  integer is the zone number of the target zone.  The key of this
035:         *  resource is an alternate name for the target zone.  Aliases
036:         *  represent Olson links and ICU compatibility IDs.
037:         *
038:         *  b. Simple zone (array, length 3).  The three subelements are:
039:         *
040:         *   i. An intvector of transitions.  These are given in epoch
041:         *   seconds.  This may be an empty invector (length 0).  If the
042:         *   transtions list is empty, then the zone's behavior is fixed and
043:         *   given by the offset list, which will contain exactly one pair.
044:         *   Otherwise each transtion indicates a time after which (inclusive)
045:         *   the associated offset pair is in effect.
046:         *
047:         *   ii. An intvector of offsets.  These are in pairs of raw offset /
048:         *   DST offset, in units of seconds.  There will be at least one pair
049:         *   (length >= 2 && length % 2 == 0).
050:         *
051:         *   iii. A binary resource.  This is of the same length as the
052:         *   transitions vector, so length may be zero.  Each unsigned byte
053:         *   corresponds to one transition, and has a value of 0..n-1, where n
054:         *   is the number of pairs in the offset vector.  This forms a map
055:         *   between transitions and offset pairs.
056:         *
057:         *  c. Simple zone with aliases (array, length 4).  This is like a
058:         *  simple zone, but also contains a fourth element:
059:         *
060:         *   iv. An intvector of aliases.  This list includes this zone
061:         *   itself, and lists all aliases of this zone.
062:         *
063:         *  d. Complex zone (array, length 5).  This is like a simple zone,
064:         *  but contains two more elements:
065:         *
066:         *   iv. A string, giving the name of a rule.  This is the "final
067:         *   rule", which governs the zone's behavior beginning in the "final
068:         *   year".  The rule ID is given without leading underscore, e.g.,
069:         *   "EU".
070:         *
071:         *   v. An intvector of length 2, containing the raw offset for the
072:         *   final rule (in seconds), and the final year.  The final rule
073:         *   takes effect for years >= the final year.
074:         *
075:         *  e. Complex zone with aliases (array, length 6).  This is like a
076:         *  complex zone, but also contains a sixth element:
077:         * 
078:         *   vi. An intvector of aliases.  This list includes this zone
079:         *   itself, and lists all aliases of this zone.
080:         *
081:         * 2. Rules.  These have keys corresponding to the Olson rule IDs,
082:         * with an underscore prepended, e.g., "_EU".  Each resource describes
083:         * the behavior of the given rule using an intvector, containing the
084:         * onset list, the cessation list, and the DST savings.  The onset and
085:         * cessation lists consist of the month, dowim, dow, time, and time
086:         * mode.  The end result is that the 11 integers describing the rule
087:         * can be passed directly into the SimpleTimeZone 13-argument
088:         * constructor (the other two arguments will be the raw offset, taken
089:         * from the complex zone element 5, and the ID string, which is not
090:         * used), with the times and the DST savings multiplied by 1000 to
091:         * scale from seconds to milliseconds.
092:         *
093:         * 3. Countries.  These have keys corresponding to the 2-letter ISO
094:         * country codes, with a percent sign prepended, e.g., "%US".  Each
095:         * resource is an intvector listing the zones associated with the
096:         * given country.  The special entry "%" corresponds to "no country",
097:         * that is, the category of zones assigned to no country in the Olson
098:         * DB.
099:         *
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,
117:                    int dayOfWeek, int milliseconds) {
118:                if (month < Calendar.JANUARY || month > Calendar.DECEMBER) {
119:                    throw new IllegalArgumentException(
120:                            "Month is not in the legal range: " + month);
121:                } else {
122:                    return getOffset(era, year, month, day, dayOfWeek,
123:                            milliseconds, MONTH_LENGTH[month
124:                                    + (isLeapYear(year) ? 12 : 0)]);
125:                }
126:            }
127:
128:            /**
129:             * TimeZone API.
130:             */
131:            public int getOffset(int era, int year, int month, int dom,
132:                    int dow, int millis, int monthLength) {
133:
134:                if ((era != GregorianCalendar.AD && era != GregorianCalendar.BC)
135:                        || month < Calendar.JANUARY
136:                        || month > Calendar.DECEMBER
137:                        || dom < 1
138:                        || dom > monthLength
139:                        || dow < Calendar.SUNDAY
140:                        || dow > Calendar.SATURDAY
141:                        || millis < 0
142:                        || millis >= MILLIS_PER_DAY
143:                        || monthLength < 28
144:                        || monthLength > 31) {
145:                    throw new IllegalArgumentException();
146:                }
147:
148:                if (era == GregorianCalendar.BC) {
149:                    year = -year;
150:                }
151:
152:                if (year > finalYear) { // [sic] >, not >=; see above
153:                    if (ASSERT)
154:                        Assert.assrt("(finalZone != null)", finalZone != null);
155:                    return finalZone.getOffset(era, year, month, dom, dow,
156:                            millis, monthLength);
157:                }
158:
159:                // Compute local epoch seconds from input fields
160:                double time = fieldsToDay(year, month, dom) * SECONDS_PER_DAY
161:                        + Math.floor(millis / (double) MILLIS_PER_SECOND);
162:
163:                int[] offsets = new int[2];
164:                getHistoricalOffset(time, true, offsets);
165:                return offsets[0] + offsets[1];
166:            }
167:
168:            /* (non-Javadoc)
169:             * @see com.ibm.icu.util.TimeZone#setRawOffset(int)
170:             */
171:            public void setRawOffset(int offsetMillis) {
172:                finalZone.setRawOffset(offsetMillis);
173:            }
174:
175:            public Object clone() {
176:                OlsonTimeZone other = (OlsonTimeZone) super .clone();
177:                if (finalZone != null) {
178:                    finalZone.setID(getID());
179:                    other.finalZone = (SimpleTimeZone) finalZone.clone();
180:                }
181:                other.transitionTimes = (int[]) transitionTimes.clone();
182:                other.typeData = (byte[]) typeData.clone();
183:                other.typeOffsets = (int[]) typeOffsets.clone();
184:                return other;
185:            }
186:
187:            /**
188:             * TimeZone API.
189:             */
190:            public void getOffset(long date, boolean local, int[] offsets) {
191:                int rawoff, dstoff;
192:                // The check against finalMillis will suffice most of the time, except
193:                // for the case in which finalMillis == DBL_MAX, date == DBL_MAX,
194:                // and finalZone == 0.  For this case we add "&& finalZone != 0".
195:                if (date >= finalMillis && finalZone != null) {
196:                    double[] doub = floorDivide(date, (double) MILLIS_PER_DAY);
197:                    double millis = doub[1];
198:                    double days = doub[0];
199:                    int[] temp = dayToFields(days);
200:                    int year = temp[0], month = temp[1], dom = temp[2], dow = temp[3];
201:                    rawoff = finalZone.getRawOffset();
202:
203:                    if (!local) {
204:                        // Adjust from GMT to local
205:                        date += rawoff;
206:                        doub = floorDivide(date, (double) MILLIS_PER_DAY);
207:                        double days2 = doub[0];
208:                        millis = doub[1];
209:                        if (days2 != days) {
210:                            temp = dayToFields(days2);
211:                            year = temp[0];
212:                            month = temp[1];
213:                            dom = temp[2];
214:                            dow = temp[3];
215:                        }
216:                    }
217:
218:                    dstoff = finalZone.getOffset(GregorianCalendar.AD, year,
219:                            month, dom, dow, (int) millis)
220:                            - rawoff;
221:                    offsets[0] = rawoff;
222:                    offsets[1] = dstoff;
223:                    return;
224:                }
225:
226:                double secs = Math.floor(date / MILLIS_PER_SECOND);
227:                getHistoricalOffset(secs, local, offsets);
228:                return;
229:            }
230:
231:            double[] floorDivide(double dividend, double divisor) {
232:                double remainder;
233:                double[] ret = new double[2];
234:                // Only designed to work for positive divisors
235:                if (ASSERT)
236:                    Assert.assrt("divisor > 0", divisor > 0);
237:                double quotient = Math.floor(dividend / divisor);
238:                remainder = dividend - (quotient * divisor);
239:                // N.B. For certain large dividends, on certain platforms, there
240:                // is a bug such that the quotient is off by one. If you doubt
241:                // this to be true, set a breakpoint below and run cintltst.
242:                if (remainder < 0 || remainder >= divisor) {
243:                    // E.g. 6.7317038241449352e+022 / 86400000.0 is wrong on my
244:                    // machine (too high by one). 4.1792057231752762e+024 /
245:                    // 86400000.0 is wrong the other way (too low).
246:                    double q = quotient;
247:                    quotient += (remainder < 0) ? -1 : +1;
248:                    if (q == quotient) {
249:                        // For quotients > ~2^53, we won't be able to add or
250:                        // subtract one, since the LSB of the mantissa will be >
251:                        // 2^0; that is, the exponent (base 2) will be larger than
252:                        // the length, in bits, of the mantissa. In that case, we
253:                        // can't give a correct answer, so we set the remainder to
254:                        // zero. This has the desired effect of making extreme
255:                        // values give back an approximate answer rather than
256:                        // crashing. For example, UDate values above a ~10^25
257:                        // might all have a time of midnight.
258:                        remainder = 0;
259:                    } else {
260:                        remainder = dividend - (quotient * divisor);
261:                    }
262:                }
263:                if (ASSERT)
264:                    Assert.assrt("0 <= remainder && remainder < divisor",
265:                            0 <= remainder && remainder < divisor);
266:                ret[0] = quotient;
267:                ret[1] = remainder;
268:                return ret;
269:            }
270:
271:            /* (non-Javadoc)
272:             * @see com.ibm.icu.util.TimeZone#getRawOffset()
273:             */
274:            public int getRawOffset() {
275:                int[] ret = new int[2];
276:                getOffset(System.currentTimeMillis(), false, ret);
277:                return ret[0];
278:            }
279:
280:            /* (non-Javadoc)
281:             * @see com.ibm.icu.util.TimeZone#useDaylightTime()
282:             */
283:            public boolean useDaylightTime() {
284:                //      If DST was observed in 1942 (for example) but has never been
285:                // observed from 1943 to the present, most clients will expect
286:                // this method to return FALSE.  This method determines whether
287:                // DST is in use in the current year (at any point in the year)
288:                // and returns TRUE if so.
289:
290:                double[] dt = floorDivide(System.currentTimeMillis(),
291:                        (double) MILLIS_PER_DAY); // epoch days
292:                int days = (int) dt[0];
293:                int[] it = dayToFields(days);
294:
295:                int year = it[0]; /*, month=it[1], dom=it[2], dow=it[3]*/
296:                if (year > finalYear) { // [sic] >, not >=; see above
297:                    if (ASSERT)
298:                        Assert
299:                                .assrt(
300:                                        "finalZone != null && finalZone.useDaylightTime()",
301:                                        finalZone != null
302:                                                && finalZone.useDaylightTime());
303:                    return true;
304:                }
305:
306:                // Find start of this year, and start of next year
307:                int start = (int) fieldsToDay(year, 0, 1) * SECONDS_PER_DAY;
308:                int limit = (int) fieldsToDay(year + 1, 0, 1) * SECONDS_PER_DAY;
309:
310:                // Return TRUE if DST is observed at any time during the current
311:                // year.
312:                for (int i = 0; i < transitionCount; ++i) {
313:                    if (transitionTimes[i] >= limit) {
314:                        break;
315:                    }
316:                    if (transitionTimes[i] >= start
317:                            && dstOffset(typeData[i]) != 0) {
318:                        return true;
319:                    }
320:                }
321:                return false;
322:            }
323:
324:            /**
325:             * TimeZone API
326:             * Returns the amount of time to be added to local standard time
327:             * to get local wall clock time.
328:             */
329:            public int getDSTSavings() {
330:                if (finalZone != null) {
331:                    return finalZone.getDSTSavings();
332:                }
333:                return super .getDSTSavings();
334:
335:            }
336:
337:            /* (non-Javadoc)
338:             * @see com.ibm.icu.util.TimeZone#inDaylightTime(java.util.Date)
339:             */
340:            public boolean inDaylightTime(Date date) {
341:                int[] temp = new int[2];
342:                getOffset(date.getTime(), false, temp);
343:                return temp[1] != 0;
344:            }
345:
346:            /**
347:             * Construct a GMT+0 zone with no transitions.  This is done when a
348:             * constructor fails so the resultant object is well-behaved.
349:             */
350:            private void constructEmpty() {
351:                transitionCount = 0;
352:                typeCount = 1;
353:                transitionTimes = typeOffsets = new int[] { 0, 0 };
354:                typeData = new byte[2];
355:
356:            }
357:
358:            /**
359:             * Construct from a resource bundle
360:             * @param top the top-level zoneinfo resource bundle.  This is used
361:             * to lookup the rule that `res' may refer to, if there is one.
362:             * @param res the resource bundle of the zone to be constructed
363:             */
364:            public OlsonTimeZone(ICUResourceBundle top, ICUResourceBundle res) {
365:                construct(top, res);
366:            }
367:
368:            private void construct(ICUResourceBundle top, ICUResourceBundle res) {
369:
370:                if ((top == null || res == null)) {
371:                    throw new IllegalArgumentException();
372:                }
373:                if (DEBUG)
374:                    System.out.println("OlsonTimeZone(" + res.getKey() + ")");
375:
376:                // TODO -- clean up -- Doesn't work if res points to an alias
377:                //        // TODO remove nonconst casts below when ures_* API is fixed
378:                //        setID(ures_getKey((UResourceBundle*) res)); // cast away const
379:
380:                // Size 1 is an alias TO another zone (int)
381:                // HOWEVER, the caller should dereference this and never pass it in to us
382:                // Size 3 is a purely historical zone (no final rules)
383:                // Size 4 is like size 3, but with an alias list at the end
384:                // Size 5 is a hybrid zone, with historical and final elements
385:                // Size 6 is like size 5, but with an alias list at the end
386:                int size = res.getSize();
387:                if (size < 3 || size > 6) {
388:                    // ec = U_INVALID_FORMAT_ERROR;
389:                    throw new IllegalArgumentException("Invalid Format");
390:                }
391:
392:                // Transitions list may be empty
393:                ICUResourceBundle r = res.get(0);
394:                transitionTimes = r.getIntVector();
395:
396:                if ((transitionTimes.length < 0 || transitionTimes.length > 0x7FFF)) {
397:                    throw new IllegalArgumentException("Invalid Format");
398:                }
399:                transitionCount = (int) transitionTimes.length;
400:
401:                // Type offsets list must be of even size, with size >= 2
402:                r = res.get(1);
403:                typeOffsets = r.getIntVector();
404:                if ((typeOffsets.length < 2 || typeOffsets.length > 0x7FFE || ((typeOffsets.length & 1) != 0))) {
405:                    throw new IllegalArgumentException("Invalid Format");
406:                }
407:                typeCount = (int) typeOffsets.length >> 1;
408:
409:                // Type data must be of the same size as the transitions list        
410:                r = res.get(2);
411:                typeData = r.getBinary().array();
412:                if (typeData.length != transitionCount) {
413:                    throw new IllegalArgumentException("Invalid Format");
414:                }
415:
416:                // Process final rule and data, if any
417:                if (size >= 5) {
418:                    String ruleid = res.getString(3);
419:                    r = res.get(4);
420:                    int[] data = r.getIntVector();
421:
422:                    if (data != null && data.length == 2) {
423:                        int rawOffset = data[0] * MILLIS_PER_SECOND;
424:                        // Subtract one from the actual final year; we
425:                        // actually store final year - 1, and compare
426:                        // using > rather than >=.  This allows us to use
427:                        // INT32_MAX as an exclusive upper limit for all
428:                        // years, including INT32_MAX.
429:                        if (ASSERT)
430:                            Assert.assrt("data[1] > Integer.MIN_VALUE",
431:                                    data[1] > Integer.MIN_VALUE);
432:                        finalYear = data[1] - 1;
433:                        // Also compute the millis for Jan 1, 0:00 GMT of the
434:                        // finalYear.  This reduces runtime computations.
435:                        finalMillis = fieldsToDay(data[1], 0, 1)
436:                                * TimeZone.MILLIS_PER_DAY;
437:                        //U_DEBUG_TZ_MSG(("zone%s|%s: {%d,%d}, finalYear%d, finalMillis%.1lf\n",
438:                        //              zKey,rKey, data[0], data[1], finalYear, finalMillis));
439:                        r = loadRule(top, ruleid);
440:
441:                        // 3, 1, -1, 7200, 0, 9, -31, -1, 7200, 0, 3600
442:                        data = r.getIntVector();
443:                        if (data.length == 11) {
444:                            //U_DEBUG_TZ_MSG(("zone%s, rule%s: {%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d}", zKey, ures_getKey(r), 
445:                            //            data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10]));
446:                            finalZone = new SimpleTimeZone(rawOffset, "",
447:                                    data[0], data[1], data[2], data[3]
448:                                            * MILLIS_PER_SECOND, data[4],
449:                                    data[5], data[6], data[7], data[8]
450:                                            * MILLIS_PER_SECOND, data[9],
451:                                    data[10] * MILLIS_PER_SECOND);
452:                        } else {
453:                            throw new IllegalArgumentException("Invalid Format");
454:                        }
455:                    } else {
456:                        throw new IllegalArgumentException("Invalid Format");
457:                    }
458:                }
459:            }
460:
461:            public OlsonTimeZone() {
462:                /*
463:                 * 
464:                 finalYear = Integer.MAX_VALUE;
465:                 finalMillis = Double.MAX_VALUE;
466:                 finalZone = null;
467:                 */
468:                constructEmpty();
469:            }
470:
471:            public OlsonTimeZone(String id) {
472:                ICUResourceBundle top = (ICUResourceBundle) ICUResourceBundle
473:                        .getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,
474:                                "zoneinfo",
475:                                ICUResourceBundle.ICU_DATA_CLASS_LOADER);
476:                ICUResourceBundle res = ZoneMeta.openOlsonResource(id);
477:                construct(top, res);
478:                if (finalZone != null) {
479:                    finalZone.setID(id);
480:                }
481:                super .setID(id);
482:            }
483:
484:            public void setID(String id) {
485:                if (finalZone != null) {
486:                    finalZone.setID(id);
487:                }
488:                super .setID(id);
489:            }
490:
491:            private static final int UNSIGNED_BYTE_MASK = 0xFF;
492:
493:            private int getInt(byte val) {
494:                return (int) (UNSIGNED_BYTE_MASK & val);
495:            }
496:
497:            private void getHistoricalOffset(double time, boolean local,
498:                    int[] offsets) {
499:                if (transitionCount != 0) {
500:                    // Linear search from the end is the fastest approach, since
501:                    // most lookups will happen at/near the end.
502:                    int i = 0;
503:                    for (i = transitionCount - 1; i > 0; --i) {
504:                        int transition = transitionTimes[i];
505:                        if (local) {
506:                            int zoneOffsetPrev = zoneOffset(getInt(typeData[i - 1]));
507:                            int zoneOffsetCurr = zoneOffset(getInt(typeData[i]));
508:                            if (zoneOffsetPrev < zoneOffsetCurr) {
509:                                transition += zoneOffsetPrev;
510:                            } else {
511:                                transition += zoneOffsetCurr;
512:                            }
513:                        }
514:                        if (time >= transition) {
515:                            break;
516:                        }
517:                    }
518:
519:                    if (ASSERT)
520:                        Assert.assrt("i>=0 && i<transitionCount", i >= 0
521:                                && i < transitionCount);
522:
523:                    // Check invariants for GMT times; if these pass for GMT times
524:                    // the local logic should be working too.
525:                    if (ASSERT) {
526:                        Assert
527:                                .assrt(
528:                                        "local || time < transitionTimes[0] || time >= transitionTimes[i]",
529:                                        local || time < transitionTimes[0]
530:                                                || time >= transitionTimes[i]);
531:                        Assert
532:                                .assrt(
533:                                        "local || i == transitionCount-1 || time < transitionTimes[i+1]",
534:                                        local
535:                                                || i == transitionCount - 1
536:                                                || time < transitionTimes[i + 1]);
537:                    }
538:                    if (i == 0) {
539:                        // Check if the given time is before the very first transition
540:                        int firstTransition = transitionTimes[0];
541:                        int initialRawOffset = rawOffset(getInt(typeData[0]));
542:                        if (local) {
543:                            firstTransition += initialRawOffset;
544:                        }
545:                        if (time >= firstTransition) {
546:                            // The given time is between the first and the second transition
547:                            offsets[0] = initialRawOffset * MILLIS_PER_SECOND;
548:                            offsets[1] = dstOffset(getInt(typeData[0]))
549:                                    * MILLIS_PER_SECOND;
550:                        } else {
551:                            // The given time is before the first transition
552:                            offsets[0] = initialRawOffset * MILLIS_PER_SECOND;
553:                            offsets[1] = 0;
554:                        }
555:                    } else {
556:                        int index = getInt(typeData[i]);
557:                        offsets[0] = rawOffset(index) * MILLIS_PER_SECOND;
558:                        offsets[1] = dstOffset(index) * MILLIS_PER_SECOND;
559:                    }
560:                } else {
561:                    // No transitions, single pair of offsets only
562:                    offsets[0] = rawOffset(0) * MILLIS_PER_SECOND;
563:                    offsets[1] = dstOffset(0) * MILLIS_PER_SECOND;
564:                }
565:            }
566:
567:            private int zoneOffset(int index) {
568:                index = index << 1;
569:                return typeOffsets[index] + typeOffsets[index + 1];
570:            }
571:
572:            private int rawOffset(int index) {
573:                return typeOffsets[(int) (index << 1)];
574:            }
575:
576:            private int dstOffset(int index) {
577:                return typeOffsets[(int) ((index << 1) + 1)];
578:            }
579:
580:            // temp
581:            public String toString() {
582:                StringBuffer buf = new StringBuffer();
583:                buf.append(super .toString());
584:                buf.append('[');
585:                buf.append("transitionCount=" + transitionCount);
586:                buf.append(",typeCount=" + typeCount);
587:                buf.append(",transitionTimes=");
588:                if (transitionTimes != null) {
589:                    buf.append('[');
590:                    for (int i = 0; i < transitionTimes.length; ++i) {
591:                        if (i > 0) {
592:                            buf.append(',');
593:                        }
594:                        buf.append(Integer.toString(transitionTimes[i]));
595:                    }
596:                    buf.append(']');
597:                } else {
598:                    buf.append("null");
599:                }
600:                buf.append(",typeOffsets=");
601:                if (typeOffsets != null) {
602:                    buf.append('[');
603:                    for (int i = 0; i < typeOffsets.length; ++i) {
604:                        if (i > 0) {
605:                            buf.append(',');
606:                        }
607:                        buf.append(Integer.toString(typeOffsets[i]));
608:                    }
609:                    buf.append(']');
610:                } else {
611:                    buf.append("null");
612:                }
613:                buf.append(",finalYear=" + finalYear);
614:                buf.append(",finalMillis=" + finalMillis);
615:                buf.append(",finalZone=" + finalZone);
616:                buf.append(']');
617:
618:                return buf.toString();
619:            }
620:
621:            /**
622:             * Number of transitions, 0..~370
623:             */
624:            private int transitionCount;
625:
626:            /**
627:             * Number of types, 1..255
628:             */
629:            private int typeCount;
630:
631:            /**
632:             * Time of each transition in seconds from 1970 epoch.
633:             * Length is transitionCount int32_t's.
634:             */
635:            private int[] transitionTimes; // alias into res; do not delete
636:
637:            /**
638:             * Offset from GMT in seconds for each type.
639:             * Length is typeCount int32_t's.
640:             */
641:            private int[] typeOffsets; // alias into res; do not delete
642:
643:            /**
644:             * Type description data, consisting of transitionCount uint8_t
645:             * type indices (from 0..typeCount-1).
646:             * Length is transitionCount int8_t's.
647:             */
648:            private byte[] typeData; // alias into res; do not delete
649:
650:            /**
651:             * The last year for which the transitions data are to be used
652:             * rather than the finalZone.  If there is no finalZone, then this
653:             * is set to INT32_MAX.  NOTE: This corresponds to the year _before_
654:             * the one indicated by finalMillis.
655:             */
656:            private int finalYear = Integer.MAX_VALUE;
657:
658:            /**
659:             * The millis for the start of the first year for which finalZone
660:             * is to be used, or DBL_MAX if finalZone is 0.  NOTE: This is
661:             * 0:00 GMT Jan 1, <finalYear + 1> (not <finalMillis>).
662:             */
663:            private double finalMillis = Double.MAX_VALUE;
664:
665:            /**
666:             * A SimpleTimeZone that governs the behavior for years > finalYear.
667:             * If and only if finalYear == INT32_MAX then finalZone == 0.
668:             */
669:            private SimpleTimeZone finalZone = null; // owned, may be NULL
670:
671:            private static final boolean DEBUG = ICUDebug.enabled("olson");
672:            private static final int[] DAYS_BEFORE = new int[] { 0, 31, 59, 90,
673:                    120, 151, 181, 212, 243, 273, 304, 334, 0, 31, 60, 91, 121,
674:                    152, 182, 213, 244, 274, 305, 335 };
675:
676:            private static final int[] MONTH_LENGTH = new int[] { 31, 28, 31,
677:                    30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29, 31, 30, 31, 30,
678:                    31, 31, 30, 31, 30, 31 };
679:            private static final int JULIAN_1_CE = 1721426; // January 1, 1 CE Gregorian
680:            private static final int JULIAN_1970_CE = 2440588; // January 1, 1970 CE Gregorian
681:            private static final int MILLIS_PER_SECOND = 1000;
682:            private static final int SECONDS_PER_DAY = 24 * 60 * 60;
683:
684:            private static final double fieldsToDay(int year, int month, int dom) {
685:                int y = year - 1;
686:                double julian = 365 * y + myFloorDivide(y, 4)
687:                        + (JULIAN_1_CE - 3) + // Julian cal
688:                        myFloorDivide(y, 400) - myFloorDivide(y, 100) + 2 + // => Gregorian cal
689:                        DAYS_BEFORE[month + (isLeapYear(year) ? 12 : 0)] + dom; // => month/dom
690:
691:                return julian - JULIAN_1970_CE; // JD => epoch day
692:            }
693:
694:            private static final boolean isLeapYear(int year) {
695:                // year&0x3 == year%4
696:                return ((year & 0x3) == 0)
697:                        && ((year % 100 != 0) || (year % 400 == 0));
698:            }
699:
700:            private static ICUResourceBundle loadRule(ICUResourceBundle top,
701:                    String ruleid) {
702:                ICUResourceBundle r = top.get("Rules");
703:                r = r.get(ruleid);
704:                return r;
705:            }
706:
707:            /**
708:             * Divide two long integers, returning the floor of the quotient.
709:             * <p>
710:             * Unlike the built-in division, this is mathematically well-behaved.
711:             * E.g., <code>-1/4</code> => 0
712:             * but <code>floorDivide(-1,4)</code> => -1.
713:             * @param numerator the numerator
714:             * @param denominator a divisor which must be > 0
715:             * @return the floor of the quotient.
716:             * @stable ICU 2.0
717:             */
718:            private static final long myFloorDivide(long numerator,
719:                    long denominator) {
720:                // We do this computation in order to handle
721:                // a numerator of Long.MIN_VALUE correctly
722:                return (numerator >= 0) ? numerator / denominator
723:                        : ((numerator + 1) / denominator) - 1;
724:            }
725:
726:            int[] dayToFields(double day) {
727:                int year, month, dom, dow;
728:                double doy;
729:                int[] ret = new int[5];
730:
731:                // Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
732:                day += JULIAN_1970_CE - JULIAN_1_CE;
733:
734:                // Convert from the day number to the multiple radix
735:                // representation.  We use 400-year, 100-year, and 4-year cycles.
736:                // For example, the 4-year cycle has 4 years + 1 leap day; giving
737:                // 1461 == 365*4 + 1 days.
738:                double[] temp = floorDivide(day, 146097); // 400-year cycle length
739:                double n400 = temp[0];
740:                doy = temp[1];
741:                temp = floorDivide(doy, 36524); // 100-year cycle length
742:                double n100 = temp[0];
743:                doy = temp[1];
744:                temp = floorDivide(doy, 1461); // 4-year cycle length
745:                double n4 = temp[0];
746:                doy = temp[1];
747:                temp = floorDivide(doy, 365);
748:                double n1 = temp[0];
749:                doy = temp[1];
750:                year = (int) (400 * n400 + 100 * n100 + 4 * n4 + n1);
751:                if (n100 == 4 || n1 == 4) {
752:                    doy = 365; // Dec 31 at end of 4- or 400-year cycle
753:                } else {
754:                    ++year;
755:                }
756:
757:                boolean isLeap = isLeapYear(year);
758:
759:                // Gregorian day zero is a Monday.
760:                dow = (int) ((day + 1) % 7);
761:                dow += (dow < 0) ? (Calendar.SUNDAY + 7) : Calendar.SUNDAY;
762:
763:                // Common Julian/Gregorian calculation
764:                int correction = 0;
765:                int march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
766:                if (doy >= march1) {
767:                    correction = isLeap ? 1 : 2;
768:                }
769:                month = (int) ((12 * (doy + correction) + 6) / 367); // zero-based month
770:                dom = (int) (doy - DAYS_BEFORE[month + (isLeap ? 12 : 0)] + 1); // one-based DOM
771:                doy++; // one-based doy
772:                ret[0] = year;
773:                ret[1] = month;
774:                ret[2] = dom;
775:                ret[3] = dow;
776:                ret[4] = (int) doy;
777:                return ret;
778:            }
779:
780:            public boolean equals(Object obj) {
781:                if (!super .equals(obj))
782:                    return false; // super does class check
783:
784:                OlsonTimeZone z = (OlsonTimeZone) obj;
785:
786:                return (Utility.arrayEquals(typeData, z.typeData) ||
787:                // If the pointers are not equal, the zones may still
788:                // be equal if their rules and transitions are equal
789:                (finalYear == z.finalYear &&
790:                // Don't compare finalMillis; if finalYear is ==, so is finalMillis
791:                ((finalZone == null && z.finalZone == null) || (finalZone != null
792:                        && z.finalZone != null && finalZone.equals(z.finalZone))
793:                        && transitionCount == z.transitionCount
794:                        && typeCount == z.typeCount
795:                        && Utility.arrayEquals(transitionTimes,
796:                                z.transitionTimes)
797:                        && Utility.arrayEquals(typeOffsets, z.typeOffsets)
798:                        && Utility.arrayEquals(typeData, z.typeData))));
799:
800:            }
801:
802:            public int hashCode() {
803:                int ret = (int) (finalYear ^ (finalYear >>> 4)
804:                        + transitionCount ^ (transitionCount >>> 6) + typeCount ^ (typeCount >>> 8)
805:                        + Double.doubleToLongBits(finalMillis)
806:                        + (finalZone == null ? 0 : finalZone.hashCode())
807:                        + super .hashCode());
808:                for (int i = 0; i < transitionTimes.length; i++) {
809:                    ret += transitionTimes[i] ^ (transitionTimes[i] >>> 8);
810:                }
811:                for (int i = 0; i < typeOffsets.length; i++) {
812:                    ret += typeOffsets[i] ^ (typeOffsets[i] >>> 8);
813:                }
814:                for (int i = 0; i < typeData.length; i++) {
815:                    ret += typeData[i] & UNSIGNED_BYTE_MASK;
816:                }
817:                return ret;
818:            }
819:            /*
820:            private void readObject(ObjectInputStream s) throws IOException  {
821:                s.defaultReadObject();
822:                // customized deserialization code
823:               
824:                // followed by code to update the object, if necessary
825:            }
826:             */
827:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.