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