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