]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/chnsecal.cpp
ICU-461.18.tar.gz
[apple/icu.git] / icuSources / i18n / chnsecal.cpp
CommitLineData
374ca955
A
1/*
2 ******************************************************************************
46f4442e
A
3 * Copyright (C) 2007-2008, International Business Machines Corporation
4 * and others. All Rights Reserved.
374ca955
A
5 ******************************************************************************
6 *
46f4442e 7 * File CHNSECAL.CPP
374ca955 8 *
46f4442e
A
9 * Modification History:
10 *
11 * Date Name Description
12 * 9/18/2007 ajmacher ported from java ChineseCalendar
13 *****************************************************************************
374ca955 14 */
46f4442e 15
374ca955
A
16#include "chnsecal.h"
17
46f4442e
A
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;
729e4ab9 294 eyear += (int32_t)ClockMath::floorDivide(m, 12.0, m);
46f4442e
A
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) {
729e4ab9 449 return ClockMath::floorDivide(millis + CHINA_OFFSET, kOneDay);
46f4442e
A
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;
729e4ab9 665 int32_t cycle = ClockMath::floorDivide(year - 1, 60, yearOfCycle);
46f4442e
A
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.
729e4ab9
A
840 UErrorCode status = U_ZERO_ERROR;
841 ChineseCalendar calendar(Locale("@calendar=chinese"),status);
842 if (U_SUCCESS(status))
46f4442e 843 {
729e4ab9
A
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)
46f4442e 850 {
729e4ab9
A
851 fgSystemDefaultCenturyStartYear = newYear;
852 fgSystemDefaultCenturyStart = newStart;
46f4442e 853 }
729e4ab9 854 umtx_unlock(NULL);
46f4442e 855 }
729e4ab9
A
856 // We have no recourse upon failure unless we want to propagate the failure
857 // out.
46f4442e
A
858}
859
860UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChineseCalendar)
861
862U_NAMESPACE_END
863
864#endif
374ca955 865