Source Code Cross Referenced for ChineseCalendar.java in  » Internationalization-Localization » icu4j » com » ibm » icu » util » 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.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*********************************************************************
002:         * Copyright (C) 2000-2006, International Business Machines Corporation and
003:         * others. All Rights Reserved.
004:         *********************************************************************
005:         */package com.ibm.icu.util;
006:
007:        import com.ibm.icu.text.*;
008:        import com.ibm.icu.util.TimeZone;
009:        import com.ibm.icu.impl.CalendarAstronomer;
010:        import com.ibm.icu.impl.CalendarCache;
011:
012:        import java.io.IOException;
013:        import java.io.ObjectInputStream;
014:        import java.util.Locale;
015:
016:        /**
017:         * <code>ChineseCalendar</code> is a concrete subclass of {@link Calendar}
018:         * that implements a traditional Chinese calendar.  The traditional Chinese
019:         * calendar is a lunisolar calendar: Each month starts on a new moon, and
020:         * the months are numbered according to solar events, specifically, to
021:         * guarantee that month 11 always contains the winter solstice.  In order
022:         * to accomplish this, leap months are inserted in certain years.  Leap
023:         * months are numbered the same as the month they follow.  The decision of
024:         * which month is a leap month depends on the relative movements of the sun
025:         * and moon.
026:         *
027:         * <p>This class defines one addition field beyond those defined by
028:         * <code>Calendar</code>: The <code>IS_LEAP_MONTH</code> field takes the
029:         * value of 0 for normal months, or 1 for leap months.
030:         *
031:         * <p>All astronomical computations are performed with respect to a time
032:         * zone of GMT+8:00 and a longitude of 120 degrees east.  Although some
033:         * calendars implement a historically more accurate convention of using
034:         * Beijing's local longitude (116 degrees 25 minutes east) and time zone
035:         * (GMT+7:45:40) for dates before 1929, we do not implement this here.
036:         *
037:         * <p>Years are counted in two different ways in the Chinese calendar.  The
038:         * first method is by sequential numbering from the 61st year of the reign
039:         * of Huang Di, 2637 BCE, which is designated year 1 on the Chinese
040:         * calendar.  The second method uses 60-year cycles from the same starting
041:         * point, which is designated year 1 of cycle 1.  In this class, the
042:         * <code>EXTENDED_YEAR</code> field contains the sequential year count.
043:         * The <code>ERA</code> field contains the cycle number, and the
044:         * <code>YEAR</code> field contains the year of the cycle, a value between
045:         * 1 and 60.
046:         *
047:         * <p>There is some variation in what is considered the starting point of
048:         * the calendar, with some sources starting in the first year of the reign
049:         * of Huang Di, rather than the 61st.  This gives continuous year numbers
050:         * 60 years greater and cycle numbers one greater than what this class
051:         * implements.
052:         *
053:         * <p>Because <code>ChineseCalendar</code> defines an additional field and
054:         * redefines the way the <code>ERA</code> field is used, it requires a new
055:         * format class, <code>ChineseDateFormat</code>.  As always, use the
056:         * methods <code>DateFormat.getXxxInstance(Calendar cal,...)</code> to
057:         * obtain a formatter for this calendar.
058:         *
059:         * <p>References:<ul>
060:         * 
061:         * <li>Dershowitz and Reingold, <i>Calendrical Calculations</i>,
062:         * Cambridge University Press, 1997</li>
063:         * 
064:         * <li>Helmer Aslaksen's
065:         * <a href="http://www.math.nus.edu.sg/aslaksen/calendar/chinese.shtml">
066:         * Chinese Calendar page</a></li>
067:         *
068:         * <li>The <a href="http://www.tondering.dk/claus/calendar.html">
069:         * Calendar FAQ</a></li>
070:         *
071:         * </ul>
072:         *
073:         * <p>
074:         * This class should not be subclassed.</p>
075:         * <p>
076:         * ChineseCalendar usually should be instantiated using 
077:         * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a <code>ULocale</code>
078:         * with the tag <code>"@calendar=chinese"</code>.</p>
079:         *
080:         * @see com.ibm.icu.text.ChineseDateFormat
081:         * @see com.ibm.icu.util.Calendar
082:         * @author Alan Liu
083:         * @stable ICU 2.8
084:         */
085:        public class ChineseCalendar extends Calendar {
086:            // jdk1.4.2 serialver
087:            private static final long serialVersionUID = 7312110751940929420L;
088:
089:            //------------------------------------------------------------------
090:            // Developer Notes
091:            // 
092:            // Time is represented as a scalar in two ways in this class.  One is
093:            // the usual UTC epoch millis, that is, milliseconds after January 1,
094:            // 1970 Gregorian, 0:00:00.000 UTC.  The other is in terms of 'local
095:            // days.'  This is the number of days after January 1, 1970 Gregorian,
096:            // local to Beijing, China (since all computations of the Chinese
097:            // calendar are done in Beijing).  That is, 0 represents January 1,
098:            // 1970 0:00 Asia/Shanghai.  Conversion of local days to and from
099:            // standard epoch milliseconds is accomplished by the daysToMillis()
100:            // and millisToDays() methods.
101:            // 
102:            // Several methods use caches to improve performance.  Caches are at
103:            // the object, not class level, under the assumption that typical
104:            // usage will be to have one instance of ChineseCalendar at a time.
105:
106:            /**
107:             * We have one instance per object, and we don't synchronize it because
108:             * Calendar doesn't support multithreaded execution in the first place.
109:             */
110:            private transient CalendarAstronomer astro = new CalendarAstronomer();
111:
112:            /**
113:             * Cache that maps Gregorian year to local days of winter solstice.
114:             * @see #winterSolstice
115:             */
116:            private transient CalendarCache winterSolsticeCache = new CalendarCache();
117:
118:            /**
119:             * Cache that maps Gregorian year to local days of Chinese new year.
120:             * @see #newYear
121:             */
122:            private transient CalendarCache newYearCache = new CalendarCache();
123:
124:            /**
125:             * True if the current year is a leap year.  Updated with each time to
126:             * fields resolution.
127:             * @see #computeChineseFields
128:             */
129:            private transient boolean isLeapYear;
130:
131:            //------------------------------------------------------------------
132:            // Constructors
133:            //------------------------------------------------------------------
134:
135:            /**
136:             * Construct a Chinese calendar with the default time zone and locale.
137:             * @stable ICU 2.8
138:             */
139:            public ChineseCalendar() {
140:                super ();
141:                setTimeInMillis(System.currentTimeMillis());
142:            }
143:
144:            /**
145:             * Construct a Chinese calendar with the given time zone and locale.
146:             * @param zone time zone for this calendar
147:             * @param locale locale for this calendar
148:             * @stable ICU 2.8
149:             */
150:            public ChineseCalendar(TimeZone zone, Locale locale) {
151:                super (zone, locale);
152:                setTimeInMillis(System.currentTimeMillis());
153:            }
154:
155:            /**
156:             * Construct a Chinese calendar with the given time zone and locale.
157:             * @param zone time zone for this calendar
158:             * @param locale ulocale for this calendar
159:             * @draft ICU 3.2
160:             * @provisional This API might change or be removed in a future release.
161:             */
162:            public ChineseCalendar(TimeZone zone, ULocale locale) {
163:                super (zone, locale);
164:                setTimeInMillis(System.currentTimeMillis());
165:            }
166:
167:            //------------------------------------------------------------------
168:            // Public constants
169:            //------------------------------------------------------------------
170:
171:            /**
172:             * Field indicating whether or not the current month is a leap month.
173:             * Should have a value of 0 for non-leap months, and 1 for leap months.
174:             * @stable ICU 2.8
175:             */
176:            public static int IS_LEAP_MONTH = BASE_FIELD_COUNT;
177:
178:            /**
179:             * Count of fields in this class.
180:             */
181:            private static final int FIELD_COUNT = IS_LEAP_MONTH + 1;
182:
183:            //------------------------------------------------------------------
184:            // Calendar framework
185:            //------------------------------------------------------------------
186:
187:            /**
188:             * Override Calendar to allocate our additional field.
189:             * @stable ICU 2.8
190:             */
191:            protected int[] handleCreateFields() {
192:                return new int[FIELD_COUNT];
193:            }
194:
195:            /**
196:             * Array defining the limits of field values for this class.  Field
197:             * limits which are invariant with respect to calendar system and
198:             * defined by Calendar are left blank.
199:             *
200:             * Notes:
201:             *
202:             * ERA 5000000 / 60 = 83333.
203:             *
204:             * MONTH There are 12 or 13 lunar months in a year.  However, we always
205:             * number them 0..11, with an intercalated, identically numbered leap
206:             * month, when necessary.
207:             *
208:             * DAY_OF_YEAR In a non-leap year there are 353, 354, or 355 days.  In
209:             * a leap year there are 383, 384, or 385 days.
210:             *
211:             * WEEK_OF_YEAR The least maximum occurs if there are 353 days in the
212:             * year, and the first 6 are the last week of the previous year.  Then
213:             * we have 49 full weeks and 4 days in the last week: 6 + 49*7 + 4 =
214:             * 353.  So the least maximum is 50.  The maximum occurs if there are
215:             * 385 days in the year, and WOY 1 extends 6 days into the prior year.
216:             * Then there are 54 full weeks, and 6 days in the last week: 1 + 54*7
217:             * + 6 = 385.  The 6 days of the last week will fall into WOY 1 of the
218:             * next year.  Maximum is 55.
219:             *
220:             * WEEK_OF_MONTH In a 29 day month, if the first 7 days make up week 1
221:             * that leaves 3 full weeks and 1 day at the end.  The least maximum is
222:             * thus 5.  In a 30 days month, if the previous 6 days belong WOM 1 of
223:             * this month, we have 4 full weeks and 1 days at the end (which
224:             * technically will be WOM 1 of the next month, but will be reported by
225:             * time->fields and hence by getActualMaximum as WOM 6 of this month).
226:             * Maximum is 6.
227:             *
228:             * DAY_OF_WEEK_IN_MONTH In a 29 or 30 day month, there are 4 full weeks
229:             * plus 1 or 2 days at the end, so the maximum is always 5.
230:             */
231:            private static final int LIMITS[][] = {
232:            // Minimum  Greatest    Least  Maximum
233:                    //           Minimum  Maximum
234:                    { 1, 1, 83333, 83333 }, // ERA
235:                    { 1, 1, 70, 70 }, // YEAR
236:                    { 0, 0, 11, 11 }, // MONTH
237:                    { 1, 1, 50, 55 }, // WEEK_OF_YEAR
238:                    { 1, 1, 5, 6 }, // WEEK_OF_MONTH
239:                    { 1, 1, 29, 30 }, // DAY_OF_MONTH
240:                    { 1, 1, 353, 385 }, // DAY_OF_YEAR
241:                    {/*                                  */}, // DAY_OF_WEEK
242:                    { -1, -1, 5, 5 }, // DAY_OF_WEEK_IN_MONTH
243:                    {/*                                  */}, // AM_PM
244:                    {/*                                  */}, // HOUR
245:                    {/*                                  */}, // HOUR_OF_DAY
246:                    {/*                                  */}, // MINUTE
247:                    {/*                                  */}, // SECOND
248:                    {/*                                  */}, // MILLISECOND
249:                    {/*                                  */}, // ZONE_OFFSET
250:                    {/*                                  */}, // DST_OFFSET
251:                    { -5000001, -5000001, 5000001, 5000001 }, // YEAR_WOY
252:                    {/*                                  */}, // DOW_LOCAL
253:                    { -5000000, -5000000, 5000000, 5000000 }, // EXTENDED_YEAR
254:                    {/*                                  */}, // JULIAN_DAY
255:                    {/*                                  */}, // MILLISECONDS_IN_DAY
256:                    { 0, 0, 1, 1 }, // IS_LEAP_MONTH
257:            };
258:
259:            /**
260:             * Override Calendar to return the limit value for the given field.
261:             * @stable ICU 2.8
262:             */
263:            protected int handleGetLimit(int field, int limitType) {
264:                return LIMITS[field][limitType];
265:            }
266:
267:            /**
268:             * Implement abstract Calendar method to return the extended year
269:             * defined by the current fields.  This will use either the ERA and
270:             * YEAR field as the cycle and year-of-cycle, or the EXTENDED_YEAR
271:             * field as the continuous year count, depending on which is newer.
272:             * @stable ICU 2.8
273:             */
274:            protected int handleGetExtendedYear() {
275:                int year;
276:                if (newestStamp(ERA, YEAR, UNSET) <= getStamp(EXTENDED_YEAR)) {
277:                    year = internalGet(EXTENDED_YEAR, 1); // Default to year 1
278:                } else {
279:                    int cycle = internalGet(ERA, 1) - 1; // 0-based cycle
280:                    year = cycle * 60 + internalGet(YEAR, 1);
281:                }
282:                return year;
283:            }
284:
285:            /**
286:             * Override Calendar method to return the number of days in the given
287:             * extended year and month.
288:             *
289:             * <p>Note: This method also reads the IS_LEAP_MONTH field to determine
290:             * whether or not the given month is a leap month.
291:             * @stable ICU 2.8
292:             */
293:            protected int handleGetMonthLength(int extendedYear, int month) {
294:                int this Start = handleComputeMonthStart(extendedYear, month,
295:                        true)
296:                        - EPOCH_JULIAN_DAY + 1; // Julian day -> local days
297:                int nextStart = newMoonNear(this Start + SYNODIC_GAP, true);
298:                return nextStart - this Start;
299:            }
300:
301:            /**
302:             * Framework method to create a calendar-specific DateFormat object
303:             * using the the given pattern.  This method is responsible for
304:             * creating the calendar- specific DateFormat and DateFormatSymbols
305:             * objects as needed.
306:             * @stable ICU 2.8
307:             */
308:            protected DateFormat handleGetDateFormat(String pattern,
309:                    ULocale locale) {
310:                return new ChineseDateFormat(pattern, locale);
311:            }
312:
313:            /**
314:             * Field resolution table that incorporates IS_LEAP_MONTH.
315:             */
316:            static final int[][][] CHINESE_DATE_PRECEDENCE = {
317:                    { { DAY_OF_MONTH }, { WEEK_OF_YEAR, DAY_OF_WEEK },
318:                            { WEEK_OF_MONTH, DAY_OF_WEEK },
319:                            { DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK },
320:                            { WEEK_OF_YEAR, DOW_LOCAL },
321:                            { WEEK_OF_MONTH, DOW_LOCAL },
322:                            { DAY_OF_WEEK_IN_MONTH, DOW_LOCAL },
323:                            { DAY_OF_YEAR },
324:                            { RESOLVE_REMAP | DAY_OF_MONTH, IS_LEAP_MONTH }, },
325:                    {
326:                            { WEEK_OF_YEAR },
327:                            { WEEK_OF_MONTH },
328:                            { DAY_OF_WEEK_IN_MONTH },
329:                            { RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK },
330:                            { RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DOW_LOCAL }, }, };
331:
332:            /**
333:             * Override Calendar to add IS_LEAP_MONTH to the field resolution
334:             * table.
335:             * @stable ICU 2.8
336:             */
337:            protected int[][][] getFieldResolutionTable() {
338:                return CHINESE_DATE_PRECEDENCE;
339:            }
340:
341:            /**
342:             * Adjust this calendar to be delta months before or after a given
343:             * start position, pinning the day of month if necessary.  The start
344:             * position is given as a local days number for the start of the month
345:             * and a day-of-month.  Used by add() and roll().
346:             * @param newMoon the local days of the first day of the month of the
347:             * start position (days after January 1, 1970 0:00 Asia/Shanghai)
348:             * @param dom the 1-based day-of-month of the start position
349:             * @param delta the number of months to move forward or backward from
350:             * the start position
351:             */
352:            private void offsetMonth(int newMoon, int dom, int delta) {
353:                // Move to the middle of the month before our target month.
354:                newMoon += (int) (CalendarAstronomer.SYNODIC_MONTH * (delta - 0.5));
355:
356:                // Search forward to the target month's new moon
357:                newMoon = newMoonNear(newMoon, true);
358:
359:                // Find the target dom
360:                int jd = newMoon + EPOCH_JULIAN_DAY - 1 + dom;
361:
362:                // Pin the dom.  In this calendar all months are 29 or 30 days
363:                // so pinning just means handling dom 30.
364:                if (dom > 29) {
365:                    set(JULIAN_DAY, jd - 1);
366:                    // TODO Fix this.  We really shouldn't ever have to
367:                    // explicitly call complete().  This is either a bug in
368:                    // this method, in ChineseCalendar, or in
369:                    // Calendar.getActualMaximum().  I suspect the last.
370:                    complete();
371:                    if (getActualMaximum(DAY_OF_MONTH) >= dom) {
372:                        set(JULIAN_DAY, jd);
373:                    }
374:                } else {
375:                    set(JULIAN_DAY, jd);
376:                }
377:            }
378:
379:            /**
380:             * Override Calendar to handle leap months properly.
381:             * @stable ICU 2.8
382:             */
383:            public void add(int field, int amount) {
384:                switch (field) {
385:                case MONTH:
386:                    if (amount != 0) {
387:                        int dom = get(DAY_OF_MONTH);
388:                        int day = get(JULIAN_DAY) - EPOCH_JULIAN_DAY; // Get local day
389:                        int moon = day - dom + 1; // New moon 
390:                        offsetMonth(moon, dom, amount);
391:                    }
392:                    break;
393:                default:
394:                    super .add(field, amount);
395:                    break;
396:                }
397:            }
398:
399:            /**
400:             * Override Calendar to handle leap months properly.
401:             * @stable ICU 2.8
402:             */
403:            public void roll(int field, int amount) {
404:                switch (field) {
405:                case MONTH:
406:                    if (amount != 0) {
407:                        int dom = get(DAY_OF_MONTH);
408:                        int day = get(JULIAN_DAY) - EPOCH_JULIAN_DAY; // Get local day
409:                        int moon = day - dom + 1; // New moon (start of this month)
410:
411:                        // Note throughout the following:  Months 12 and 1 are never
412:                        // followed by a leap month (D&R p. 185).
413:
414:                        // Compute the adjusted month number m.  This is zero-based
415:                        // value from 0..11 in a non-leap year, and from 0..12 in a
416:                        // leap year.
417:                        int m = get(MONTH); // 0-based month
418:                        if (isLeapYear) { // (member variable)
419:                            if (get(IS_LEAP_MONTH) == 1) {
420:                                ++m;
421:                            } else {
422:                                // Check for a prior leap month.  (In the
423:                                // following, month 0 is the first month of the
424:                                // year.)  Month 0 is never followed by a leap
425:                                // month, and we know month m is not a leap month.
426:                                // moon1 will be the start of month 0 if there is
427:                                // no leap month between month 0 and month m;
428:                                // otherwise it will be the start of month 1.
429:                                int moon1 = moon
430:                                        - (int) (CalendarAstronomer.SYNODIC_MONTH * (m - 0.5));
431:                                moon1 = newMoonNear(moon1, true);
432:                                if (isLeapMonthBetween(moon1, moon)) {
433:                                    ++m;
434:                                }
435:                            }
436:                        }
437:
438:                        // Now do the standard roll computation on m, with the
439:                        // allowed range of 0..n-1, where n is 12 or 13.
440:                        int n = isLeapYear ? 13 : 12; // Months in this year
441:                        int newM = (m + amount) % n;
442:                        if (newM < 0) {
443:                            newM += n;
444:                        }
445:
446:                        if (newM != m) {
447:                            offsetMonth(moon, dom, newM - m);
448:                        }
449:                    }
450:                    break;
451:                default:
452:                    super .roll(field, amount);
453:                    break;
454:                }
455:            }
456:
457:            //------------------------------------------------------------------
458:            // Support methods and constants
459:            //------------------------------------------------------------------
460:
461:            /**
462:             * The start year of the Chinese calendar, the 61st year of the reign
463:             * of Huang Di.  Some sources use the first year of his reign,
464:             * resulting in EXTENDED_YEAR values 60 years greater and ERA (cycle)
465:             * values one greater.
466:             */
467:            private static final int CHINESE_EPOCH_YEAR = -2636; // Gregorian year
468:
469:            /**
470:             * The offset from GMT in milliseconds at which we perform astronomical
471:             * computations.  Some sources use a different historically accurate
472:             * offset of GMT+7:45:40 for years before 1929; we do not do this.
473:             */
474:            private static final long CHINA_OFFSET = 8 * ONE_HOUR;
475:
476:            /**
477:             * Value to be added or subtracted from the local days of a new moon to
478:             * get close to the next or prior new moon, but not cross it.  Must be
479:             * >= 1 and < CalendarAstronomer.SYNODIC_MONTH.
480:             */
481:            private static final int SYNODIC_GAP = 25;
482:
483:            /**
484:             * Convert local days to UTC epoch milliseconds.
485:             * @param days days after January 1, 1970 0:00 Asia/Shanghai
486:             * @return milliseconds after January 1, 1970 0:00 GMT
487:             */
488:            private static final long daysToMillis(int days) {
489:                return (days * ONE_DAY) - CHINA_OFFSET;
490:            }
491:
492:            /**
493:             * Convert UTC epoch milliseconds to local days.
494:             * @param millis milliseconds after January 1, 1970 0:00 GMT
495:             * @return days after January 1, 1970 0:00 Asia/Shanghai
496:             */
497:            private static final int millisToDays(long millis) {
498:                return (int) floorDivide(millis + CHINA_OFFSET, ONE_DAY);
499:            }
500:
501:            //------------------------------------------------------------------
502:            // Astronomical computations
503:            //------------------------------------------------------------------
504:
505:            /**
506:             * Return the major solar term on or after December 15 of the given
507:             * Gregorian year, that is, the winter solstice of the given year.
508:             * Computations are relative to Asia/Shanghai time zone.
509:             * @param gyear a Gregorian year
510:             * @return days after January 1, 1970 0:00 Asia/Shanghai of the
511:             * winter solstice of the given year
512:             */
513:            private int winterSolstice(int gyear) {
514:
515:                long cacheValue = winterSolsticeCache.get(gyear);
516:
517:                if (cacheValue == CalendarCache.EMPTY) {
518:                    // In books December 15 is used, but it fails for some years
519:                    // using our algorithms, e.g.: 1298 1391 1492 1553 1560.  That
520:                    // is, winterSolstice(1298) starts search at Dec 14 08:00:00
521:                    // PST 1298 with a final result of Dec 14 10:31:59 PST 1299.
522:                    long ms = daysToMillis(computeGregorianMonthStart(gyear,
523:                            DECEMBER)
524:                            + 1 - EPOCH_JULIAN_DAY);
525:                    astro.setTime(ms);
526:
527:                    // Winter solstice is 270 degrees solar longitude aka Dongzhi
528:                    long solarLong = astro.getSunTime(
529:                            CalendarAstronomer.WINTER_SOLSTICE, true);
530:                    cacheValue = millisToDays(solarLong);
531:                    winterSolsticeCache.put(gyear, cacheValue);
532:                }
533:                return (int) cacheValue;
534:            }
535:
536:            /**
537:             * Return the closest new moon to the given date, searching either
538:             * forward or backward in time.
539:             * @param days days after January 1, 1970 0:00 Asia/Shanghai
540:             * @param after if true, search for a new moon on or after the given
541:             * date; otherwise, search for a new moon before it
542:             * @return days after January 1, 1970 0:00 Asia/Shanghai of the nearest
543:             * new moon after or before <code>days</code>
544:             */
545:            private int newMoonNear(int days, boolean after) {
546:
547:                astro.setTime(daysToMillis(days));
548:                long newMoon = astro.getMoonTime(CalendarAstronomer.NEW_MOON,
549:                        after);
550:
551:                return millisToDays(newMoon);
552:            }
553:
554:            /**
555:             * Return the nearest integer number of synodic months between
556:             * two dates.
557:             * @param day1 days after January 1, 1970 0:00 Asia/Shanghai
558:             * @param day2 days after January 1, 1970 0:00 Asia/Shanghai
559:             * @return the nearest integer number of months between day1 and day2
560:             */
561:            private int synodicMonthsBetween(int day1, int day2) {
562:                return (int) Math.round((day2 - day1)
563:                        / CalendarAstronomer.SYNODIC_MONTH);
564:            }
565:
566:            /**
567:             * Return the major solar term on or before a given date.  This
568:             * will be an integer from 1..12, with 1 corresponding to 330 degrees,
569:             * 2 to 0 degrees, 3 to 30 degrees,..., and 12 to 300 degrees.
570:             * @param days days after January 1, 1970 0:00 Asia/Shanghai
571:             */
572:            private int majorSolarTerm(int days) {
573:
574:                astro.setTime(daysToMillis(days));
575:
576:                // Compute (floor(solarLongitude / (pi/6)) + 2) % 12
577:                int term = ((int) Math.floor(6 * astro.getSunLongitude()
578:                        / Math.PI) + 2) % 12;
579:                if (term < 1) {
580:                    term += 12;
581:                }
582:                return term;
583:            }
584:
585:            /**
586:             * Return true if the given month lacks a major solar term.
587:             * @param newMoon days after January 1, 1970 0:00 Asia/Shanghai of a new
588:             * moon
589:             */
590:            private boolean hasNoMajorSolarTerm(int newMoon) {
591:
592:                int mst = majorSolarTerm(newMoon);
593:                int nmn = newMoonNear(newMoon + SYNODIC_GAP, true);
594:                int mstt = majorSolarTerm(nmn);
595:                return mst == mstt;
596:                /*
597:                return majorSolarTerm(newMoon) ==
598:                    majorSolarTerm(newMoonNear(newMoon + SYNODIC_GAP, true));
599:                 */
600:            }
601:
602:            //------------------------------------------------------------------
603:            // Time to fields
604:            //------------------------------------------------------------------
605:
606:            /**
607:             * Return true if there is a leap month on or after month newMoon1 and
608:             * at or before month newMoon2.
609:             * @param newMoon1 days after January 1, 1970 0:00 Asia/Shanghai of a
610:             * new moon
611:             * @param newMoon2 days after January 1, 1970 0:00 Asia/Shanghai of a
612:             * new moon
613:             */
614:            private boolean isLeapMonthBetween(int newMoon1, int newMoon2) {
615:
616:                // This is only needed to debug the timeOfAngle divergence bug.
617:                // Remove this later. Liu 11/9/00
618:                // DEBUG
619:                if (synodicMonthsBetween(newMoon1, newMoon2) >= 50) {
620:                    throw new IllegalArgumentException("isLeapMonthBetween("
621:                            + newMoon1 + ", " + newMoon2
622:                            + "): Invalid parameters");
623:                }
624:
625:                return (newMoon2 >= newMoon1)
626:                        && (isLeapMonthBetween(newMoon1, newMoonNear(newMoon2
627:                                - SYNODIC_GAP, false)) || hasNoMajorSolarTerm(newMoon2));
628:            }
629:
630:            /**
631:             * Override Calendar to compute several fields specific to the Chinese
632:             * calendar system.  These are:
633:             *
634:             * <ul><li>ERA
635:             * <li>YEAR
636:             * <li>MONTH
637:             * <li>DAY_OF_MONTH
638:             * <li>DAY_OF_YEAR
639:             * <li>EXTENDED_YEAR</ul>
640:             * 
641:             * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
642:             * method is called.  The getGregorianXxx() methods return Gregorian
643:             * calendar equivalents for the given Julian day.
644:             *
645:             * <p>Compute the ChineseCalendar-specific field IS_LEAP_MONTH.
646:             * @stable ICU 2.8
647:             */
648:            protected void handleComputeFields(int julianDay) {
649:
650:                computeChineseFields(julianDay - EPOCH_JULIAN_DAY, // local days
651:                        getGregorianYear(), getGregorianMonth(), true); // set all fields
652:            }
653:
654:            /**
655:             * Compute fields for the Chinese calendar system.  This method can
656:             * either set all relevant fields, as required by
657:             * <code>handleComputeFields()</code>, or it can just set the MONTH and
658:             * IS_LEAP_MONTH fields, as required by
659:             * <code>handleComputeMonthStart()</code>.
660:             *
661:             * <p>As a side effect, this method sets {@link #isLeapYear}.
662:             * @param days days after January 1, 1970 0:00 Asia/Shanghai of the
663:             * date to compute fields for
664:             * @param gyear the Gregorian year of the given date
665:             * @param gmonth the Gregorian month of the given date
666:             * @param setAllFields if true, set the EXTENDED_YEAR, ERA, YEAR,
667:             * DAY_OF_MONTH, and DAY_OF_YEAR fields.  In either case set the MONTH
668:             * and IS_LEAP_MONTH fields.
669:             */
670:            private void computeChineseFields(int days, int gyear, int gmonth,
671:                    boolean setAllFields) {
672:
673:                // Find the winter solstices before and after the target date.
674:                // These define the boundaries of this Chinese year, specifically,
675:                // the position of month 11, which always contains the solstice.
676:                // We want solsticeBefore <= date < solsticeAfter.
677:                int solsticeBefore;
678:                int solsticeAfter = winterSolstice(gyear);
679:                if (days < solsticeAfter) {
680:                    solsticeBefore = winterSolstice(gyear - 1);
681:                } else {
682:                    solsticeBefore = solsticeAfter;
683:                    solsticeAfter = winterSolstice(gyear + 1);
684:                }
685:
686:                // Find the start of the month after month 11.  This will be either
687:                // the prior month 12 or leap month 11 (very rare).  Also find the
688:                // start of the following month 11.
689:                int firstMoon = newMoonNear(solsticeBefore + 1, true);
690:                int lastMoon = newMoonNear(solsticeAfter + 1, false);
691:                int this Moon = newMoonNear(days + 1, false); // Start of this month
692:                // Note: isLeapYear is a member variable
693:                isLeapYear = synodicMonthsBetween(firstMoon, lastMoon) == 12;
694:
695:                int month = synodicMonthsBetween(firstMoon, this Moon);
696:                if (isLeapYear && isLeapMonthBetween(firstMoon, this Moon)) {
697:                    month--;
698:                }
699:                if (month < 1) {
700:                    month += 12;
701:                }
702:
703:                boolean isLeapMonth = isLeapYear
704:                        && hasNoMajorSolarTerm(this Moon)
705:                        && !isLeapMonthBetween(firstMoon, newMoonNear(this Moon
706:                                - SYNODIC_GAP, false));
707:
708:                internalSet(MONTH, month - 1); // Convert from 1-based to 0-based
709:                internalSet(IS_LEAP_MONTH, isLeapMonth ? 1 : 0);
710:
711:                if (setAllFields) {
712:
713:                    int year = gyear - CHINESE_EPOCH_YEAR;
714:                    if (month < 11 || gmonth >= JULY) {
715:                        year++;
716:                    }
717:                    int dayOfMonth = days - this Moon + 1;
718:
719:                    internalSet(EXTENDED_YEAR, year);
720:
721:                    // 0->0,60  1->1,1  60->1,60  61->2,1  etc.
722:                    int[] yearOfCycle = new int[1];
723:                    int cycle = floorDivide(year - 1, 60, yearOfCycle);
724:                    internalSet(ERA, cycle + 1);
725:                    internalSet(YEAR, yearOfCycle[0] + 1);
726:
727:                    internalSet(DAY_OF_MONTH, dayOfMonth);
728:
729:                    // Days will be before the first new year we compute if this
730:                    // date is in month 11, leap 11, 12.  There is never a leap 12.
731:                    // New year computations are cached so this should be cheap in
732:                    // the long run.
733:                    int newYear = newYear(gyear);
734:                    if (days < newYear) {
735:                        newYear = newYear(gyear - 1);
736:                    }
737:                    internalSet(DAY_OF_YEAR, days - newYear + 1);
738:                }
739:            }
740:
741:            //------------------------------------------------------------------
742:            // Fields to time
743:            //------------------------------------------------------------------
744:
745:            /**
746:             * Return the Chinese new year of the given Gregorian year.
747:             * @param gyear a Gregorian year
748:             * @return days after January 1, 1970 0:00 Asia/Shanghai of the
749:             * Chinese new year of the given year (this will be a new moon)
750:             */
751:            private int newYear(int gyear) {
752:
753:                long cacheValue = newYearCache.get(gyear);
754:
755:                if (cacheValue == CalendarCache.EMPTY) {
756:
757:                    int solsticeBefore = winterSolstice(gyear - 1);
758:                    int solsticeAfter = winterSolstice(gyear);
759:                    int newMoon1 = newMoonNear(solsticeBefore + 1, true);
760:                    int newMoon2 = newMoonNear(newMoon1 + SYNODIC_GAP, true);
761:                    int newMoon11 = newMoonNear(solsticeAfter + 1, false);
762:
763:                    if (synodicMonthsBetween(newMoon1, newMoon11) == 12
764:                            && (hasNoMajorSolarTerm(newMoon1) || hasNoMajorSolarTerm(newMoon2))) {
765:                        cacheValue = newMoonNear(newMoon2 + SYNODIC_GAP, true);
766:                    } else {
767:                        cacheValue = newMoon2;
768:                    }
769:
770:                    newYearCache.put(gyear, cacheValue);
771:                }
772:                return (int) cacheValue;
773:            }
774:
775:            /**
776:             * Return the Julian day number of day before the first day of the
777:             * given month in the given extended year.
778:             * 
779:             * <p>Note: This method reads the IS_LEAP_MONTH field to determine
780:             * whether the given month is a leap month.
781:             * @param eyear the extended year
782:             * @param month the zero-based month.  The month is also determined
783:             * by reading the IS_LEAP_MONTH field.
784:             * @return the Julian day number of the day before the first
785:             * day of the given month and year
786:             * @stable ICU 2.8
787:             */
788:            protected int handleComputeMonthStart(int eyear, int month,
789:                    boolean useMonth) {
790:
791:                // If the month is out of range, adjust it into range, and
792:                // modify the extended year value accordingly.
793:                if (month < 0 || month > 11) {
794:                    int[] rem = new int[1];
795:                    eyear += floorDivide(month, 12, rem);
796:                    month = rem[0];
797:                }
798:
799:                int gyear = eyear + CHINESE_EPOCH_YEAR - 1; // Gregorian year
800:                int newYear = newYear(gyear);
801:                int newMoon = newMoonNear(newYear + month * 29, true);
802:
803:                int julianDay = newMoon + EPOCH_JULIAN_DAY;
804:
805:                // Save fields for later restoration
806:                int saveMonth = internalGet(MONTH);
807:                int saveIsLeapMonth = internalGet(IS_LEAP_MONTH);
808:
809:                // Ignore IS_LEAP_MONTH field if useMonth is false
810:                int isLeapMonth = useMonth ? saveIsLeapMonth : 0;
811:
812:                computeGregorianFields(julianDay);
813:
814:                // This will modify the MONTH and IS_LEAP_MONTH fields (only)
815:                computeChineseFields(newMoon, getGregorianYear(),
816:                        getGregorianMonth(), false);
817:
818:                if (month != internalGet(MONTH)
819:                        || isLeapMonth != internalGet(IS_LEAP_MONTH)) {
820:                    newMoon = newMoonNear(newMoon + SYNODIC_GAP, true);
821:                    julianDay = newMoon + EPOCH_JULIAN_DAY;
822:                }
823:
824:                internalSet(MONTH, saveMonth);
825:                internalSet(IS_LEAP_MONTH, saveIsLeapMonth);
826:
827:                return julianDay - 1;
828:            }
829:
830:            /**
831:             * Return the current Calendar type.
832:             * @return type of calendar (gregorian, etc.)
833:             * @internal ICU 3.0
834:             * @deprecated This API is ICU internal only.
835:             */
836:            public String getType() {
837:                return "chinese";
838:            }
839:
840:            /**
841:             * Override readObject.
842:             */
843:            private void readObject(ObjectInputStream stream)
844:                    throws IOException, ClassNotFoundException {
845:                stream.defaultReadObject();
846:
847:                /* set up the transient caches... */
848:                astro = new CalendarAstronomer();
849:                winterSolsticeCache = new CalendarCache();
850:                newYearCache = new CalendarCache();
851:            }
852:
853:            /*
854:            private static CalendarFactory factory;
855:            public static CalendarFactory factory() {
856:                if (factory == null) {
857:                    factory = new CalendarFactory() {
858:                        public Calendar create(TimeZone tz, ULocale loc) {
859:                            return new ChineseCalendar(tz, loc);
860:                        }
861:
862:                        public String factoryName() {
863:                            return "Chinese";
864:                        }
865:                    };
866:                }
867:                return factory;
868:            }
869:             */
870:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.