]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_8_1_1/main/classes/core/src/com/ibm/icu/util/ChineseCalendar.java
Added flags.
[Dictionary.git] / jars / icu4j-4_8_1_1 / main / classes / core / src / com / ibm / icu / util / ChineseCalendar.java
1 /*********************************************************************
2  * Copyright (C) 2000-2011, International Business Machines
3  * Corporation and others. All Rights Reserved.
4  *********************************************************************
5  */
6
7 package com.ibm.icu.util;
8
9 import java.io.IOException;
10 import java.io.ObjectInputStream;
11 import java.util.Date;
12 import java.util.Locale;
13
14 import com.ibm.icu.impl.CalendarAstronomer;
15 import com.ibm.icu.impl.CalendarCache;
16 import com.ibm.icu.text.ChineseDateFormat;
17 import com.ibm.icu.text.DateFormat;
18 import com.ibm.icu.util.ULocale.Category;
19
20 /**
21  * <code>ChineseCalendar</code> is a concrete subclass of {@link Calendar}
22  * that implements a traditional Chinese calendar.  The traditional Chinese
23  * calendar is a lunisolar calendar: Each month starts on a new moon, and
24  * the months are numbered according to solar events, specifically, to
25  * guarantee that month 11 always contains the winter solstice.  In order
26  * to accomplish this, leap months are inserted in certain years.  Leap
27  * months are numbered the same as the month they follow.  The decision of
28  * which month is a leap month depends on the relative movements of the sun
29  * and moon.
30  *
31  * <p>All astronomical computations are performed with respect to a time
32  * zone of GMT+8:00 and a longitude of 120 degrees east.  Although some
33  * calendars implement a historically more accurate convention of using
34  * Beijing's local longitude (116 degrees 25 minutes east) and time zone
35  * (GMT+7:45:40) for dates before 1929, we do not implement this here.
36  *
37  * <p>Years are counted in two different ways in the Chinese calendar.  The
38  * first method is by sequential numbering from the 61st year of the reign
39  * of Huang Di, 2637 BCE, which is designated year 1 on the Chinese
40  * calendar.  The second method uses 60-year cycles from the same starting
41  * point, which is designated year 1 of cycle 1.  In this class, the
42  * <code>EXTENDED_YEAR</code> field contains the sequential year count.
43  * The <code>ERA</code> field contains the cycle number, and the
44  * <code>YEAR</code> field contains the year of the cycle, a value between
45  * 1 and 60.
46  *
47  * <p>There is some variation in what is considered the starting point of
48  * the calendar, with some sources starting in the first year of the reign
49  * of Huang Di, rather than the 61st.  This gives continuous year numbers
50  * 60 years greater and cycle numbers one greater than what this class
51  * implements.
52  *
53  * <p>Because <code>ChineseCalendar</code> defines an additional field and
54  * redefines the way the <code>ERA</code> field is used, it requires a new
55  * format class, <code>ChineseDateFormat</code>.  As always, use the
56  * methods <code>DateFormat.getXxxInstance(Calendar cal,...)</code> to
57  * obtain a formatter for this calendar.
58  *
59  * <p>References:<ul>
60  * 
61  * <li>Dershowitz and Reingold, <i>Calendrical Calculations</i>,
62  * Cambridge University Press, 1997</li>
63  * 
64  * <li>Helmer Aslaksen's
65  * <a href="http://www.math.nus.edu.sg/aslaksen/calendar/chinese.shtml">
66  * Chinese Calendar page</a></li>
67  *
68  * <li>The <a href="http://www.tondering.dk/claus/calendar.html">
69  * Calendar FAQ</a></li>
70  *
71  * </ul>
72  *
73  * <p>
74  * This class should not be subclassed.</p>
75  * <p>
76  * ChineseCalendar usually should be instantiated using 
77  * {@link com.ibm.icu.util.Calendar#getInstance(ULocale)} passing in a <code>ULocale</code>
78  * with the tag <code>"@calendar=chinese"</code>.</p>
79  *
80  * @see com.ibm.icu.text.ChineseDateFormat
81  * @see com.ibm.icu.util.Calendar
82  * @author Alan Liu
83  * @stable ICU 2.8
84  */
85 public class ChineseCalendar extends Calendar {
86     // jdk1.4.2 serialver
87     private static final long serialVersionUID = 7312110751940929420L;
88
89     //------------------------------------------------------------------
90     // Developer Notes
91     // 
92     // Time is represented as a scalar in two ways in this class.  One is
93     // the usual UTC epoch millis, that is, milliseconds after January 1,
94     // 1970 Gregorian, 0:00:00.000 UTC.  The other is in terms of 'local
95     // days.'  This is the number of days after January 1, 1970 Gregorian,
96     // local to Beijing, China (since all computations of the Chinese
97     // calendar are done in Beijing).  That is, 0 represents January 1,
98     // 1970 0:00 Asia/Shanghai.  Conversion of local days to and from
99     // 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 <code>ChineseCalendar</code> 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 <code>ChineseCalendar</code> with the give date set in the default time zone
146      * with the default locale.
147      * @param date The date to which the new calendar is set.
148      * @stable ICU 4.0
149      */
150     public ChineseCalendar(Date date) {
151         super();
152         setTime(date);
153     }
154
155     /**
156      * Constructs a <code>ChineseCalendar</code> with the given date set
157      * in the default time zone with the default <code>FORMAT</code> locale.
158      *
159      * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field.
160      * @param month     The value used to set the calendar's {@link #MONTH MONTH} time field.
161      *                  The value is 0-based. e.g., 0 for January.
162      * @param isLeapMonth The value used to set the Chinese calendar's (@link #IS_LEAP_MONTH)
163      *                  time field.
164      * @param date      The value used to set the calendar's {@link #DATE DATE} time field.
165      * @see Category#FORMAT
166      * @stable ICU 4.0
167      */
168     public ChineseCalendar(int year, int month, int isLeapMonth, int date) {
169         super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
170
171         // We need to set the current time once to initialize the ChineseCalendar's
172         // ERA field to be the current era.
173         setTimeInMillis(System.currentTimeMillis());
174         // Then we need to clean up time fields
175         this.set(MILLISECONDS_IN_DAY, 0);
176
177         // Then set the given field values.
178         this.set(YEAR, year);
179         this.set(MONTH, month);
180         this.set(IS_LEAP_MONTH, isLeapMonth);
181         this.set(DATE, date);
182     }
183
184     /**
185      * Constructs a <code>ChineseCalendar</code> with the given date
186      * and time set for the default time zone with the default <code>FORMAT</code> locale.
187      *
188      * @param year  the value used to set the {@link #YEAR YEAR} time field in the calendar.
189      * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar.
190      *              Note that the month value is 0-based. e.g., 0 for January.
191      * @param isLeapMonth the value used to set the {@link #IS_LEAP_MONTH} time field
192      *              in the calendar.
193      * @param date  the value used to set the {@link #DATE DATE} time field in the calendar.
194      * @param hour  the value used to set the {@link #HOUR_OF_DAY HOUR_OF_DAY} time field
195      *              in the calendar.
196      * @param minute the value used to set the {@link #MINUTE MINUTE} time field
197      *              in the calendar.
198      * @param second the value used to set the {@link #SECOND SECOND} time field
199      *              in the calendar.
200      * @see Category#FORMAT
201      * @stable ICU 4.0
202      */
203     public ChineseCalendar(int year, int month, int isLeapMonth, int date, int hour,
204                              int minute, int second)
205     {
206         super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
207
208         // We need to set the current time once to initialize the ChineseCalendar's
209         // ERA field to be the current era.
210         setTimeInMillis(System.currentTimeMillis());
211         // Then set 0 to millisecond field
212         this.set(MILLISECOND, 0);
213
214         // Then, set the given field values.
215         this.set(YEAR, year);
216         this.set(MONTH, month);
217         this.set(IS_LEAP_MONTH, isLeapMonth);
218         this.set(DATE, date);
219         this.set(HOUR_OF_DAY, hour);
220         this.set(MINUTE, minute);
221         this.set(SECOND, second);
222     }
223
224     /** 
225      * Constructs a <code>ChineseCalendar</code> with the given date set 
226      * in the default time zone with the default <code>FORMAT</code> locale. 
227      * 
228      * @param era       The value used to set the calendar's {@link #ERA ERA} time field. 
229      * @param year      The value used to set the calendar's {@link #YEAR YEAR} time field. 
230      * @param month     The value used to set the calendar's {@link #MONTH MONTH} time field. 
231      *                  The value is 0-based. e.g., 0 for January. 
232      * @param isLeapMonth The value used to set the Chinese calendar's (@link #IS_LEAP_MONTH) 
233      *                  time field. 
234      * @param date      The value used to set the calendar's {@link #DATE DATE} time field.
235      * @see Category#FORMAT
236      * @draft ICU 4.6 
237      * @provisional This API might change or be removed in a future release.
238      */ 
239     public ChineseCalendar(int era, int year, int month, int isLeapMonth, int date) 
240     { 
241         super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT)); 
242
243         // We need to set the current time once to initialize the ChineseCalendar's 
244         // ERA field to be the current era. 
245         setTimeInMillis(System.currentTimeMillis()); 
246
247         // Then we need to clean up time fields 
248         this.set(MILLISECONDS_IN_DAY, 0); 
249
250         // Then set the given field values. 
251         this.set(ERA, era); 
252         this.set(YEAR, year); 
253         this.set(MONTH, month); 
254         this.set(IS_LEAP_MONTH, isLeapMonth); 
255         this.set(DATE, date); 
256     } 
257   
258     /** 
259      * Constructs a <code>ChineseCalendar</code> with the given date 
260      * and time set for the default time zone with the default <code>FORMAT</code> locale. 
261      * 
262      * @param era   the value used to set the calendar's {@link #ERA ERA} time field. 
263      * @param year  the value used to set the {@link #YEAR YEAR} time field in the calendar. 
264      * @param month the value used to set the {@link #MONTH MONTH} time field in the calendar. 
265      *              Note that the month value is 0-based. e.g., 0 for January. 
266      * @param isLeapMonth the value used to set the {@link #IS_LEAP_MONTH} time field 
267      *              in the calendar. 
268      * @param date  the value used to set the {@link #DATE DATE} time field in the calendar. 
269      * @param hour  the value used to set the {@link #HOUR_OF_DAY HOUR_OF_DAY} time field 
270      *              in the calendar. 
271      * @param minute the value used to set the {@link #MINUTE MINUTE} time field 
272      *              in the calendar. 
273      * @param second the value used to set the {@link #SECOND SECOND} time field 
274      *              in the calendar.
275      * @see Category#FORMAT
276      * @draft ICU 4.6
277      * @provisional This API might change or be removed in a future release.
278      */ 
279     public ChineseCalendar(int era, int year, int month, int isLeapMonth, int date, int hour, 
280                            int minute, int second) 
281     { 
282         super(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT)); 
283
284         // We need to set the current time once to initialize the ChineseCalendar's 
285         // ERA field to be the current era. 
286         setTimeInMillis(System.currentTimeMillis()); 
287
288         // Then set 0 to millisecond field 
289         this.set(MILLISECOND, 0); 
290
291         // Then, set the given field values. 
292         this.set(ERA, era); 
293         this.set(YEAR, year); 
294         this.set(MONTH, month); 
295         this.set(IS_LEAP_MONTH, isLeapMonth); 
296         this.set(DATE, date); 
297         this.set(HOUR_OF_DAY, hour); 
298         this.set(MINUTE, minute); 
299         this.set(SECOND, second); 
300     }     
301     
302     /**
303      * Constructs a <code>ChineseCalendar</code> based on the current time
304      * in the default time zone with the given locale.
305      * @param aLocale The given locale
306      * @stable ICU 4.0
307      */
308     public ChineseCalendar(Locale aLocale) {
309         this(TimeZone.getDefault(), aLocale);
310         setTimeInMillis(System.currentTimeMillis());
311     }
312
313     /**
314      * Construct a <code>ChineseCalendar</code> based on the current time
315      * in the given time zone with the default <code>FORMAT</code> locale.
316      * @param zone the given time zone
317      * @see Category#FORMAT
318      * @stable ICU 4.0
319      */
320     public ChineseCalendar(TimeZone zone) {
321         super(zone, ULocale.getDefault(Category.FORMAT));
322         setTimeInMillis(System.currentTimeMillis());
323     }
324
325     /**
326      * Construct a <code>ChineseCalendar</code> based on the current time
327      * in the given time zone with the given locale.
328      * @param zone the given time zone
329      * @param aLocale the given locale
330      * @stable ICU 2.8
331      */
332     public ChineseCalendar(TimeZone zone, Locale aLocale) {
333         super(zone, aLocale);
334         setTimeInMillis(System.currentTimeMillis());
335     }
336
337     /**
338      * Constructs a <code>ChineseCalendar</code> based on the current time
339      * in the default time zone with the given locale.
340      *
341      * @param locale the given ulocale
342      * @stable ICU 4.0
343      */
344     public ChineseCalendar(ULocale locale) {
345         this(TimeZone.getDefault(), locale);
346         setTimeInMillis(System.currentTimeMillis());
347     }
348
349     /**
350      * Construct a <code>ChineseCalendar</code>  based on the current time
351      * with the given time zone with the given locale.
352      * @param zone the given time zone
353      * @param locale the given ulocale
354      * @stable ICU 3.2
355      */
356     public ChineseCalendar(TimeZone zone, ULocale locale) {
357         super(zone, locale);
358         setTimeInMillis(System.currentTimeMillis());
359     }
360
361     //------------------------------------------------------------------
362     // Public constants
363     //------------------------------------------------------------------
364
365     /**
366      * Field indicating whether or not the current month is a leap month.
367      * Should have a value of 0 for non-leap months, and 1 for leap months.
368      * @stable ICU 2.8
369      */
370     // public static int IS_LEAP_MONTH = BASE_FIELD_COUNT;
371
372
373     //------------------------------------------------------------------
374     // Calendar framework
375     //------------------------------------------------------------------
376
377     /**
378      * Array defining the limits of field values for this class.  Field
379      * limits which are invariant with respect to calendar system and
380      * defined by Calendar are left blank.
381      *
382      * Notes:
383      *
384      * ERA 5000000 / 60 = 83333.
385      *
386      * MONTH There are 12 or 13 lunar months in a year.  However, we always
387      * number them 0..11, with an intercalated, identically numbered leap
388      * month, when necessary.
389      *
390      * DAY_OF_YEAR In a non-leap year there are 353, 354, or 355 days.  In
391      * a leap year there are 383, 384, or 385 days.
392      *
393      * WEEK_OF_YEAR The least maximum occurs if there are 353 days in the
394      * year, and the first 6 are the last week of the previous year.  Then
395      * we have 49 full weeks and 4 days in the last week: 6 + 49*7 + 4 =
396      * 353.  So the least maximum is 50.  The maximum occurs if there are
397      * 385 days in the year, and WOY 1 extends 6 days into the prior year.
398      * Then there are 54 full weeks, and 6 days in the last week: 1 + 54*7
399      * + 6 = 385.  The 6 days of the last week will fall into WOY 1 of the
400      * next year.  Maximum is 55.
401      *
402      * WEEK_OF_MONTH In a 29 day month, if the first 7 days make up week 1
403      * that leaves 3 full weeks and 1 day at the end.  The least maximum is
404      * thus 5.  In a 30 days month, if the previous 6 days belong WOM 1 of
405      * this month, we have 4 full weeks and 1 days at the end (which
406      * technically will be WOM 1 of the next month, but will be reported by
407      * time->fields and hence by getActualMaximum as WOM 6 of this month).
408      * Maximum is 6.
409      *
410      * DAY_OF_WEEK_IN_MONTH In a 29 or 30 day month, there are 4 full weeks
411      * plus 1 or 2 days at the end, so the maximum is always 5.
412      */
413     private static final int LIMITS[][] = {
414         // Minimum  Greatest    Least  Maximum
415         //           Minimum  Maximum
416         {        1,        1,   83333,   83333 }, // ERA
417         {        1,        1,      60,      60 }, // YEAR
418         {        0,        0,      11,      11 }, // MONTH
419         {        1,        1,      50,      55 }, // WEEK_OF_YEAR
420         {/*                                  */}, // WEEK_OF_MONTH
421         {        1,        1,      29,      30 }, // DAY_OF_MONTH
422         {        1,        1,     353,     385 }, // DAY_OF_YEAR
423         {/*                                  */}, // DAY_OF_WEEK
424         {       -1,       -1,       5,       5 }, // DAY_OF_WEEK_IN_MONTH
425         {/*                                  */}, // AM_PM
426         {/*                                  */}, // HOUR
427         {/*                                  */}, // HOUR_OF_DAY
428         {/*                                  */}, // MINUTE
429         {/*                                  */}, // SECOND
430         {/*                                  */}, // MILLISECOND
431         {/*                                  */}, // ZONE_OFFSET
432         {/*                                  */}, // DST_OFFSET
433         { -5000000, -5000000, 5000000, 5000000 }, // YEAR_WOY
434         {/*                                  */}, // DOW_LOCAL
435         { -5000000, -5000000, 5000000, 5000000 }, // EXTENDED_YEAR
436         {/*                                  */}, // JULIAN_DAY
437         {/*                                  */}, // MILLISECONDS_IN_DAY
438         {        0,        0,       1,       1 }, // IS_LEAP_MONTH
439     };
440
441     /**
442      * Override Calendar to return the limit value for the given field.
443      * @stable ICU 2.8
444      */
445     protected int handleGetLimit(int field, int limitType) {
446         return LIMITS[field][limitType];
447     }
448
449     /**
450      * Implement abstract Calendar method to return the extended year
451      * defined by the current fields.  This will use either the ERA and
452      * YEAR field as the cycle and year-of-cycle, or the EXTENDED_YEAR
453      * field as the continuous year count, depending on which is newer.
454      * @stable ICU 2.8
455      */
456     protected int handleGetExtendedYear() {
457         int year;
458         if (newestStamp(ERA, YEAR, UNSET) <= getStamp(EXTENDED_YEAR)) {
459             year = internalGet(EXTENDED_YEAR, 1); // Default to year 1
460         } else {
461             int cycle = internalGet(ERA, 1) - 1; // 0-based cycle
462             year = cycle * 60 + internalGet(YEAR, 1);
463         }
464         return year;
465     }
466
467     /**
468      * Override Calendar method to return the number of days in the given
469      * extended year and month.
470      *
471      * <p>Note: This method also reads the IS_LEAP_MONTH field to determine
472      * whether or not the given month is a leap month.
473      * @stable ICU 2.8
474      */
475     protected int handleGetMonthLength(int extendedYear, int month) {
476         int thisStart = handleComputeMonthStart(extendedYear, month, true) -
477             EPOCH_JULIAN_DAY + 1; // Julian day -> local days
478         int nextStart = newMoonNear(thisStart + SYNODIC_GAP, true);
479         return nextStart - thisStart;
480     }
481
482     /**
483      * Framework method to create a calendar-specific DateFormat object
484      * using the the given pattern.  This method is responsible for
485      * creating the calendar- specific DateFormat and DateFormatSymbols
486      * objects as needed.
487      * @param pattern The date formatting pattern
488      * @param override The override string.  A numbering system override string can take one of the following forms:
489      *     1). If just a numbering system name is specified, it applies to all numeric fields in the date format pattern.
490      *     2). To specify an alternate numbering system on a field by field basis, use the field letters from the pattern
491      *         followed by an = sign, followed by the numbering system name.  For example, to specify that just the year
492      *         be formatted using Hebrew digits, use the override "y=hebr".  Multiple overrides can be specified in a single
493      *         string by separating them with a semi-colon. For example, the override string "m=thai;y=deva" would format using
494      *         Thai digits for the month and Devanagari digits for the year.
495      * @param locale The locale
496      * @stable ICU 4.2
497      */
498     protected DateFormat handleGetDateFormat(String pattern, String override, ULocale locale) {
499         return new ChineseDateFormat(pattern, override, locale);
500     }
501
502     /**
503      * Field resolution table that incorporates IS_LEAP_MONTH.
504      */
505     static final int[][][] CHINESE_DATE_PRECEDENCE = {
506         {
507             { DAY_OF_MONTH },
508             { WEEK_OF_YEAR, DAY_OF_WEEK },
509             { WEEK_OF_MONTH, DAY_OF_WEEK },
510             { DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK },
511             { WEEK_OF_YEAR, DOW_LOCAL },
512             { WEEK_OF_MONTH, DOW_LOCAL },
513             { DAY_OF_WEEK_IN_MONTH, DOW_LOCAL },
514             { DAY_OF_YEAR },
515             { RESOLVE_REMAP | DAY_OF_MONTH, IS_LEAP_MONTH },
516         },
517         {
518             { WEEK_OF_YEAR },
519             { WEEK_OF_MONTH },
520             { DAY_OF_WEEK_IN_MONTH },
521             { RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK },
522             { RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DOW_LOCAL },
523         },
524     };
525
526     /**
527      * Override Calendar to add IS_LEAP_MONTH to the field resolution
528      * table.
529      * @stable ICU 2.8
530      */
531     protected int[][][] getFieldResolutionTable() {
532         return CHINESE_DATE_PRECEDENCE;
533     }
534
535     /**
536      * Adjust this calendar to be delta months before or after a given
537      * start position, pinning the day of month if necessary.  The start
538      * position is given as a local days number for the start of the month
539      * and a day-of-month.  Used by add() and roll().
540      * @param newMoon the local days of the first day of the month of the
541      * start position (days after January 1, 1970 0:00 Asia/Shanghai)
542      * @param dom the 1-based day-of-month of the start position
543      * @param delta the number of months to move forward or backward from
544      * the start position
545      */
546     private void offsetMonth(int newMoon, int dom, int delta) {
547         // Move to the middle of the month before our target month.
548         newMoon += (int) (CalendarAstronomer.SYNODIC_MONTH * (delta - 0.5));
549
550         // Search forward to the target month's new moon
551         newMoon = newMoonNear(newMoon, true);
552
553         // Find the target dom
554         int jd = newMoon + EPOCH_JULIAN_DAY - 1 + dom;
555
556         // Pin the dom.  In this calendar all months are 29 or 30 days
557         // so pinning just means handling dom 30.
558         if (dom > 29) {
559             set(JULIAN_DAY, jd-1);
560             // TODO Fix this.  We really shouldn't ever have to
561             // explicitly call complete().  This is either a bug in
562             // this method, in ChineseCalendar, or in
563             // Calendar.getActualMaximum().  I suspect the last.
564             complete();
565             if (getActualMaximum(DAY_OF_MONTH) >= dom) {
566                 set(JULIAN_DAY, jd);
567             }
568         } else {
569             set(JULIAN_DAY, jd);
570         }
571     }
572
573     /**
574      * Override Calendar to handle leap months properly.
575      * @stable ICU 2.8
576      */
577     public void add(int field, int amount) {
578         switch (field) {
579         case MONTH:
580             if (amount != 0) {
581                 int dom = get(DAY_OF_MONTH);
582                 int day = get(JULIAN_DAY) - EPOCH_JULIAN_DAY; // Get local day
583                 int moon = day - dom + 1; // New moon 
584                 offsetMonth(moon, dom, amount);
585             }
586             break;
587         default:
588             super.add(field, amount);
589             break;
590         }
591     }
592
593     /**
594      * Override Calendar to handle leap months properly.
595      * @stable ICU 2.8
596      */
597     public void roll(int field, int amount) {
598         switch (field) {
599         case MONTH:
600             if (amount != 0) {
601                 int dom = get(DAY_OF_MONTH);
602                 int day = get(JULIAN_DAY) - EPOCH_JULIAN_DAY; // Get local day
603                 int moon = day - dom + 1; // New moon (start of this month)
604
605                 // Note throughout the following:  Months 12 and 1 are never
606                 // followed by a leap month (D&R p. 185).
607
608                 // Compute the adjusted month number m.  This is zero-based
609                 // value from 0..11 in a non-leap year, and from 0..12 in a
610                 // leap year.
611                 int m = get(MONTH); // 0-based month
612                 if (isLeapYear) { // (member variable)
613                     if (get(IS_LEAP_MONTH) == 1) {
614                         ++m;
615                     } else {
616                         // Check for a prior leap month.  (In the
617                         // following, month 0 is the first month of the
618                         // year.)  Month 0 is never followed by a leap
619                         // month, and we know month m is not a leap month.
620                         // moon1 will be the start of month 0 if there is
621                         // no leap month between month 0 and month m;
622                         // otherwise it will be the start of month 1.
623                         int moon1 = moon -
624                             (int) (CalendarAstronomer.SYNODIC_MONTH * (m - 0.5));
625                         moon1 = newMoonNear(moon1, true);
626                         if (isLeapMonthBetween(moon1, moon)) {
627                             ++m;
628                         }
629                     }
630                 }
631
632                 // Now do the standard roll computation on m, with the
633                 // allowed range of 0..n-1, where n is 12 or 13.
634                 int n = isLeapYear ? 13 : 12; // Months in this year
635                 int newM = (m + amount) % n;
636                 if (newM < 0) {
637                     newM += n;
638                 }
639
640                 if (newM != m) {
641                     offsetMonth(moon, dom, newM - m);
642                 }
643             }
644             break;
645         default:
646             super.roll(field, amount);
647             break;
648         }
649     }
650
651     //------------------------------------------------------------------
652     // Support methods and constants
653     //------------------------------------------------------------------
654    
655     /**
656      * The start year of the Chinese calendar, the 61st year of the reign
657      * of Huang Di.  Some sources use the first year of his reign,
658      * resulting in EXTENDED_YEAR values 60 years greater and ERA (cycle)
659      * values one greater.
660      */
661     private static final int CHINESE_EPOCH_YEAR = -2636; // Gregorian year
662
663     /**
664      * The offset from GMT in milliseconds at which we perform astronomical
665      * computations.  Some sources use a different historically accurate
666      * offset of GMT+7:45:40 for years before 1929; we do not do this.
667      */
668     private static final long CHINA_OFFSET = 8*ONE_HOUR;
669
670     /**
671      * Value to be added or subtracted from the local days of a new moon to
672      * get close to the next or prior new moon, but not cross it.  Must be
673      * >= 1 and < CalendarAstronomer.SYNODIC_MONTH.
674      */
675     private static final int SYNODIC_GAP = 25;
676
677     /**
678      * Convert local days to UTC epoch milliseconds.
679      * @param days days after January 1, 1970 0:00 Asia/Shanghai
680      * @return milliseconds after January 1, 1970 0:00 GMT
681      */
682     private static final long daysToMillis(int days) {
683         return (days * ONE_DAY) - CHINA_OFFSET;
684     }
685
686     /**
687      * Convert UTC epoch milliseconds to local days.
688      * @param millis milliseconds after January 1, 1970 0:00 GMT
689      * @return days after January 1, 1970 0:00 Asia/Shanghai
690      */
691     private static final int millisToDays(long millis) {
692         return (int) floorDivide(millis + CHINA_OFFSET, ONE_DAY);
693     }
694
695     //------------------------------------------------------------------
696     // Astronomical computations
697     //------------------------------------------------------------------
698     
699     /**
700      * Return the major solar term on or after December 15 of the given
701      * Gregorian year, that is, the winter solstice of the given year.
702      * Computations are relative to Asia/Shanghai time zone.
703      * @param gyear a Gregorian year
704      * @return days after January 1, 1970 0:00 Asia/Shanghai of the
705      * winter solstice of the given year
706      */
707     private int winterSolstice(int gyear) {
708
709         long cacheValue = winterSolsticeCache.get(gyear);
710
711         if (cacheValue == CalendarCache.EMPTY) {
712             // In books December 15 is used, but it fails for some years
713             // using our algorithms, e.g.: 1298 1391 1492 1553 1560.  That
714             // is, winterSolstice(1298) starts search at Dec 14 08:00:00
715             // PST 1298 with a final result of Dec 14 10:31:59 PST 1299.
716             long ms = daysToMillis(computeGregorianMonthStart(gyear, DECEMBER) +
717                                    1 - EPOCH_JULIAN_DAY);
718             astro.setTime(ms);
719             
720             // Winter solstice is 270 degrees solar longitude aka Dongzhi
721             long solarLong = astro.getSunTime(CalendarAstronomer.WINTER_SOLSTICE,
722                                               true);
723             cacheValue = millisToDays(solarLong);
724             winterSolsticeCache.put(gyear, cacheValue);
725         }
726         return (int) cacheValue;
727     }
728
729     /**
730      * Return the closest new moon to the given date, searching either
731      * forward or backward in time.
732      * @param days days after January 1, 1970 0:00 Asia/Shanghai
733      * @param after if true, search for a new moon on or after the given
734      * date; otherwise, search for a new moon before it
735      * @return days after January 1, 1970 0:00 Asia/Shanghai of the nearest
736      * new moon after or before <code>days</code>
737      */
738     private int newMoonNear(int days, boolean after) {
739         
740         astro.setTime(daysToMillis(days));
741         long newMoon = astro.getMoonTime(CalendarAstronomer.NEW_MOON, after);
742         
743         return millisToDays(newMoon);
744     }
745
746     /**
747      * Return the nearest integer number of synodic months between
748      * two dates.
749      * @param day1 days after January 1, 1970 0:00 Asia/Shanghai
750      * @param day2 days after January 1, 1970 0:00 Asia/Shanghai
751      * @return the nearest integer number of months between day1 and day2
752      */
753     private int synodicMonthsBetween(int day1, int day2) {
754         return (int) Math.round((day2 - day1) / CalendarAstronomer.SYNODIC_MONTH);
755     }
756
757     /**
758      * Return the major solar term on or before a given date.  This
759      * will be an integer from 1..12, with 1 corresponding to 330 degrees,
760      * 2 to 0 degrees, 3 to 30 degrees,..., and 12 to 300 degrees.
761      * @param days days after January 1, 1970 0:00 Asia/Shanghai
762      */
763     private int majorSolarTerm(int days) {
764         
765         astro.setTime(daysToMillis(days));
766
767         // Compute (floor(solarLongitude / (pi/6)) + 2) % 12
768         int term = ((int) Math.floor(6 * astro.getSunLongitude() / Math.PI) + 2) % 12;
769         if (term < 1) {
770             term += 12;
771         }
772         return term;
773     }
774
775     /**
776      * Return true if the given month lacks a major solar term.
777      * @param newMoon days after January 1, 1970 0:00 Asia/Shanghai of a new
778      * moon
779      */
780     private boolean hasNoMajorSolarTerm(int newMoon) {
781         
782         int mst = majorSolarTerm(newMoon);
783         int nmn = newMoonNear(newMoon + SYNODIC_GAP, true);
784         int mstt = majorSolarTerm(nmn);
785         return mst == mstt;
786         /*
787         return majorSolarTerm(newMoon) ==
788             majorSolarTerm(newMoonNear(newMoon + SYNODIC_GAP, true));
789         */
790     }
791
792     //------------------------------------------------------------------
793     // Time to fields
794     //------------------------------------------------------------------
795     
796     /**
797      * Return true if there is a leap month on or after month newMoon1 and
798      * at or before month newMoon2.
799      * @param newMoon1 days after January 1, 1970 0:00 Asia/Shanghai of a
800      * new moon
801      * @param newMoon2 days after January 1, 1970 0:00 Asia/Shanghai of a
802      * new moon
803      */
804     private boolean isLeapMonthBetween(int newMoon1, int newMoon2) {
805
806         // This is only needed to debug the timeOfAngle divergence bug.
807         // Remove this later. Liu 11/9/00
808         // DEBUG
809         if (synodicMonthsBetween(newMoon1, newMoon2) >= 50) {
810             throw new IllegalArgumentException("isLeapMonthBetween(" + newMoon1 +
811                                                ", " + newMoon2 +
812                                                "): Invalid parameters");
813         }
814
815         return (newMoon2 >= newMoon1) &&
816             (isLeapMonthBetween(newMoon1, newMoonNear(newMoon2 - SYNODIC_GAP, false)) ||
817              hasNoMajorSolarTerm(newMoon2));
818     }
819
820     /**
821      * Override Calendar to compute several fields specific to the Chinese
822      * calendar system.  These are:
823      *
824      * <ul><li>ERA
825      * <li>YEAR
826      * <li>MONTH
827      * <li>DAY_OF_MONTH
828      * <li>DAY_OF_YEAR
829      * <li>EXTENDED_YEAR</ul>
830      * 
831      * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
832      * method is called.  The getGregorianXxx() methods return Gregorian
833      * calendar equivalents for the given Julian day.
834      *
835      * <p>Compute the ChineseCalendar-specific field IS_LEAP_MONTH.
836      * @stable ICU 2.8
837      */
838     protected void handleComputeFields(int julianDay) {
839
840         computeChineseFields(julianDay - EPOCH_JULIAN_DAY, // local days
841                              getGregorianYear(), getGregorianMonth(),
842                              true); // set all fields
843     }
844
845     /**
846      * Compute fields for the Chinese calendar system.  This method can
847      * either set all relevant fields, as required by
848      * <code>handleComputeFields()</code>, or it can just set the MONTH and
849      * IS_LEAP_MONTH fields, as required by
850      * <code>handleComputeMonthStart()</code>.
851      *
852      * <p>As a side effect, this method sets {@link #isLeapYear}.
853      * @param days days after January 1, 1970 0:00 Asia/Shanghai of the
854      * date to compute fields for
855      * @param gyear the Gregorian year of the given date
856      * @param gmonth the Gregorian month of the given date
857      * @param setAllFields if true, set the EXTENDED_YEAR, ERA, YEAR,
858      * DAY_OF_MONTH, and DAY_OF_YEAR fields.  In either case set the MONTH
859      * and IS_LEAP_MONTH fields.
860      */
861     private void computeChineseFields(int days, int gyear, int gmonth,
862                                       boolean setAllFields) {
863
864         // Find the winter solstices before and after the target date.
865         // These define the boundaries of this Chinese year, specifically,
866         // the position of month 11, which always contains the solstice.
867         // We want solsticeBefore <= date < solsticeAfter.
868         int solsticeBefore;
869         int solsticeAfter = winterSolstice(gyear);
870         if (days < solsticeAfter) {
871             solsticeBefore = winterSolstice(gyear - 1);
872         } else {
873             solsticeBefore = solsticeAfter;
874             solsticeAfter = winterSolstice(gyear + 1);
875         }
876
877         // Find the start of the month after month 11.  This will be either
878         // the prior month 12 or leap month 11 (very rare).  Also find the
879         // start of the following month 11.
880         int firstMoon = newMoonNear(solsticeBefore + 1, true);
881         int lastMoon = newMoonNear(solsticeAfter + 1, false);
882         int thisMoon = newMoonNear(days + 1, false); // Start of this month
883         // Note: isLeapYear is a member variable
884         isLeapYear = synodicMonthsBetween(firstMoon, lastMoon) == 12;
885
886         int month = synodicMonthsBetween(firstMoon, thisMoon);
887         if (isLeapYear && isLeapMonthBetween(firstMoon, thisMoon)) {
888             month--;
889         }
890         if (month < 1) {
891             month += 12;
892         }
893
894         boolean isLeapMonth = isLeapYear &&
895             hasNoMajorSolarTerm(thisMoon) &&
896             !isLeapMonthBetween(firstMoon, newMoonNear(thisMoon - SYNODIC_GAP, false));
897
898         internalSet(MONTH, month-1); // Convert from 1-based to 0-based
899         internalSet(IS_LEAP_MONTH, isLeapMonth?1:0);
900
901         if (setAllFields) {
902
903             int year = gyear - CHINESE_EPOCH_YEAR;
904             if (month < 11 ||
905                 gmonth >= JULY) {
906                 year++;
907             }
908             int dayOfMonth = days - thisMoon + 1;
909
910             internalSet(EXTENDED_YEAR, year);
911
912             // 0->0,60  1->1,1  60->1,60  61->2,1  etc.
913             int[] yearOfCycle = new int[1];
914             int cycle = floorDivide(year-1, 60, yearOfCycle);
915             internalSet(ERA, cycle+1);
916             internalSet(YEAR, yearOfCycle[0]+1);
917
918             internalSet(DAY_OF_MONTH, dayOfMonth);
919
920             // Days will be before the first new year we compute if this
921             // date is in month 11, leap 11, 12.  There is never a leap 12.
922             // New year computations are cached so this should be cheap in
923             // the long run.
924             int newYear = newYear(gyear);
925             if (days < newYear) {
926                 newYear = newYear(gyear-1);
927             }
928             internalSet(DAY_OF_YEAR, days - newYear + 1);
929         }
930     }
931
932     //------------------------------------------------------------------
933     // Fields to time
934     //------------------------------------------------------------------
935     
936     /**
937      * Return the Chinese new year of the given Gregorian year.
938      * @param gyear a Gregorian year
939      * @return days after January 1, 1970 0:00 Asia/Shanghai of the
940      * Chinese new year of the given year (this will be a new moon)
941      */
942     private int newYear(int gyear) {
943
944         long cacheValue = newYearCache.get(gyear);
945
946         if (cacheValue == CalendarCache.EMPTY) {
947
948             int solsticeBefore= winterSolstice(gyear - 1);
949             int solsticeAfter = winterSolstice(gyear);
950             int newMoon1 = newMoonNear(solsticeBefore + 1, true);
951             int newMoon2 = newMoonNear(newMoon1 + SYNODIC_GAP, true);
952             int newMoon11 = newMoonNear(solsticeAfter + 1, false);
953             
954             if (synodicMonthsBetween(newMoon1, newMoon11) == 12 &&
955                 (hasNoMajorSolarTerm(newMoon1) || hasNoMajorSolarTerm(newMoon2))) {
956                 cacheValue = newMoonNear(newMoon2 + SYNODIC_GAP, true);
957             } else {
958                 cacheValue = newMoon2;
959             }
960
961             newYearCache.put(gyear, cacheValue);
962         }
963         return (int) cacheValue;
964     }
965
966     /**
967      * Return the Julian day number of day before the first day of the
968      * given month in the given extended year.
969      * 
970      * <p>Note: This method reads the IS_LEAP_MONTH field to determine
971      * whether the given month is a leap month.
972      * @param eyear the extended year
973      * @param month the zero-based month.  The month is also determined
974      * by reading the IS_LEAP_MONTH field.
975      * @return the Julian day number of the day before the first
976      * day of the given month and year
977      * @stable ICU 2.8
978      */
979     protected int handleComputeMonthStart(int eyear, int month, boolean useMonth) {
980
981         // If the month is out of range, adjust it into range, and
982         // modify the extended year value accordingly.
983         if (month < 0 || month > 11) {
984             int[] rem = new int[1];
985             eyear += floorDivide(month, 12, rem);
986             month = rem[0];
987         }
988
989         int gyear = eyear + CHINESE_EPOCH_YEAR - 1; // Gregorian year
990         int newYear = newYear(gyear);
991         int newMoon = newMoonNear(newYear + month * 29, true);
992         
993         int julianDay = newMoon + EPOCH_JULIAN_DAY;
994
995         // Save fields for later restoration
996         int saveMonth = internalGet(MONTH);
997         int saveIsLeapMonth = internalGet(IS_LEAP_MONTH);
998
999         // Ignore IS_LEAP_MONTH field if useMonth is false
1000         int isLeapMonth = useMonth ? saveIsLeapMonth : 0;
1001
1002         computeGregorianFields(julianDay);
1003         
1004         // This will modify the MONTH and IS_LEAP_MONTH fields (only)
1005         computeChineseFields(newMoon, getGregorianYear(),
1006                              getGregorianMonth(), false);        
1007
1008         if (month != internalGet(MONTH) ||
1009             isLeapMonth != internalGet(IS_LEAP_MONTH)) {
1010             newMoon = newMoonNear(newMoon + SYNODIC_GAP, true);
1011             julianDay = newMoon + EPOCH_JULIAN_DAY;
1012         }
1013
1014         internalSet(MONTH, saveMonth);
1015         internalSet(IS_LEAP_MONTH, saveIsLeapMonth);
1016
1017         return julianDay - 1;
1018     }
1019
1020     /**
1021      * Return the current Calendar type.
1022      * @return type of calendar
1023      * @stable ICU 3.8
1024      */
1025     public String getType() {
1026         return "chinese";
1027     }
1028
1029     /**
1030      * Override readObject.
1031      */
1032     private void readObject(ObjectInputStream stream)
1033         throws IOException, ClassNotFoundException
1034     {
1035         stream.defaultReadObject();
1036         
1037         /* set up the transient caches... */
1038         astro = new CalendarAstronomer();
1039         winterSolsticeCache = new CalendarCache();
1040         newYearCache = new CalendarCache();
1041     }
1042     
1043     /*
1044     private static CalendarFactory factory;
1045     public static CalendarFactory factory() {
1046         if (factory == null) {
1047             factory = new CalendarFactory() {
1048                 public Calendar create(TimeZone tz, ULocale loc) {
1049                     return new ChineseCalendar(tz, loc);
1050                 }
1051
1052                 public String factoryName() {
1053                     return "Chinese";
1054                 }
1055             };
1056         }
1057         return factory;
1058     }
1059     */
1060 }