1/*
2 ******************************************************************************
3 * Copyright (C) 2007-2008, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 ******************************************************************************
6 *
7 * File CHNSECAL.CPP
8 *
9 * Modification History:
10 *
11 *   Date        Name        Description
12 *   9/18/2007  ajmacher         ported from java ChineseCalendar
13 *****************************************************************************
14 */
15
16#include "chnsecal.h"
17
18#if !UCONFIG_NO_FORMATTING
19
20#include "umutex.h"
21#include <float.h>
22#include "gregoimp.h" // Math
23#include "astro.h" // CalendarAstronomer
24#include "uhash.h"
25#include "ucln_in.h"
26
27// Debugging
28#ifdef U_DEBUG_CHNSECAL
29# include <stdio.h>
30# include <stdarg.h>
31static void debug_chnsecal_loc(const char *f, int32_t l)
32{
33    fprintf(stderr, "%s:%d: ", f, l);
34}
35
36static void debug_chnsecal_msg(const char *pat, ...)
37{
38    va_list ap;
39    va_start(ap, pat);
40    vfprintf(stderr, pat, ap);
41    fflush(stderr);
42}
43// must use double parens, i.e.:  U_DEBUG_CHNSECAL_MSG(("four is: %d",4));
44#define U_DEBUG_CHNSECAL_MSG(x) {debug_chnsecal_loc(__FILE__,__LINE__);debug_chnsecal_msg x;}
45#else
46#define U_DEBUG_CHNSECAL_MSG(x)
47#endif
48
49
50// --- The cache --
51static UMTX astroLock = 0;  // pod bay door lock
52static U_NAMESPACE_QUALIFIER CalendarAstronomer *gChineseCalendarAstro = NULL;
53static U_NAMESPACE_QUALIFIER CalendarCache *gChineseCalendarWinterSolsticeCache = NULL;
54static U_NAMESPACE_QUALIFIER CalendarCache *gChineseCalendarNewYearCache = NULL;
55
56/**
57 * The start year of the Chinese calendar, the 61st year of the reign
58 * of Huang Di.  Some sources use the first year of his reign,
59 * resulting in EXTENDED_YEAR values 60 years greater and ERA (cycle)
60 * values one greater.
61 */
62static const int32_t CHINESE_EPOCH_YEAR = -2636; // Gregorian year
63
64/**
65 * The offset from GMT in milliseconds at which we perform astronomical
66 * computations.  Some sources use a different historically accurate
67 * offset of GMT+7:45:40 for years before 1929; we do not do this.
68 */
69static const double CHINA_OFFSET = 8 * kOneHour;
70
71/**
72 * Value to be added or subtracted from the local days of a new moon to
73 * get close to the next or prior new moon, but not cross it.  Must be
74 * >= 1 and < CalendarAstronomer.SYNODIC_MONTH.
75 */
76static const int32_t SYNODIC_GAP = 25;
77
78
79U_CDECL_BEGIN
80static UBool calendar_chinese_cleanup(void) {
81    if (gChineseCalendarAstro) {
82        delete gChineseCalendarAstro;
83        gChineseCalendarAstro = NULL;
84    }
85    if (gChineseCalendarWinterSolsticeCache) {
86        delete gChineseCalendarWinterSolsticeCache;
87        gChineseCalendarWinterSolsticeCache = NULL;
88    }
89    if (gChineseCalendarNewYearCache) {
90        delete gChineseCalendarNewYearCache;
91        gChineseCalendarNewYearCache = NULL;
92    }
93    umtx_destroy(&astroLock);
94    return TRUE;
95}
96U_CDECL_END
97
98U_NAMESPACE_BEGIN
99
100
101// Implementation of the ChineseCalendar class
102
103
104//-------------------------------------------------------------------------
105// Constructors...
106//-------------------------------------------------------------------------
107
108
109Calendar* ChineseCalendar::clone() const {
110    return new ChineseCalendar(*this);
111}
112
113ChineseCalendar::ChineseCalendar(const Locale& aLocale, UErrorCode& success)
114:   Calendar(TimeZone::createDefault(), aLocale, success)
115{
116    isLeapYear = FALSE;
117    setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
118}
119
120ChineseCalendar::ChineseCalendar(const ChineseCalendar& other) : Calendar(other) {
121    isLeapYear = other.isLeapYear;
122}
123
124ChineseCalendar::~ChineseCalendar()
125{
126}
127
128const char *ChineseCalendar::getType() const {
129    return "chinese";
130}
131
132//-------------------------------------------------------------------------
133// Minimum / Maximum access functions
134//-------------------------------------------------------------------------
135
136
137static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
138    // Minimum  Greatest     Least    Maximum
139    //           Minimum   Maximum
140    {        1,        1,    83333,    83333}, // ERA
141    {        1,        1,       60,       60}, // YEAR
142    {        0,        0,       11,       11}, // MONTH
143    {        1,        1,       50,       55}, // WEEK_OF_YEAR
144    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
145    {        1,        1,       29,       30}, // DAY_OF_MONTH
146    {        1,        1,      353,      385}, // DAY_OF_YEAR
147    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
148    {       -1,       -1,        5,        5}, // DAY_OF_WEEK_IN_MONTH
149    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
150    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
151    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
152    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
153    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
154    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
155    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
156    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
157    { -5000000, -5000000,  5000000,  5000000}, // YEAR_WOY
158    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
159    { -5000000, -5000000,  5000000,  5000000}, // EXTENDED_YEAR
160    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
161    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
162    {        0,        0,        1,        1}, // IS_LEAP_MONTH
163};
164
165
166/**
167* @draft ICU 2.4
168*/
169int32_t ChineseCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
170    return LIMITS[field][limitType];
171}
172
173
174//----------------------------------------------------------------------
175// Calendar framework
176//----------------------------------------------------------------------
177
178/**
179 * Implement abstract Calendar method to return the extended year
180 * defined by the current fields.  This will use either the ERA and
181 * YEAR field as the cycle and year-of-cycle, or the EXTENDED_YEAR
182 * field as the continuous year count, depending on which is newer.
183 * @stable ICU 2.8
184 */
185int32_t ChineseCalendar::handleGetExtendedYear() {
186    int32_t year;
187    if (newestStamp(UCAL_ERA, UCAL_YEAR, kUnset) <= fStamp[UCAL_EXTENDED_YEAR]) {
188        year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
189    } else {
190        int32_t cycle = internalGet(UCAL_ERA, 1) - 1; // 0-based cycle
191        year = cycle * 60 + internalGet(UCAL_YEAR, 1);
192    }
193    return year;
194}
195
196/**
197 * Override Calendar method to return the number of days in the given
198 * extended year and month.
199 *
200 * <p>Note: This method also reads the IS_LEAP_MONTH field to determine
201 * whether or not the given month is a leap month.
202 * @stable ICU 2.8
203 */
204int32_t ChineseCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
205    int32_t thisStart = handleComputeMonthStart(extendedYear, month, TRUE) -
206        kEpochStartAsJulianDay + 1; // Julian day -> local days
207    int32_t nextStart = newMoonNear(thisStart + SYNODIC_GAP, TRUE);
208    return nextStart - thisStart;
209}
210
211/**
212 * Override Calendar to compute several fields specific to the Chinese
213 * calendar system.  These are:
214 *
215 * <ul><li>ERA
216 * <li>YEAR
217 * <li>MONTH
218 * <li>DAY_OF_MONTH
219 * <li>DAY_OF_YEAR
220 * <li>EXTENDED_YEAR</ul>
221 *
222 * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
223 * method is called.  The getGregorianXxx() methods return Gregorian
224 * calendar equivalents for the given Julian day.
225 *
226 * <p>Compute the ChineseCalendar-specific field IS_LEAP_MONTH.
227 * @stable ICU 2.8
228 */
229void ChineseCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) {
230
231    computeChineseFields(julianDay - kEpochStartAsJulianDay, // local days
232                         getGregorianYear(), getGregorianMonth(),
233                         TRUE); // set all fields
234}
235
236/**
237 * Field resolution table that incorporates IS_LEAP_MONTH.
238 */
239const UFieldResolutionTable ChineseCalendar::CHINESE_DATE_PRECEDENCE[] =
240{
241    {
242        { UCAL_DAY_OF_MONTH, kResolveSTOP },
243        { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP },
244        { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
245        { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
246        { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP },
247        { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
248        { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
249        { UCAL_DAY_OF_YEAR, kResolveSTOP },
250        { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_IS_LEAP_MONTH, kResolveSTOP },
251        { kResolveSTOP }
252    },
253    {
254        { UCAL_WEEK_OF_YEAR, kResolveSTOP },
255        { UCAL_WEEK_OF_MONTH, kResolveSTOP },
256        { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP },
257        { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
258        { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
259        { kResolveSTOP }
260    },
261    {{kResolveSTOP}}
262};
263
264/**
265 * Override Calendar to add IS_LEAP_MONTH to the field resolution
266 * table.
267 * @stable ICU 2.8
268 */
269const UFieldResolutionTable* ChineseCalendar::getFieldResolutionTable() const {
270    return CHINESE_DATE_PRECEDENCE;
271}
272
273/**
274 * Return the Julian day number of day before the first day of the
275 * given month in the given extended year.
276 *
277 * <p>Note: This method reads the IS_LEAP_MONTH field to determine
278 * whether the given month is a leap month.
279 * @param eyear the extended year
280 * @param month the zero-based month.  The month is also determined
281 * by reading the IS_LEAP_MONTH field.
282 * @return the Julian day number of the day before the first
283 * day of the given month and year
284 * @stable ICU 2.8
285 */
286int32_t ChineseCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const {
287
288    ChineseCalendar *nonConstThis = (ChineseCalendar*)this; // cast away const
289
290    // If the month is out of range, adjust it into range, and
291    // modify the extended year value accordingly.
292    if (month < 0 || month > 11) {
293        double m = month;
294        eyear += (int32_t)ClockMath::floorDivide(m, 12.0, m);
295        month = (int32_t)m;
296    }
297
298    int32_t gyear = eyear + CHINESE_EPOCH_YEAR - 1; // Gregorian year
299    int32_t theNewYear = newYear(gyear);
300    int32_t newMoon = newMoonNear(theNewYear + month * 29, TRUE);
301
302    int32_t julianDay = newMoon + kEpochStartAsJulianDay;
303
304    // Save fields for later restoration
305    int32_t saveMonth = internalGet(UCAL_MONTH);
306    int32_t saveIsLeapMonth = internalGet(UCAL_IS_LEAP_MONTH);
307
308    // Ignore IS_LEAP_MONTH field if useMonth is false
309    int32_t isLeapMonth = useMonth ? saveIsLeapMonth : 0;
310
311    UErrorCode status = U_ZERO_ERROR;
312    nonConstThis->computeGregorianFields(julianDay, status);
313    if (U_FAILURE(status))
314        return 0;
315
316    // This will modify the MONTH and IS_LEAP_MONTH fields (only)
317    nonConstThis->computeChineseFields(newMoon, getGregorianYear(),
318                         getGregorianMonth(), FALSE);
319
320    if (month != internalGet(UCAL_MONTH) ||
321        isLeapMonth != internalGet(UCAL_IS_LEAP_MONTH)) {
322        newMoon = newMoonNear(newMoon + SYNODIC_GAP, TRUE);
323        julianDay = newMoon + kEpochStartAsJulianDay;
324    }
325
326    nonConstThis->internalSet(UCAL_MONTH, saveMonth);
327    nonConstThis->internalSet(UCAL_IS_LEAP_MONTH, saveIsLeapMonth);
328
329    return julianDay - 1;
330}
331
332
333/**
334 * Override Calendar to handle leap months properly.
335 * @stable ICU 2.8
336 */
337void ChineseCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) {
338    switch (field) {
339    case UCAL_MONTH:
340        if (amount != 0) {
341            int32_t dom = get(UCAL_DAY_OF_MONTH, status);
342            if (U_FAILURE(status)) break;
343            int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day
344            if (U_FAILURE(status)) break;
345            int32_t moon = day - dom + 1; // New moon
346            offsetMonth(moon, dom, amount);
347        }
348        break;
349    default:
350        Calendar::add(field, amount, status);
351        break;
352    }
353}
354
355/**
356 * Override Calendar to handle leap months properly.
357 * @stable ICU 2.8
358 */
359void ChineseCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) {
360    add((UCalendarDateFields)field, amount, status);
361}
362
363/**
364 * Override Calendar to handle leap months properly.
365 * @stable ICU 2.8
366 */
367void ChineseCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) {
368    switch (field) {
369    case UCAL_MONTH:
370        if (amount != 0) {
371            int32_t dom = get(UCAL_DAY_OF_MONTH, status);
372            if (U_FAILURE(status)) break;
373            int32_t day = get(UCAL_JULIAN_DAY, status) - kEpochStartAsJulianDay; // Get local day
374            if (U_FAILURE(status)) break;
375            int32_t moon = day - dom + 1; // New moon (start of this month)
376
377            // Note throughout the following:  Months 12 and 1 are never
378            // followed by a leap month (D&R p. 185).
379
380            // Compute the adjusted month number m.  This is zero-based
381            // value from 0..11 in a non-leap year, and from 0..12 in a
382            // leap year.
383            int32_t m = get(UCAL_MONTH, status); // 0-based month
384            if (U_FAILURE(status)) break;
385            if (isLeapYear) { // (member variable)
386                if (get(UCAL_IS_LEAP_MONTH, status) == 1) {
387                    ++m;
388                } else {
389                    // Check for a prior leap month.  (In the
390                    // following, month 0 is the first month of the
391                    // year.)  Month 0 is never followed by a leap
392                    // month, and we know month m is not a leap month.
393                    // moon1 will be the start of month 0 if there is
394                    // no leap month between month 0 and month m;
395                    // otherwise it will be the start of month 1.
396                    int moon1 = moon -
397                        (int) (CalendarAstronomer::SYNODIC_MONTH * (m - 0.5));
398                    moon1 = newMoonNear(moon1, TRUE);
399                    if (isLeapMonthBetween(moon1, moon)) {
400                        ++m;
401                    }
402                }
403                if (U_FAILURE(status)) break;
404            }
405
406            // Now do the standard roll computation on m, with the
407            // allowed range of 0..n-1, where n is 12 or 13.
408            int32_t n = isLeapYear ? 13 : 12; // Months in this year
409            int32_t newM = (m + amount) % n;
410            if (newM < 0) {
411                newM += n;
412            }
413
414            if (newM != m) {
415                offsetMonth(moon, dom, newM - m);
416            }
417        }
418        break;
419    default:
420        Calendar::roll(field, amount, status);
421        break;
422    }
423}
424
425void ChineseCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) {
426    roll((UCalendarDateFields)field, amount, status);
427}
428
429
430//------------------------------------------------------------------
431// Support methods and constants
432//------------------------------------------------------------------
433
434/**
435 * Convert local days to UTC epoch milliseconds.
436 * @param days days after January 1, 1970 0:00 Asia/Shanghai
437 * @return milliseconds after January 1, 1970 0:00 GMT
438 */
439double ChineseCalendar::daysToMillis(double days) {
440    return (days * kOneDay) - CHINA_OFFSET;
441}
442
443/**
444 * Convert UTC epoch milliseconds to local days.
445 * @param millis milliseconds after January 1, 1970 0:00 GMT
446 * @return days after January 1, 1970 0:00 Asia/Shanghai
447 */
448double ChineseCalendar::millisToDays(double millis) {
449    return ClockMath::floorDivide(millis + CHINA_OFFSET, kOneDay);
450}
451
452//------------------------------------------------------------------
453// Astronomical computations
454//------------------------------------------------------------------
455
456
457/**
458 * Return the major solar term on or after December 15 of the given
459 * Gregorian year, that is, the winter solstice of the given year.
460 * Computations are relative to Asia/Shanghai time zone.
461 * @param gyear a Gregorian year
462 * @return days after January 1, 1970 0:00 Asia/Shanghai of the
463 * winter solstice of the given year
464 */
465int32_t ChineseCalendar::winterSolstice(int32_t gyear) const {
466
467    UErrorCode status = U_ZERO_ERROR;
468    int32_t cacheValue = CalendarCache::get(&gChineseCalendarWinterSolsticeCache, gyear, status);
469
470    if (cacheValue == 0) {
471        // In books December 15 is used, but it fails for some years
472        // using our algorithms, e.g.: 1298 1391 1492 1553 1560.  That
473        // is, winterSolstice(1298) starts search at Dec 14 08:00:00
474        // PST 1298 with a final result of Dec 14 10:31:59 PST 1299.
475        double ms = daysToMillis(Grego::fieldsToDay(gyear, UCAL_DECEMBER, 1));
476
477        umtx_lock(&astroLock);
478        if(gChineseCalendarAstro == NULL) {
479            gChineseCalendarAstro = new CalendarAstronomer();
480            ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
481        }
482        gChineseCalendarAstro->setTime(ms);
483        UDate solarLong = gChineseCalendarAstro->getSunTime(CalendarAstronomer::WINTER_SOLSTICE(), TRUE);
484        umtx_unlock(&astroLock);
485
486        // Winter solstice is 270 degrees solar longitude aka Dongzhi
487        cacheValue = (int32_t)millisToDays(solarLong);
488        CalendarCache::put(&gChineseCalendarWinterSolsticeCache, gyear, cacheValue, status);
489    }
490    if(U_FAILURE(status)) {
491        cacheValue = 0;
492    }
493    return cacheValue;
494}
495
496/**
497 * Return the closest new moon to the given date, searching either
498 * forward or backward in time.
499 * @param days days after January 1, 1970 0:00 Asia/Shanghai
500 * @param after if true, search for a new moon on or after the given
501 * date; otherwise, search for a new moon before it
502 * @return days after January 1, 1970 0:00 Asia/Shanghai of the nearest
503 * new moon after or before <code>days</code>
504 */
505int32_t ChineseCalendar::newMoonNear(double days, UBool after) const {
506
507    umtx_lock(&astroLock);
508    if(gChineseCalendarAstro == NULL) {
509        gChineseCalendarAstro = new CalendarAstronomer();
510        ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
511    }
512    gChineseCalendarAstro->setTime(daysToMillis(days));
513    UDate newMoon = gChineseCalendarAstro->getMoonTime(CalendarAstronomer::NEW_MOON(), after);
514    umtx_unlock(&astroLock);
515
516    return (int32_t) millisToDays(newMoon);
517}
518
519/**
520 * Return the nearest integer number of synodic months between
521 * two dates.
522 * @param day1 days after January 1, 1970 0:00 Asia/Shanghai
523 * @param day2 days after January 1, 1970 0:00 Asia/Shanghai
524 * @return the nearest integer number of months between day1 and day2
525 */
526int32_t ChineseCalendar::synodicMonthsBetween(int32_t day1, int32_t day2) const {
527    double roundme = ((day2 - day1) / CalendarAstronomer::SYNODIC_MONTH);
528    return (int32_t) (roundme + (roundme >= 0 ? .5 : -.5));
529}
530
531/**
532 * Return the major solar term on or before a given date.  This
533 * will be an integer from 1..12, with 1 corresponding to 330 degrees,
534 * 2 to 0 degrees, 3 to 30 degrees,..., and 12 to 300 degrees.
535 * @param days days after January 1, 1970 0:00 Asia/Shanghai
536 */
537int32_t ChineseCalendar::majorSolarTerm(int32_t days) const {
538
539    umtx_lock(&astroLock);
540    if(gChineseCalendarAstro == NULL) {
541        gChineseCalendarAstro = new CalendarAstronomer();
542        ucln_i18n_registerCleanup(UCLN_I18N_CHINESE_CALENDAR, calendar_chinese_cleanup);
543    }
544    gChineseCalendarAstro->setTime(daysToMillis(days));
545    UDate solarLongitude = gChineseCalendarAstro->getSunLongitude();
546    umtx_unlock(&astroLock);
547
548    // Compute (floor(solarLongitude / (pi/6)) + 2) % 12
549    int32_t term = ( ((int32_t)(6 * solarLongitude / CalendarAstronomer::PI)) + 2 ) % 12;
550    if (term < 1) {
551        term += 12;
552    }
553    return term;
554}
555
556/**
557 * Return true if the given month lacks a major solar term.
558 * @param newMoon days after January 1, 1970 0:00 Asia/Shanghai of a new
559 * moon
560 */
561UBool ChineseCalendar::hasNoMajorSolarTerm(int32_t newMoon) const {
562    return majorSolarTerm(newMoon) ==
563        majorSolarTerm(newMoonNear(newMoon + SYNODIC_GAP, TRUE));
564}
565
566
567//------------------------------------------------------------------
568// Time to fields
569//------------------------------------------------------------------
570
571/**
572 * Return true if there is a leap month on or after month newMoon1 and
573 * at or before month newMoon2.
574 * @param newMoon1 days after January 1, 1970 0:00 Asia/Shanghai of a
575 * new moon
576 * @param newMoon2 days after January 1, 1970 0:00 Asia/Shanghai of a
577 * new moon
578 */
579UBool ChineseCalendar::isLeapMonthBetween(int32_t newMoon1, int32_t newMoon2) const {
580
581#ifdef U_DEBUG_CHNSECAL
582    // This is only needed to debug the timeOfAngle divergence bug.
583    // Remove this later. Liu 11/9/00
584    if (synodicMonthsBetween(newMoon1, newMoon2) >= 50) {
585        U_DEBUG_CHNSECAL_MSG((
586            "isLeapMonthBetween(%d, %d): Invalid parameters", newMoon1, newMoon2
587            ));
588    }
589#endif
590
591    return (newMoon2 >= newMoon1) &&
592        (isLeapMonthBetween(newMoon1, newMoonNear(newMoon2 - SYNODIC_GAP, FALSE)) ||
593         hasNoMajorSolarTerm(newMoon2));
594}
595
596/**
597 * Compute fields for the Chinese calendar system.  This method can
598 * either set all relevant fields, as required by
599 * <code>handleComputeFields()</code>, or it can just set the MONTH and
600 * IS_LEAP_MONTH fields, as required by
601 * <code>handleComputeMonthStart()</code>.
602 *
603 * <p>As a side effect, this method sets {@link #isLeapYear}.
604 * @param days days after January 1, 1970 0:00 Asia/Shanghai of the
605 * date to compute fields for
606 * @param gyear the Gregorian year of the given date
607 * @param gmonth the Gregorian month of the given date
608 * @param setAllFields if true, set the EXTENDED_YEAR, ERA, YEAR,
609 * DAY_OF_MONTH, and DAY_OF_YEAR fields.  In either case set the MONTH
610 * and IS_LEAP_MONTH fields.
611 */
612void ChineseCalendar::computeChineseFields(int32_t days, int32_t gyear, int32_t gmonth,
613                                  UBool setAllFields) {
614
615    // Find the winter solstices before and after the target date.
616    // These define the boundaries of this Chinese year, specifically,
617    // the position of month 11, which always contains the solstice.
618    // We want solsticeBefore <= date < solsticeAfter.
619    int32_t solsticeBefore;
620    int32_t solsticeAfter = winterSolstice(gyear);
621    if (days < solsticeAfter) {
622        solsticeBefore = winterSolstice(gyear - 1);
623    } else {
624        solsticeBefore = solsticeAfter;
625        solsticeAfter = winterSolstice(gyear + 1);
626    }
627
628    // Find the start of the month after month 11.  This will be either
629    // the prior month 12 or leap month 11 (very rare).  Also find the
630    // start of the following month 11.
631    int32_t firstMoon = newMoonNear(solsticeBefore + 1, TRUE);
632    int32_t lastMoon = newMoonNear(solsticeAfter + 1, FALSE);
633    int32_t thisMoon = newMoonNear(days + 1, FALSE); // Start of this month
634    // Note: isLeapYear is a member variable
635    isLeapYear = synodicMonthsBetween(firstMoon, lastMoon) == 12;
636
637    int32_t month = synodicMonthsBetween(firstMoon, thisMoon);
638    if (isLeapYear && isLeapMonthBetween(firstMoon, thisMoon)) {
639        month--;
640    }
641    if (month < 1) {
642        month += 12;
643    }
644
645    UBool isLeapMonth = isLeapYear &&
646        hasNoMajorSolarTerm(thisMoon) &&
647        !isLeapMonthBetween(firstMoon, newMoonNear(thisMoon - SYNODIC_GAP, FALSE));
648
649    internalSet(UCAL_MONTH, month-1); // Convert from 1-based to 0-based
650    internalSet(UCAL_IS_LEAP_MONTH, isLeapMonth?1:0);
651
652    if (setAllFields) {
653
654        int32_t year = gyear - CHINESE_EPOCH_YEAR;
655        if (month < 11 ||
656            gmonth >= UCAL_JULY) {
657            year++;
658        }
659        int32_t dayOfMonth = days - thisMoon + 1;
660
661        internalSet(UCAL_EXTENDED_YEAR, year);
662
663        // 0->0,60  1->1,1  60->1,60  61->2,1  etc.
664        int32_t yearOfCycle;
665        int32_t cycle = ClockMath::floorDivide(year - 1, 60, yearOfCycle);
666        internalSet(UCAL_ERA, cycle + 1);
667        internalSet(UCAL_YEAR, yearOfCycle + 1);
668
669        internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
670
671        // Days will be before the first new year we compute if this
672        // date is in month 11, leap 11, 12.  There is never a leap 12.
673        // New year computations are cached so this should be cheap in
674        // the long run.
675        int32_t theNewYear = newYear(gyear);
676        if (days < theNewYear) {
677            theNewYear = newYear(gyear-1);
678        }
679        internalSet(UCAL_DAY_OF_YEAR, days - theNewYear + 1);
680    }
681}
682
683
684//------------------------------------------------------------------
685// Fields to time
686//------------------------------------------------------------------
687
688/**
689 * Return the Chinese new year of the given Gregorian year.
690 * @param gyear a Gregorian year
691 * @return days after January 1, 1970 0:00 Asia/Shanghai of the
692 * Chinese new year of the given year (this will be a new moon)
693 */
694int32_t ChineseCalendar::newYear(int32_t gyear) const {
695    UErrorCode status = U_ZERO_ERROR;
696    int32_t cacheValue = CalendarCache::get(&gChineseCalendarNewYearCache, gyear, status);
697
698    if (cacheValue == 0) {
699
700        int32_t solsticeBefore= winterSolstice(gyear - 1);
701        int32_t solsticeAfter = winterSolstice(gyear);
702        int32_t newMoon1 = newMoonNear(solsticeBefore + 1, TRUE);
703        int32_t newMoon2 = newMoonNear(newMoon1 + SYNODIC_GAP, TRUE);
704        int32_t newMoon11 = newMoonNear(solsticeAfter + 1, FALSE);
705
706        if (synodicMonthsBetween(newMoon1, newMoon11) == 12 &&
707            (hasNoMajorSolarTerm(newMoon1) || hasNoMajorSolarTerm(newMoon2))) {
708            cacheValue = newMoonNear(newMoon2 + SYNODIC_GAP, TRUE);
709        } else {
710            cacheValue = newMoon2;
711        }
712
713        CalendarCache::put(&gChineseCalendarNewYearCache, gyear, cacheValue, status);
714    }
715    if(U_FAILURE(status)) {
716        cacheValue = 0;
717    }
718    return cacheValue;
719}
720
721/**
722 * Adjust this calendar to be delta months before or after a given
723 * start position, pinning the day of month if necessary.  The start
724 * position is given as a local days number for the start of the month
725 * and a day-of-month.  Used by add() and roll().
726 * @param newMoon the local days of the first day of the month of the
727 * start position (days after January 1, 1970 0:00 Asia/Shanghai)
728 * @param dom the 1-based day-of-month of the start position
729 * @param delta the number of months to move forward or backward from
730 * the start position
731 */
732void ChineseCalendar::offsetMonth(int32_t newMoon, int32_t dom, int32_t delta) {
733    UErrorCode status = U_ZERO_ERROR;
734
735    // Move to the middle of the month before our target month.
736    newMoon += (int32_t) (CalendarAstronomer::SYNODIC_MONTH * (delta - 0.5));
737
738    // Search forward to the target month's new moon
739    newMoon = newMoonNear(newMoon, TRUE);
740
741    // Find the target dom
742    int32_t jd = newMoon + kEpochStartAsJulianDay - 1 + dom;
743
744    // Pin the dom.  In this calendar all months are 29 or 30 days
745    // so pinning just means handling dom 30.
746    if (dom > 29) {
747        set(UCAL_JULIAN_DAY, jd-1);
748        // TODO Fix this.  We really shouldn't ever have to
749        // explicitly call complete().  This is either a bug in
750        // this method, in ChineseCalendar, or in
751        // Calendar.getActualMaximum().  I suspect the last.
752        complete(status);
753        if (U_FAILURE(status)) return;
754        if (getActualMaximum(UCAL_DAY_OF_MONTH, status) >= dom) {
755            if (U_FAILURE(status)) return;
756            set(UCAL_JULIAN_DAY, jd);
757        }
758    } else {
759        set(UCAL_JULIAN_DAY, jd);
760    }
761}
762
763
764UBool
765ChineseCalendar::inDaylightTime(UErrorCode& status) const
766{
767    // copied from GregorianCalendar
768    if (U_FAILURE(status) || !getTimeZone().useDaylightTime())
769        return FALSE;
770
771    // Force an update of the state of the Calendar.
772    ((ChineseCalendar*)this)->complete(status); // cast away const
773
774    return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE);
775}
776
777// default century
778const UDate     ChineseCalendar::fgSystemDefaultCentury        = DBL_MIN;
779const int32_t   ChineseCalendar::fgSystemDefaultCenturyYear    = -1;
780
781UDate           ChineseCalendar::fgSystemDefaultCenturyStart       = DBL_MIN;
782int32_t         ChineseCalendar::fgSystemDefaultCenturyStartYear   = -1;
783
784
785UBool ChineseCalendar::haveDefaultCentury() const
786{
787    return TRUE;
788}
789
790UDate ChineseCalendar::defaultCenturyStart() const
791{
792    return internalGetDefaultCenturyStart();
793}
794
795int32_t ChineseCalendar::defaultCenturyStartYear() const
796{
797    return internalGetDefaultCenturyStartYear();
798}
799
800UDate
801ChineseCalendar::internalGetDefaultCenturyStart() const
802{
803    // lazy-evaluate systemDefaultCenturyStart
804    UBool needsUpdate;
805    UMTX_CHECK(NULL, (fgSystemDefaultCenturyStart == fgSystemDefaultCentury), needsUpdate);
806
807    if (needsUpdate) {
808        initializeSystemDefaultCentury();
809    }
810
811    // use defaultCenturyStart unless it's the flag value;
812    // then use systemDefaultCenturyStart
813
814    return fgSystemDefaultCenturyStart;
815}
816
817int32_t
818ChineseCalendar::internalGetDefaultCenturyStartYear() const
819{
820    // lazy-evaluate systemDefaultCenturyStartYear
821    UBool needsUpdate;
822    UMTX_CHECK(NULL, (fgSystemDefaultCenturyStart == fgSystemDefaultCentury), needsUpdate);
823
824    if (needsUpdate) {
825        initializeSystemDefaultCentury();
826    }
827
828    // use defaultCenturyStart unless it's the flag value;
829    // then use systemDefaultCenturyStartYear
830
831    return    fgSystemDefaultCenturyStartYear;
832}
833
834void
835ChineseCalendar::initializeSystemDefaultCentury()
836{
837    // initialize systemDefaultCentury and systemDefaultCenturyYear based
838    // on the current time.  They'll be set to 80 years before
839    // the current time.
840    UErrorCode status = U_ZERO_ERROR;
841    ChineseCalendar calendar(Locale("@calendar=chinese"),status);
842    if (U_SUCCESS(status))
843    {
844        calendar.setTime(Calendar::getNow(), status);
845        calendar.add(UCAL_YEAR, -80, status);
846        UDate    newStart =  calendar.getTime(status);
847        int32_t  newYear  =  calendar.get(UCAL_YEAR, status);
848        umtx_lock(NULL);
849        if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury)
850        {
851            fgSystemDefaultCenturyStartYear = newYear;
852            fgSystemDefaultCenturyStart = newStart;
853        }
854        umtx_unlock(NULL);
855    }
856    // We have no recourse upon failure unless we want to propagate the failure
857    // out.
858}
859
860UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChineseCalendar)
861
862U_NAMESPACE_END
863
864#endif
865
866