KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ibm > icu > util > ChineseCalendar


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

6 package com.ibm.icu.util;
7 import com.ibm.icu.text.*;
8 import com.ibm.icu.util.TimeZone;
9 import com.ibm.icu.impl.CalendarAstronomer;
10 import com.ibm.icu.impl.CalendarCache;
11
12 import java.io.IOException JavaDoc;
13 import java.io.ObjectInputStream JavaDoc;
14 import java.util.Locale JavaDoc;
15
16 /**
17  * <code>ChineseCalendar</code> is a concrete subclass of {@link Calendar}
18  * that implements a traditional Chinese calendar. The traditional Chinese
19  * calendar is a lunisolar calendar: Each month starts on a new moon, and
20  * the months are numbered according to solar events, specifically, to
21  * guarantee that month 11 always contains the winter solstice. In order
22  * to accomplish this, leap months are inserted in certain years. Leap
23  * months are numbered the same as the month they follow. The decision of
24  * which month is a leap month depends on the relative movements of the sun
25  * and moon.
26  *
27  * <p>This class defines one addition field beyond those defined by
28  * <code>Calendar</code>: The <code>IS_LEAP_MONTH</code> field takes the
29  * value of 0 for normal months, or 1 for leap months.
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 Chinese calendar with the default time zone and locale.
137      * @stable ICU 2.8
138      */

139     public ChineseCalendar() {
140         super();
141         setTimeInMillis(System.currentTimeMillis());
142     }
143
144     /**
145      * Construct a Chinese calendar with the given time zone and locale.
146      * @param zone time zone for this calendar
147      * @param locale locale for this calendar
148      * @stable ICU 2.8
149      */

150     public ChineseCalendar(TimeZone zone, Locale JavaDoc locale) {
151         super(zone, locale);
152         setTimeInMillis(System.currentTimeMillis());
153     }
154
155     /**
156      * Construct a Chinese calendar with the given time zone and locale.
157      * @param zone time zone for this calendar
158      * @param locale ulocale for this calendar
159      * @draft ICU 3.2
160      * @provisional This API might change or be removed in a future release.
161      */

162     public ChineseCalendar(TimeZone zone, ULocale locale) {
163         super(zone, locale);
164         setTimeInMillis(System.currentTimeMillis());
165     }
166
167     //------------------------------------------------------------------
168
// Public constants
169
//------------------------------------------------------------------
170

171     /**
172      * Field indicating whether or not the current month is a leap month.
173      * Should have a value of 0 for non-leap months, and 1 for leap months.
174      * @stable ICU 2.8
175      */

176     public static int IS_LEAP_MONTH = BASE_FIELD_COUNT;
177
178     /**
179      * Count of fields in this class.
180      */

181     private static final int FIELD_COUNT = IS_LEAP_MONTH + 1;
182
183     //------------------------------------------------------------------
184
// Calendar framework
185
//------------------------------------------------------------------
186

187     /**
188      * Override Calendar to allocate our additional field.
189      * @stable ICU 2.8
190      */

191     protected int[] handleCreateFields() {
192         return new int[FIELD_COUNT];
193     }
194
195     /**
196      * Array defining the limits of field values for this class. Field
197      * limits which are invariant with respect to calendar system and
198      * defined by Calendar are left blank.
199      *
200      * Notes:
201      *
202      * ERA 5000000 / 60 = 83333.
203      *
204      * MONTH There are 12 or 13 lunar months in a year. However, we always
205      * number them 0..11, with an intercalated, identically numbered leap
206      * month, when necessary.
207      *
208      * DAY_OF_YEAR In a non-leap year there are 353, 354, or 355 days. In
209      * a leap year there are 383, 384, or 385 days.
210      *
211      * WEEK_OF_YEAR The least maximum occurs if there are 353 days in the
212      * year, and the first 6 are the last week of the previous year. Then
213      * we have 49 full weeks and 4 days in the last week: 6 + 49*7 + 4 =
214      * 353. So the least maximum is 50. The maximum occurs if there are
215      * 385 days in the year, and WOY 1 extends 6 days into the prior year.
216      * Then there are 54 full weeks, and 6 days in the last week: 1 + 54*7
217      * + 6 = 385. The 6 days of the last week will fall into WOY 1 of the
218      * next year. Maximum is 55.
219      *
220      * WEEK_OF_MONTH In a 29 day month, if the first 7 days make up week 1
221      * that leaves 3 full weeks and 1 day at the end. The least maximum is
222      * thus 5. In a 30 days month, if the previous 6 days belong WOM 1 of
223      * this month, we have 4 full weeks and 1 days at the end (which
224      * technically will be WOM 1 of the next month, but will be reported by
225      * time->fields and hence by getActualMaximum as WOM 6 of this month).
226      * Maximum is 6.
227      *
228      * DAY_OF_WEEK_IN_MONTH In a 29 or 30 day month, there are 4 full weeks
229      * plus 1 or 2 days at the end, so the maximum is always 5.
230      */

231     private static final int LIMITS[][] = {
232         // Minimum Greatest Least Maximum
233
// Minimum Maximum
234
{ 1, 1, 83333, 83333 }, // ERA
235
{ 1, 1, 70, 70 }, // YEAR
236
{ 0, 0, 11, 11 }, // MONTH
237
{ 1, 1, 50, 55 }, // WEEK_OF_YEAR
238
{ 1, 1, 5, 6 }, // WEEK_OF_MONTH
239
{ 1, 1, 29, 30 }, // DAY_OF_MONTH
240
{ 1, 1, 353, 385 }, // DAY_OF_YEAR
241
{/* */}, // DAY_OF_WEEK
242
{ -1, -1, 5, 5 }, // DAY_OF_WEEK_IN_MONTH
243
{/* */}, // AM_PM
244
{/* */}, // HOUR
245
{/* */}, // HOUR_OF_DAY
246
{/* */}, // MINUTE
247
{/* */}, // SECOND
248
{/* */}, // MILLISECOND
249
{/* */}, // ZONE_OFFSET
250
{/* */}, // DST_OFFSET
251
{ -5000001, -5000001, 5000001, 5000001 }, // YEAR_WOY
252
{/* */}, // DOW_LOCAL
253
{ -5000000, -5000000, 5000000, 5000000 }, // EXTENDED_YEAR
254
{/* */}, // JULIAN_DAY
255
{/* */}, // MILLISECONDS_IN_DAY
256
{ 0, 0, 1, 1 }, // IS_LEAP_MONTH
257
};
258
259     /**
260      * Override Calendar to return the limit value for the given field.
261      * @stable ICU 2.8
262      */

263     protected int handleGetLimit(int field, int limitType) {
264         return LIMITS[field][limitType];
265     }
266
267     /**
268      * Implement abstract Calendar method to return the extended year
269      * defined by the current fields. This will use either the ERA and
270      * YEAR field as the cycle and year-of-cycle, or the EXTENDED_YEAR
271      * field as the continuous year count, depending on which is newer.
272      * @stable ICU 2.8
273      */

274     protected int handleGetExtendedYear() {
275         int year;
276         if (newestStamp(ERA, YEAR, UNSET) <= getStamp(EXTENDED_YEAR)) {
277             year = internalGet(EXTENDED_YEAR, 1); // Default to year 1
278
} else {
279             int cycle = internalGet(ERA, 1) - 1; // 0-based cycle
280
year = cycle * 60 + internalGet(YEAR, 1);
281         }
282         return year;
283     }
284
285     /**
286      * Override Calendar method to return the number of days in the given
287      * extended year and month.
288      *
289      * <p>Note: This method also reads the IS_LEAP_MONTH field to determine
290      * whether or not the given month is a leap month.
291      * @stable ICU 2.8
292      */

293     protected int handleGetMonthLength(int extendedYear, int month) {
294         int thisStart = handleComputeMonthStart(extendedYear, month, true) -
295             EPOCH_JULIAN_DAY + 1; // Julian day -> local days
296
int nextStart = newMoonNear(thisStart + SYNODIC_GAP, true);
297         return nextStart - thisStart;
298     }
299
300     /**
301      * Framework method to create a calendar-specific DateFormat object
302      * using the the given pattern. This method is responsible for
303      * creating the calendar- specific DateFormat and DateFormatSymbols
304      * objects as needed.
305      * @stable ICU 2.8
306      */

307     protected DateFormat handleGetDateFormat(String JavaDoc pattern, ULocale locale) {
308         return new ChineseDateFormat(pattern, locale);
309     }
310
311     /**
312      * Field resolution table that incorporates IS_LEAP_MONTH.
313      */

314     static final int[][][] CHINESE_DATE_PRECEDENCE = {
315         {
316             { DAY_OF_MONTH },
317             { WEEK_OF_YEAR, DAY_OF_WEEK },
318             { WEEK_OF_MONTH, DAY_OF_WEEK },
319             { DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK },
320             { WEEK_OF_YEAR, DOW_LOCAL },
321             { WEEK_OF_MONTH, DOW_LOCAL },
322             { DAY_OF_WEEK_IN_MONTH, DOW_LOCAL },
323             { DAY_OF_YEAR },
324             { RESOLVE_REMAP | DAY_OF_MONTH, IS_LEAP_MONTH },
325         },
326         {
327             { WEEK_OF_YEAR },
328             { WEEK_OF_MONTH },
329             { DAY_OF_WEEK_IN_MONTH },
330             { RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK },
331             { RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DOW_LOCAL },
332         },
333     };
334
335     /**
336      * Override Calendar to add IS_LEAP_MONTH to the field resolution
337      * table.
338      * @stable ICU 2.8
339      */

340     protected int[][][] getFieldResolutionTable() {
341         return CHINESE_DATE_PRECEDENCE;
342     }
343
344     /**
345      * Adjust this calendar to be delta months before or after a given
346      * start position, pinning the day of month if necessary. The start
347      * position is given as a local days number for the start of the month
348      * and a day-of-month. Used by add() and roll().
349      * @param newMoon the local days of the first day of the month of the
350      * start position (days after January 1, 1970 0:00 Asia/Shanghai)
351      * @param dom the 1-based day-of-month of the start position
352      * @param delta the number of months to move forward or backward from
353      * the start position
354      */

355     private void offsetMonth(int newMoon, int dom, int delta) {
356         // Move to the middle of the month before our target month.
357
newMoon += (int) (CalendarAstronomer.SYNODIC_MONTH * (delta - 0.5));
358
359         // Search forward to the target month's new moon
360
newMoon = newMoonNear(newMoon, true);
361
362         // Find the target dom
363
int jd = newMoon + EPOCH_JULIAN_DAY - 1 + dom;
364
365         // Pin the dom. In this calendar all months are 29 or 30 days
366
// so pinning just means handling dom 30.
367
if (dom > 29) {
368             set(JULIAN_DAY, jd-1);
369             // TODO Fix this. We really shouldn't ever have to
370
// explicitly call complete(). This is either a bug in
371
// this method, in ChineseCalendar, or in
372
// Calendar.getActualMaximum(). I suspect the last.
373
complete();
374             if (getActualMaximum(DAY_OF_MONTH) >= dom) {
375                 set(JULIAN_DAY, jd);
376             }
377         } else {
378             set(JULIAN_DAY, jd);
379         }
380     }
381
382     /**
383      * Override Calendar to handle leap months properly.
384      * @stable ICU 2.8
385      */

386     public void add(int field, int amount) {
387         switch (field) {
388         case MONTH:
389             if (amount != 0) {
390                 int dom = get(DAY_OF_MONTH);
391                 int day = get(JULIAN_DAY) - EPOCH_JULIAN_DAY; // Get local day
392
int moon = day - dom + 1; // New moon
393
offsetMonth(moon, dom, amount);
394             }
395             break;
396         default:
397             super.add(field, amount);
398             break;
399         }
400     }
401
402     /**
403      * Override Calendar to handle leap months properly.
404      * @stable ICU 2.8
405      */

406     public void roll(int field, int amount) {
407         switch (field) {
408         case MONTH:
409             if (amount != 0) {
410                 int dom = get(DAY_OF_MONTH);
411                 int day = get(JULIAN_DAY) - EPOCH_JULIAN_DAY; // Get local day
412
int moon = day - dom + 1; // New moon (start of this month)
413

414                 // Note throughout the following: Months 12 and 1 are never
415
// followed by a leap month (D&R p. 185).
416

417                 // Compute the adjusted month number m. This is zero-based
418
// value from 0..11 in a non-leap year, and from 0..12 in a
419
// leap year.
420
int m = get(MONTH); // 0-based month
421
if (isLeapYear) { // (member variable)
422
if (get(IS_LEAP_MONTH) == 1) {
423                         ++m;
424                     } else {
425                         // Check for a prior leap month. (In the
426
// following, month 0 is the first month of the
427
// year.) Month 0 is never followed by a leap
428
// month, and we know month m is not a leap month.
429
// moon1 will be the start of month 0 if there is
430
// no leap month between month 0 and month m;
431
// otherwise it will be the start of month 1.
432
int moon1 = moon -
433                             (int) (CalendarAstronomer.SYNODIC_MONTH * (m - 0.5));
434                         moon1 = newMoonNear(moon1, true);
435                         if (isLeapMonthBetween(moon1, moon)) {
436                             ++m;
437                         }
438                     }
439                 }
440
441                 // Now do the standard roll computation on m, with the
442
// allowed range of 0..n-1, where n is 12 or 13.
443
int n = isLeapYear ? 13 : 12; // Months in this year
444
int newM = (m + amount) % n;
445                 if (newM < 0) {
446                     newM += n;
447                 }
448
449                 if (newM != m) {
450                     offsetMonth(moon, dom, newM - m);
451                 }
452             }
453             break;
454         default:
455             super.roll(field, amount);
456             break;
457         }
458     }
459
460     //------------------------------------------------------------------
461
// Support methods and constants
462
//------------------------------------------------------------------
463

464     /**
465      * The start year of the Chinese calendar, the 61st year of the reign
466      * of Huang Di. Some sources use the first year of his reign,
467      * resulting in EXTENDED_YEAR values 60 years greater and ERA (cycle)
468      * values one greater.
469      */

470     private static final int CHINESE_EPOCH_YEAR = -2636; // Gregorian year
471

472     /**
473      * The offset from GMT in milliseconds at which we perform astronomical
474      * computations. Some sources use a different historically accurate
475      * offset of GMT+7:45:40 for years before 1929; we do not do this.
476      */

477     private static final long CHINA_OFFSET = 8*ONE_HOUR;
478
479     /**
480      * Value to be added or subtracted from the local days of a new moon to
481      * get close to the next or prior new moon, but not cross it. Must be
482      * >= 1 and < CalendarAstronomer.SYNODIC_MONTH.
483      */

484     private static final int SYNODIC_GAP = 25;
485
486     /**
487      * Convert local days to UTC epoch milliseconds.
488      * @param days days after January 1, 1970 0:00 Asia/Shanghai
489      * @return milliseconds after January 1, 1970 0:00 GMT
490      */

491     private static final long daysToMillis(int days) {
492         return (days * ONE_DAY) - CHINA_OFFSET;
493     }
494
495     /**
496      * Convert UTC epoch milliseconds to local days.
497      * @param millis milliseconds after January 1, 1970 0:00 GMT
498      * @return days after January 1, 1970 0:00 Asia/Shanghai
499      */

500     private static final int millisToDays(long millis) {
501         return (int) floorDivide(millis + CHINA_OFFSET, ONE_DAY);
502     }
503
504     //------------------------------------------------------------------
505
// Astronomical computations
506
//------------------------------------------------------------------
507

508     /**
509      * Return the major solar term on or after December 15 of the given
510      * Gregorian year, that is, the winter solstice of the given year.
511      * Computations are relative to Asia/Shanghai time zone.
512      * @param gyear a Gregorian year
513      * @return days after January 1, 1970 0:00 Asia/Shanghai of the
514      * winter solstice of the given year
515      */

516     private int winterSolstice(int gyear) {
517
518         long cacheValue = winterSolsticeCache.get(gyear);
519
520         if (cacheValue == CalendarCache.EMPTY) {
521             // In books December 15 is used, but it fails for some years
522
// using our algorithms, e.g.: 1298 1391 1492 1553 1560. That
523
// is, winterSolstice(1298) starts search at Dec 14 08:00:00
524
// PST 1298 with a final result of Dec 14 10:31:59 PST 1299.
525
long ms = daysToMillis(computeGregorianMonthStart(gyear, DECEMBER) +
526                                    1 - EPOCH_JULIAN_DAY);
527             astro.setTime(ms);
528             
529             // Winter solstice is 270 degrees solar longitude aka Dongzhi
530
long solarLong = astro.getSunTime(CalendarAstronomer.WINTER_SOLSTICE,
531                                               true);
532             cacheValue = millisToDays(solarLong);
533             winterSolsticeCache.put(gyear, cacheValue);
534         }
535         return (int) cacheValue;
536     }
537
538     /**
539      * Return the closest new moon to the given date, searching either
540      * forward or backward in time.
541      * @param days days after January 1, 1970 0:00 Asia/Shanghai
542      * @param after if true, search for a new moon on or after the given
543      * date; otherwise, search for a new moon before it
544      * @return days after January 1, 1970 0:00 Asia/Shanghai of the nearest
545      * new moon after or before <code>days</code>
546      */

547     private int newMoonNear(int days, boolean after) {
548         
549         astro.setTime(daysToMillis(days));
550         long newMoon = astro.getMoonTime(CalendarAstronomer.NEW_MOON, after);
551         
552         return millisToDays(newMoon);
553     }
554
555     /**
556      * Return the nearest integer number of synodic months between
557      * two dates.
558      * @param day1 days after January 1, 1970 0:00 Asia/Shanghai
559      * @param day2 days after January 1, 1970 0:00 Asia/Shanghai
560      * @return the nearest integer number of months between day1 and day2
561      */

562     private int synodicMonthsBetween(int day1, int day2) {
563         return (int) Math.round((day2 - day1) / CalendarAstronomer.SYNODIC_MONTH);
564     }
565
566     /**
567      * Return the major solar term on or before a given date. This
568      * will be an integer from 1..12, with 1 corresponding to 330 degrees,
569      * 2 to 0 degrees, 3 to 30 degrees,..., and 12 to 300 degrees.
570      * @param days days after January 1, 1970 0:00 Asia/Shanghai
571      */

572     private int majorSolarTerm(int days) {
573         
574         astro.setTime(daysToMillis(days));
575
576         // Compute (floor(solarLongitude / (pi/6)) + 2) % 12
577
int term = ((int) Math.floor(6 * astro.getSunLongitude() / Math.PI) + 2) % 12;
578         if (term < 1) {
579             term += 12;
580         }
581         return term;
582     }
583
584     /**
585      * Return true if the given month lacks a major solar term.
586      * @param newMoon days after January 1, 1970 0:00 Asia/Shanghai of a new
587      * moon
588      */

589     private boolean hasNoMajorSolarTerm(int newMoon) {
590         
591         int mst = majorSolarTerm(newMoon);
592         int nmn = newMoonNear(newMoon + SYNODIC_GAP, true);
593         int mstt = majorSolarTerm(nmn);
594         return mst == mstt;
595         /*
596         return majorSolarTerm(newMoon) ==
597             majorSolarTerm(newMoonNear(newMoon + SYNODIC_GAP, true));
598         */

599     }
600
601     //------------------------------------------------------------------
602
// Time to fields
603
//------------------------------------------------------------------
604

605     /**
606      * Return true if there is a leap month on or after month newMoon1 and
607      * at or before month newMoon2.
608      * @param newMoon1 days after January 1, 1970 0:00 Asia/Shanghai of a
609      * new moon
610      * @param newMoon2 days after January 1, 1970 0:00 Asia/Shanghai of a
611      * new moon
612      */

613     private boolean isLeapMonthBetween(int newMoon1, int newMoon2) {
614
615         // This is only needed to debug the timeOfAngle divergence bug.
616
// Remove this later. Liu 11/9/00
617
// DEBUG
618
if (synodicMonthsBetween(newMoon1, newMoon2) >= 50) {
619             throw new IllegalArgumentException JavaDoc("isLeapMonthBetween(" + newMoon1 +
620                                                ", " + newMoon2 +
621                                                "): Invalid parameters");
622         }
623
624         return (newMoon2 >= newMoon1) &&
625             (isLeapMonthBetween(newMoon1, newMoonNear(newMoon2 - SYNODIC_GAP, false)) ||
626              hasNoMajorSolarTerm(newMoon2));
627     }
628
629     /**
630      * Override Calendar to compute several fields specific to the Chinese
631      * calendar system. These are:
632      *
633      * <ul><li>ERA
634      * <li>YEAR
635      * <li>MONTH
636      * <li>DAY_OF_MONTH
637      * <li>DAY_OF_YEAR
638      * <li>EXTENDED_YEAR</ul>
639      *
640      * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
641      * method is called. The getGregorianXxx() methods return Gregorian
642      * calendar equivalents for the given Julian day.
643      *
644      * <p>Compute the ChineseCalendar-specific field IS_LEAP_MONTH.
645      * @stable ICU 2.8
646      */

647     protected void handleComputeFields(int julianDay) {
648
649         computeChineseFields(julianDay - EPOCH_JULIAN_DAY, // local days
650
getGregorianYear(), getGregorianMonth(),
651                              true); // set all fields
652
}
653
654     /**
655      * Compute fields for the Chinese calendar system. This method can
656      * either set all relevant fields, as required by
657      * <code>handleComputeFields()</code>, or it can just set the MONTH and
658      * IS_LEAP_MONTH fields, as required by
659      * <code>handleComputeMonthStart()</code>.
660      *
661      * <p>As a side effect, this method sets {@link #isLeapYear}.
662      * @param days days after January 1, 1970 0:00 Asia/Shanghai of the
663      * date to compute fields for
664      * @param gyear the Gregorian year of the given date
665      * @param gmonth the Gregorian month of the given date
666      * @param setAllFields if true, set the EXTENDED_YEAR, ERA, YEAR,
667      * DAY_OF_MONTH, and DAY_OF_YEAR fields. In either case set the MONTH
668      * and IS_LEAP_MONTH fields.
669      */

670     private void computeChineseFields(int days, int gyear, int gmonth,
671                                       boolean setAllFields) {
672
673         // Find the winter solstices before and after the target date.
674
// These define the boundaries of this Chinese year, specifically,
675
// the position of month 11, which always contains the solstice.
676
// We want solsticeBefore <= date < solsticeAfter.
677
int solsticeBefore;
678         int solsticeAfter = winterSolstice(gyear);
679         if (days < solsticeAfter) {
680             solsticeBefore = winterSolstice(gyear - 1);
681         } else {
682             solsticeBefore = solsticeAfter;
683             solsticeAfter = winterSolstice(gyear + 1);
684         }
685
686         // Find the start of the month after month 11. This will be either
687
// the prior month 12 or leap month 11 (very rare). Also find the
688
// start of the following month 11.
689
int firstMoon = newMoonNear(solsticeBefore + 1, true);
690         int lastMoon = newMoonNear(solsticeAfter + 1, false);
691         int thisMoon = newMoonNear(days + 1, false); // Start of this month
692
// Note: isLeapYear is a member variable
693
isLeapYear = synodicMonthsBetween(firstMoon, lastMoon) == 12;
694
695         int month = synodicMonthsBetween(firstMoon, thisMoon);
696         if (isLeapYear && isLeapMonthBetween(firstMoon, thisMoon)) {
697             month--;
698         }
699         if (month < 1) {
700             month += 12;
701         }
702
703         boolean isLeapMonth = isLeapYear &&
704             hasNoMajorSolarTerm(thisMoon) &&
705             !isLeapMonthBetween(firstMoon, newMoonNear(thisMoon - SYNODIC_GAP, false));
706
707         internalSet(MONTH, month-1); // Convert from 1-based to 0-based
708
internalSet(IS_LEAP_MONTH, isLeapMonth?1:0);
709
710         if (setAllFields) {
711
712             int year = gyear - CHINESE_EPOCH_YEAR;
713             if (month < 11 ||
714                 gmonth >= JULY) {
715                 year++;
716             }
717             int dayOfMonth = days - thisMoon + 1;
718
719             internalSet(EXTENDED_YEAR, year);
720
721             // 0->0,60 1->1,1 60->1,60 61->2,1 etc.
722
int[] yearOfCycle = new int[1];
723             int cycle = floorDivide(year-1, 60, yearOfCycle);
724             internalSet(ERA, cycle+1);
725             internalSet(YEAR, yearOfCycle[0]+1);
726
727             internalSet(DAY_OF_MONTH, dayOfMonth);
728
729             // Days will be before the first new year we compute if this
730
// date is in month 11, leap 11, 12. There is never a leap 12.
731
// New year computations are cached so this should be cheap in
732
// the long run.
733
int newYear = newYear(gyear);
734             if (days < newYear) {
735                 newYear = newYear(gyear-1);
736             }
737             internalSet(DAY_OF_YEAR, days - newYear + 1);
738         }
739     }
740
741     //------------------------------------------------------------------
742
// Fields to time
743
//------------------------------------------------------------------
744

745     /**
746      * Return the Chinese new year of the given Gregorian year.
747      * @param gyear a Gregorian year
748      * @return days after January 1, 1970 0:00 Asia/Shanghai of the
749      * Chinese new year of the given year (this will be a new moon)
750      */

751     private int newYear(int gyear) {
752
753         long cacheValue = newYearCache.get(gyear);
754
755         if (cacheValue == CalendarCache.EMPTY) {
756
757             int solsticeBefore= winterSolstice(gyear - 1);
758             int solsticeAfter = winterSolstice(gyear);
759             int newMoon1 = newMoonNear(solsticeBefore + 1, true);
760             int newMoon2 = newMoonNear(newMoon1 + SYNODIC_GAP, true);
761             int newMoon11 = newMoonNear(solsticeAfter + 1, false);
762             
763             if (synodicMonthsBetween(newMoon1, newMoon11) == 12 &&
764                 (hasNoMajorSolarTerm(newMoon1) || hasNoMajorSolarTerm(newMoon2))) {
765                 cacheValue = newMoonNear(newMoon2 + SYNODIC_GAP, true);
766             } else {
767                 cacheValue = newMoon2;
768             }
769
770             newYearCache.put(gyear, cacheValue);
771         }
772         return (int) cacheValue;
773     }
774
775     /**
776      * Return the Julian day number of day before the first day of the
777      * given month in the given extended year.
778      *
779      * <p>Note: This method reads the IS_LEAP_MONTH field to determine
780      * whether the given month is a leap month.
781      * @param eyear the extended year
782      * @param month the zero-based month. The month is also determined
783      * by reading the IS_LEAP_MONTH field.
784      * @return the Julian day number of the day before the first
785      * day of the given month and year
786      * @stable ICU 2.8
787      */

788     protected int handleComputeMonthStart(int eyear, int month, boolean useMonth) {
789
790         // If the month is out of range, adjust it into range, and
791
// modify the extended year value accordingly.
792
if (month < 0 || month > 11) {
793             int[] rem = new int[1];
794             eyear += floorDivide(month, 12, rem);
795             month = rem[0];
796         }
797
798         int gyear = eyear + CHINESE_EPOCH_YEAR - 1; // Gregorian year
799
int newYear = newYear(gyear);
800         int newMoon = newMoonNear(newYear + month * 29, true);
801         
802         int julianDay = newMoon + EPOCH_JULIAN_DAY;
803
804         // Save fields for later restoration
805
int saveMonth = internalGet(MONTH);
806         int saveIsLeapMonth = internalGet(IS_LEAP_MONTH);
807
808         // Ignore IS_LEAP_MONTH field if useMonth is false
809
int isLeapMonth = useMonth ? saveIsLeapMonth : 0;
810
811         computeGregorianFields(julianDay);
812         
813         // This will modify the MONTH and IS_LEAP_MONTH fields (only)
814
computeChineseFields(newMoon, getGregorianYear(),
815                              getGregorianMonth(), false);
816
817         if (month != internalGet(MONTH) ||
818             isLeapMonth != internalGet(IS_LEAP_MONTH)) {
819             newMoon = newMoonNear(newMoon + SYNODIC_GAP, true);
820             julianDay = newMoon + EPOCH_JULIAN_DAY;
821         }
822
823         internalSet(MONTH, saveMonth);
824         internalSet(IS_LEAP_MONTH, saveIsLeapMonth);
825
826         return julianDay - 1;
827     }
828
829     /**
830      * Return the current Calendar type.
831      * @return type of calendar (gregorian, etc.)
832      * @internal ICU 3.0
833      * @deprecated This API is ICU internal only.
834      */

835     public String JavaDoc getType() {
836         return "chinese";
837     }
838
839     /**
840      * Override readObject.
841      */

842     private void readObject(ObjectInputStream JavaDoc stream)
843         throws IOException JavaDoc, ClassNotFoundException JavaDoc
844     {
845         stream.defaultReadObject();
846         
847         /* set up the transient caches... */
848         astro = new CalendarAstronomer();
849         winterSolsticeCache = new CalendarCache();
850         newYearCache = new CalendarCache();
851     }
852     
853     /*
854     private static CalendarFactory factory;
855     public static CalendarFactory factory() {
856         if (factory == null) {
857             factory = new CalendarFactory() {
858                 public Calendar create(TimeZone tz, ULocale loc) {
859                     return new ChineseCalendar(tz, loc);
860                 }
861
862                 public String factoryName() {
863                     return "Chinese";
864                 }
865             };
866         }
867         return factory;
868     }
869     */

870 }
871
Popular Tags