1 /*
2 ******************************************************************************
3 * Copyright (C) 2003-2015, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 ******************************************************************************
6 *
7 * File ISLAMCAL.H
8 *
9 * Modification History:
10 *
11 *   Date        Name        Description
12 *   10/14/2003  srl         ported from java IslamicCalendar
13 *****************************************************************************
14 */
15 
16 #include "islamcal.h"
17 
18 #if !UCONFIG_NO_FORMATTING
19 
20 #include "umutex.h"
21 #include <float.h>
22 #include "gregoimp.h" // Math
23 #include "astro.h" // CalendarAstronomer
24 #include "uhash.h"
25 #include "ucln_in.h"
26 #include "uassert.h"
27 
28 static const UDate HIJRA_MILLIS = -42521587200000.0;    // 7/16/622 AD 00:00
29 
30 // Debugging
31 #ifdef U_DEBUG_ISLAMCAL
32 # include <stdio.h>
33 # include <stdarg.h>
debug_islamcal_loc(const char * f,int32_t l)34 static void debug_islamcal_loc(const char *f, int32_t l)
35 {
36     fprintf(stderr, "%s:%d: ", f, l);
37 }
38 
debug_islamcal_msg(const char * pat,...)39 static void debug_islamcal_msg(const char *pat, ...)
40 {
41     va_list ap;
42     va_start(ap, pat);
43     vfprintf(stderr, pat, ap);
44     fflush(stderr);
45 }
46 // must use double parens, i.e.:  U_DEBUG_ISLAMCAL_MSG(("four is: %d",4));
47 #define U_DEBUG_ISLAMCAL_MSG(x) {debug_islamcal_loc(__FILE__,__LINE__);debug_islamcal_msg x;}
48 #else
49 #define U_DEBUG_ISLAMCAL_MSG(x)
50 #endif
51 
52 
53 // --- The cache --
54 // cache of months
55 static UMutex astroLock = U_MUTEX_INITIALIZER;  // pod bay door lock
56 static icu::CalendarCache *gMonthCache = NULL;
57 static icu::CalendarAstronomer *gIslamicCalendarAstro = NULL;
58 
59 U_CDECL_BEGIN
calendar_islamic_cleanup(void)60 static UBool calendar_islamic_cleanup(void) {
61     if (gMonthCache) {
62         delete gMonthCache;
63         gMonthCache = NULL;
64     }
65     if (gIslamicCalendarAstro) {
66         delete gIslamicCalendarAstro;
67         gIslamicCalendarAstro = NULL;
68     }
69     return TRUE;
70 }
71 U_CDECL_END
72 
73 U_NAMESPACE_BEGIN
74 
75 // Implementation of the IslamicCalendar class
76 
77 /**
78  * Friday EPOC
79  */
80 static const int32_t CIVIL_EPOC = 1948440; // CE 622 July 16 Friday (Julian calendar) / CE 622 July 19 (Gregorian calendar)
81 
82 /**
83   * Thursday EPOC
84   */
85 static const int32_t ASTRONOMICAL_EPOC = 1948439; // CE 622 July 15 Thursday (Julian calendar)
86 
87 
88 static const int32_t UMALQURA_YEAR_START = 1300;
89 static const int32_t UMALQURA_YEAR_END = 1600;
90 
91 static const int UMALQURA_MONTHLENGTH[] = {
92     //* 1300 -1302 */ "1010 1010 1010", "1101 0101 0100", "1110 1100 1001",
93                             0x0AAA,           0x0D54,           0x0EC9,
94     //* 1303 -1307 */ "0110 1101 0100", "0110 1110 1010", "0011 0110 1100", "1010 1010 1101", "0101 0101 0101",
95                             0x06D4,           0x06EA,           0x036C,           0x0AAD,           0x0555,
96     //* 1308 -1312 */ "0110 1010 1001", "0111 1001 0010", "1011 1010 1001", "0101 1101 0100", "1010 1101 1010",
97                             0x06A9,           0x0792,           0x0BA9,           0x05D4,           0x0ADA,
98     //* 1313 -1317 */ "0101 0101 1100", "1101 0010 1101", "0110 1001 0101", "0111 0100 1010", "1011 0101 0100",
99                             0x055C,           0x0D2D,           0x0695,           0x074A,           0x0B54,
100     //* 1318 -1322 */ "1011 0110 1010", "0101 1010 1101", "0100 1010 1110", "1010 0100 1111", "0101 0001 0111",
101                             0x0B6A,           0x05AD,           0x04AE,           0x0A4F,           0x0517,
102     //* 1323 -1327 */ "0110 1000 1011", "0110 1010 0101", "1010 1101 0101", "0010 1101 0110", "1001 0101 1011",
103                             0x068B,           0x06A5,           0x0AD5,           0x02D6,           0x095B,
104     //* 1328 -1332 */ "0100 1001 1101", "1010 0100 1101", "1101 0010 0110", "1101 1001 0101", "0101 1010 1100",
105                             0x049D,           0x0A4D,           0x0D26,           0x0D95,           0x05AC,
106     //* 1333 -1337 */ "1001 1011 0110", "0010 1011 1010", "1010 0101 1011", "0101 0010 1011", "1010 1001 0101",
107                             0x09B6,           0x02BA,           0x0A5B,           0x052B,           0x0A95,
108     //* 1338 -1342 */ "0110 1100 1010", "1010 1110 1001", "0010 1111 0100", "1001 0111 0110", "0010 1011 0110",
109                             0x06CA,           0x0AE9,           0x02F4,           0x0976,           0x02B6,
110     //* 1343 -1347 */ "1001 0101 0110", "1010 1100 1010", "1011 1010 0100", "1011 1101 0010", "0101 1101 1001",
111                             0x0956,           0x0ACA,           0x0BA4,           0x0BD2,           0x05D9,
112     //* 1348 -1352 */ "0010 1101 1100", "1001 0110 1101", "0101 0100 1101", "1010 1010 0101", "1011 0101 0010",
113                             0x02DC,           0x096D,           0x054D,           0x0AA5,           0x0B52,
114     //* 1353 -1357 */ "1011 1010 0101", "0101 1011 0100", "1001 1011 0110", "0101 0101 0111", "0010 1001 0111",
115                             0x0BA5,           0x05B4,           0x09B6,           0x0557,           0x0297,
116     //* 1358 -1362 */ "0101 0100 1011", "0110 1010 0011", "0111 0101 0010", "1011 0110 0101", "0101 0110 1010",
117                             0x054B,           0x06A3,           0x0752,           0x0B65,           0x056A,
118     //* 1363 -1367 */ "1010 1010 1011", "0101 0010 1011", "1100 1001 0101", "1101 0100 1010", "1101 1010 0101",
119                             0x0AAB,           0x052B,           0x0C95,           0x0D4A,           0x0DA5,
120     //* 1368 -1372 */ "0101 1100 1010", "1010 1101 0110", "1001 0101 0111", "0100 1010 1011", "1001 0100 1011",
121                             0x05CA,           0x0AD6,           0x0957,           0x04AB,           0x094B,
122     //* 1373 -1377 */ "1010 1010 0101", "1011 0101 0010", "1011 0110 1010", "0101 0111 0101", "0010 0111 0110",
123                             0x0AA5,           0x0B52,           0x0B6A,           0x0575,           0x0276,
124     //* 1378 -1382 */ "1000 1011 0111", "0100 0101 1011", "0101 0101 0101", "0101 1010 1001", "0101 1011 0100",
125                             0x08B7,           0x045B,           0x0555,           0x05A9,           0x05B4,
126     //* 1383 -1387 */ "1001 1101 1010", "0100 1101 1101", "0010 0110 1110", "1001 0011 0110", "1010 1010 1010",
127                             0x09DA,           0x04DD,           0x026E,           0x0936,           0x0AAA,
128     //* 1388 -1392 */ "1101 0101 0100", "1101 1011 0010", "0101 1101 0101", "0010 1101 1010", "1001 0101 1011",
129                             0x0D54,           0x0DB2,           0x05D5,           0x02DA,           0x095B,
130     //* 1393 -1397 */ "0100 1010 1011", "1010 0101 0101", "1011 0100 1001", "1011 0110 0100", "1011 0111 0001",
131                             0x04AB,           0x0A55,           0x0B49,           0x0B64,           0x0B71,
132     //* 1398 -1402 */ "0101 1011 0100", "1010 1011 0101", "1010 0101 0101", "1101 0010 0101", "1110 1001 0010",
133                             0x05B4,           0x0AB5,           0x0A55,           0x0D25,           0x0E92,
134     //* 1403 -1407 */ "1110 1100 1001", "0110 1101 0100", "1010 1110 1001", "1001 0110 1011", "0100 1010 1011",
135                             0x0EC9,           0x06D4,           0x0AE9,           0x096B,           0x04AB,
136     //* 1408 -1412 */ "1010 1001 0011", "1101 0100 1001", "1101 1010 0100", "1101 1011 0010", "1010 1011 1001",
137                             0x0A93,           0x0D49,         0x0DA4,           0x0DB2,           0x0AB9,
138     //* 1413 -1417 */ "0100 1011 1010", "1010 0101 1011", "0101 0010 1011", "1010 1001 0101", "1011 0010 1010",
139                             0x04BA,           0x0A5B,           0x052B,           0x0A95,           0x0B2A,
140     //* 1418 -1422 */ "1011 0101 0101", "0101 0101 1100", "0100 1011 1101", "0010 0011 1101", "1001 0001 1101",
141                             0x0B55,           0x055C,           0x04BD,           0x023D,           0x091D,
142     //* 1423 -1427 */ "1010 1001 0101", "1011 0100 1010", "1011 0101 1010", "0101 0110 1101", "0010 1011 0110",
143                             0x0A95,           0x0B4A,           0x0B5A,           0x056D,           0x02B6,
144     //* 1428 -1432 */ "1001 0011 1011", "0100 1001 1011", "0110 0101 0101", "0110 1010 1001", "0111 0101 0100",
145                             0x093B,           0x049B,           0x0655,           0x06A9,           0x0754,
146     //* 1433 -1437 */ "1011 0110 1010", "0101 0110 1100", "1010 1010 1101", "0101 0101 0101", "1011 0010 1001",
147                             0x0B6A,           0x056C,           0x0AAD,           0x0555,           0x0B29,
148     //* 1438 -1442 */ "1011 1001 0010", "1011 1010 1001", "0101 1101 0100", "1010 1101 1010", "0101 0101 1010",
149                             0x0B92,           0x0BA9,           0x05D4,           0x0ADA,           0x055A,
150     //* 1443 -1447 */ "1010 1010 1011", "0101 1001 0101", "0111 0100 1001", "0111 0110 0100", "1011 1010 1010",
151                             0x0AAB,           0x0595,           0x0749,           0x0764,           0x0BAA,
152     //* 1448 -1452 */ "0101 1011 0101", "0010 1011 0110", "1010 0101 0110", "1110 0100 1101", "1011 0010 0101",
153                             0x05B5,           0x02B6,           0x0A56,           0x0E4D,           0x0B25,
154     //* 1453 -1457 */ "1011 0101 0010", "1011 0110 1010", "0101 1010 1101", "0010 1010 1110", "1001 0010 1111",
155                             0x0B52,           0x0B6A,           0x05AD,           0x02AE,           0x092F,
156     //* 1458 -1462 */ "0100 1001 0111", "0110 0100 1011", "0110 1010 0101", "0110 1010 1100", "1010 1101 0110",
157                             0x0497,           0x064B,           0x06A5,           0x06AC,           0x0AD6,
158     //* 1463 -1467 */ "0101 0101 1101", "0100 1001 1101", "1010 0100 1101", "1101 0001 0110", "1101 1001 0101",
159                             0x055D,           0x049D,           0x0A4D,           0x0D16,           0x0D95,
160     //* 1468 -1472 */ "0101 1010 1010", "0101 1011 0101", "0010 1101 1010", "1001 0101 1011", "0100 1010 1101",
161                             0x05AA,           0x05B5,           0x02DA,           0x095B,           0x04AD,
162     //* 1473 -1477 */ "0101 1001 0101", "0110 1100 1010", "0110 1110 0100", "1010 1110 1010", "0100 1111 0101",
163                             0x0595,           0x06CA,           0x06E4,           0x0AEA,           0x04F5,
164     //* 1478 -1482 */ "0010 1011 0110", "1001 0101 0110", "1010 1010 1010", "1011 0101 0100", "1011 1101 0010",
165                             0x02B6,           0x0956,           0x0AAA,           0x0B54,           0x0BD2,
166     //* 1483 -1487 */ "0101 1101 1001", "0010 1110 1010", "1001 0110 1101", "0100 1010 1101", "1010 1001 0101",
167                             0x05D9,           0x02EA,           0x096D,           0x04AD,           0x0A95,
168     //* 1488 -1492 */ "1011 0100 1010", "1011 1010 0101", "0101 1011 0010", "1001 1011 0101", "0100 1101 0110",
169                             0x0B4A,           0x0BA5,           0x05B2,           0x09B5,           0x04D6,
170     //* 1493 -1497 */ "1010 1001 0111", "0101 0100 0111", "0110 1001 0011", "0111 0100 1001", "1011 0101 0101",
171                             0x0A97,           0x0547,           0x0693,           0x0749,           0x0B55,
172     //* 1498 -1508 */ "0101 0110 1010", "1010 0110 1011", "0101 0010 1011", "1010 1000 1011", "1101 0100 0110", "1101 1010 0011", "0101 1100 1010", "1010 1101 0110", "0100 1101 1011", "0010 0110 1011", "1001 0100 1011",
173                             0x056A,           0x0A6B,           0x052B,           0x0A8B,           0x0D46,           0x0DA3,           0x05CA,           0x0AD6,           0x04DB,           0x026B,           0x094B,
174     //* 1509 -1519 */ "1010 1010 0101", "1011 0101 0010", "1011 0110 1001", "0101 0111 0101", "0001 0111 0110", "1000 1011 0111", "0010 0101 1011", "0101 0010 1011", "0101 0110 0101", "0101 1011 0100", "1001 1101 1010",
175                             0x0AA5,           0x0B52,           0x0B69,           0x0575,           0x0176,           0x08B7,           0x025B,           0x052B,           0x0565,           0x05B4,           0x09DA,
176     //* 1520 -1530 */ "0100 1110 1101", "0001 0110 1101", "1000 1011 0110", "1010 1010 0110", "1101 0101 0010", "1101 1010 1001", "0101 1101 0100", "1010 1101 1010", "1001 0101 1011", "0100 1010 1011", "0110 0101 0011",
177                             0x04ED,           0x016D,           0x08B6,           0x0AA6,           0x0D52,           0x0DA9,           0x05D4,           0x0ADA,           0x095B,           0x04AB,           0x0653,
178     //* 1531 -1541 */ "0111 0010 1001", "0111 0110 0010", "1011 1010 1001", "0101 1011 0010", "1010 1011 0101", "0101 0101 0101", "1011 0010 0101", "1101 1001 0010", "1110 1100 1001", "0110 1101 0010", "1010 1110 1001",
179                             0x0729,           0x0762,           0x0BA9,           0x05B2,           0x0AB5,           0x0555,           0x0B25,           0x0D92,           0x0EC9,           0x06D2,           0x0AE9,
180     //* 1542 -1552 */ "0101 0110 1011", "0100 1010 1011", "1010 0101 0101", "1101 0010 1001", "1101 0101 0100", "1101 1010 1010", "1001 1011 0101", "0100 1011 1010", "1010 0011 1011", "0100 1001 1011", "1010 0100 1101",
181                             0x056B,           0x04AB,           0x0A55,           0x0D29,           0x0D54,           0x0DAA,           0x09B5,           0x04BA,           0x0A3B,           0x049B,           0x0A4D,
182     //* 1553 -1563 */ "1010 1010 1010", "1010 1101 0101", "0010 1101 1010", "1001 0101 1101", "0100 0101 1110", "1010 0010 1110", "1100 1001 1010", "1101 0101 0101", "0110 1011 0010", "0110 1011 1001", "0100 1011 1010",
183                             0x0AAA,           0x0AD5,           0x02DA,           0x095D,           0x045E,           0x0A2E,           0x0C9A,           0x0D55,           0x06B2,           0x06B9,           0x04BA,
184     //* 1564 -1574 */ "1010 0101 1101", "0101 0010 1101", "1010 1001 0101", "1011 0101 0010", "1011 1010 1000", "1011 1011 0100", "0101 1011 1001", "0010 1101 1010", "1001 0101 1010", "1011 0100 1010", "1101 1010 0100",
185                             0x0A5D,           0x052D,           0x0A95,           0x0B52,           0x0BA8,           0x0BB4,           0x05B9,           0x02DA,           0x095A,           0x0B4A,           0x0DA4,
186     //* 1575 -1585 */ "1110 1101 0001", "0110 1110 1000", "1011 0110 1010", "0101 0110 1101", "0101 0011 0101", "0110 1001 0101", "1101 0100 1010", "1101 1010 1000", "1101 1101 0100", "0110 1101 1010", "0101 0101 1011",
187                             0x0ED1,           0x06E8,           0x0B6A,           0x056D,           0x0535,           0x0695,           0x0D4A,           0x0DA8,           0x0DD4,           0x06DA,           0x055B,
188     //* 1586 -1596 */ "0010 1001 1101", "0110 0010 1011", "1011 0001 0101", "1011 0100 1010", "1011 1001 0101", "0101 1010 1010", "1010 1010 1110", "1001 0010 1110", "1100 1000 1111", "0101 0010 0111", "0110 1001 0101",
189                             0x029D,           0x062B,           0x0B15,           0x0B4A,           0x0B95,           0x05AA,           0x0AAE,           0x092E,           0x0C8F,           0x0527,           0x0695,
190     //* 1597 -1600 */ "0110 1010 1010", "1010 1101 0110", "0101 0101 1101", "0010 1001 1101", };
191                             0x06AA,           0x0AD6,           0x055D,           0x029D
192 };
193 
getUmalqura_MonthLength(int32_t y,int32_t m)194 int32_t getUmalqura_MonthLength(int32_t y, int32_t m) {
195     int32_t mask = (int32_t) (0x01 << (11 - m));    // set mask for bit corresponding to month
196     if((UMALQURA_MONTHLENGTH[y] & mask) == 0 )
197         return 29;
198     else
199         return 30;
200 
201 }
202 
203 //-------------------------------------------------------------------------
204 // Constructors...
205 //-------------------------------------------------------------------------
206 
getType() const207 const char *IslamicCalendar::getType() const {
208     const char *sType = NULL;
209 
210     switch (cType) {
211     case CIVIL:
212         sType = "islamic-civil";
213         break;
214     case ASTRONOMICAL:
215         sType = "islamic";
216         break;
217     case TBLA:
218         sType = "islamic-tbla";
219         break;
220     case UMALQURA:
221         sType = "islamic-umalqura";
222         break;
223     default:
224         U_ASSERT(false); // out of range
225         sType = "islamic";  // "islamic" is used as the generic type
226         break;
227     }
228     return sType;
229 }
230 
clone() const231 Calendar* IslamicCalendar::clone() const {
232     return new IslamicCalendar(*this);
233 }
234 
IslamicCalendar(const Locale & aLocale,UErrorCode & success,ECalculationType type)235 IslamicCalendar::IslamicCalendar(const Locale& aLocale, UErrorCode& success, ECalculationType type)
236 :   Calendar(TimeZone::createDefault(), aLocale, success),
237 cType(type)
238 {
239     setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
240 }
241 
IslamicCalendar(const IslamicCalendar & other)242 IslamicCalendar::IslamicCalendar(const IslamicCalendar& other) : Calendar(other), cType(other.cType) {
243 }
244 
~IslamicCalendar()245 IslamicCalendar::~IslamicCalendar()
246 {
247 }
248 
setCalculationType(ECalculationType type,UErrorCode & status)249 void IslamicCalendar::setCalculationType(ECalculationType type, UErrorCode &status)
250 {
251     if (cType != type) {
252         // The fields of the calendar will become invalid, because the calendar
253         // rules are different
254         UDate m = getTimeInMillis(status);
255         cType = type;
256         clear();
257         setTimeInMillis(m, status);
258     }
259 }
260 
261 /**
262 * Returns <code>true</code> if this object is using the fixed-cycle civil
263 * calendar, or <code>false</code> if using the religious, astronomical
264 * calendar.
265 * @draft ICU 2.4
266 */
isCivil()267 UBool IslamicCalendar::isCivil() {
268     return (cType == CIVIL);
269 }
270 
271 //-------------------------------------------------------------------------
272 // Minimum / Maximum access functions
273 //-------------------------------------------------------------------------
274 
275 // Note: Current IslamicCalendar implementation does not work
276 // well with negative years.
277 
278 // TODO: In some cases the current ICU Islamic calendar implementation shows
279 // a month as having 31 days. Since date parsing now uses range checks based
280 // on the table below, we need to change the range for last day of month to
281 // include 31 as a workaround until the implementation is fixed.
282 static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
283     // Minimum  Greatest    Least  Maximum
284     //           Minimum  Maximum
285     {        0,        0,        0,        0}, // ERA
286     {        1,        1,  5000000,  5000000}, // YEAR
287     {        0,        0,       11,       11}, // MONTH
288     {        1,        1,       50,       51}, // WEEK_OF_YEAR
289     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
290     {        1,        1,       29,       31}, // DAY_OF_MONTH - 31 to workaround for cal implementation bug, should be 30
291     {        1,        1,      354,      355}, // DAY_OF_YEAR
292     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
293     {       -1,       -1,        5,        5}, // DAY_OF_WEEK_IN_MONTH
294     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
295     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
296     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
297     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
298     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
299     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
300     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
301     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
302     {        1,        1,  5000000,  5000000}, // YEAR_WOY
303     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
304     {        1,        1,  5000000,  5000000}, // EXTENDED_YEAR
305     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
306     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
307     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
308 };
309 
310 /**
311 * @draft ICU 2.4
312 */
handleGetLimit(UCalendarDateFields field,ELimitType limitType) const313 int32_t IslamicCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
314     return LIMITS[field][limitType];
315 }
316 
317 //-------------------------------------------------------------------------
318 // Assorted calculation utilities
319 //
320 
321 // we could compress this down more if we need to
322 static const int8_t umAlQuraYrStartEstimateFix[] = {
323      0,  0, -1,  0, -1,  0,  0,  0,  0,  0, // 1300..
324     -1,  0,  0,  0,  0,  0,  0,  0, -1,  0, // 1310..
325      1,  0,  1,  1,  0,  0,  0,  0,  1,  0, // 1320..
326      0,  0,  0,  0,  0,  0,  1,  0,  0,  0, // 1330..
327      0,  0,  1,  0,  0, -1, -1,  0,  0,  0, // 1340..
328      1,  0,  0, -1,  0,  0,  0,  1,  1,  0, // 1350..
329      0,  0,  0,  0,  0,  0,  0, -1,  0,  0, // 1360..
330      0,  1,  1,  0,  0, -1,  0,  1,  0,  1, // 1370..
331      1,  0,  0, -1,  0,  1,  0,  0,  0, -1, // 1380..
332      0,  1,  0,  1,  0,  0,  0, -1,  0,  0, // 1390..
333      0,  0, -1, -1,  0, -1,  0,  1,  0,  0, // 1400..
334      0, -1,  0,  0,  0,  1,  0,  0,  0,  0, // 1410..
335      0,  1,  0,  0, -1, -1,  0,  0,  0,  1, // 1420..
336      0,  0, -1, -1,  0, -1,  0,  0, -1, -1, // 1430..
337      0, -1,  0, -1,  0,  0, -1, -1,  0,  0, // 1440..
338      0,  0,  0,  0, -1,  0,  1,  0,  1,  1, // 1450..
339      0,  0, -1,  0,  1,  0,  0,  0,  0,  0, // 1460..
340      1,  0,  1,  0,  0,  0, -1,  0,  1,  0, // 1470..
341      0, -1, -1,  0,  0,  0,  1,  0,  0,  0, // 1480..
342      0,  0,  0,  0,  1,  0,  0,  0,  0,  0, // 1490..
343      1,  0,  0, -1,  0,  0,  0,  1,  1,  0, // 1500..
344      0, -1,  0,  1,  0,  1,  1,  0,  0,  0, // 1510..
345      0,  1,  0,  0,  0, -1,  0,  0,  0,  1, // 1520..
346      0,  0,  0, -1,  0,  0,  0,  0,  0, -1, // 1530..
347      0, -1,  0,  1,  0,  0,  0, -1,  0,  1, // 1540..
348      0,  1,  0,  0,  0,  0,  0,  1,  0,  0, // 1550..
349     -1,  0,  0,  0,  0,  1,  0,  0,  0, -1, // 1560..
350      0,  0,  0,  0, -1, -1,  0, -1,  0,  1, // 1570..
351      0,  0, -1, -1,  0,  0,  1,  1,  0,  0, // 1580..
352     -1,  0,  0,  0,  0,  1,  0,  0,  0,  0, // 1590..
353      1 // 1600
354 };
355 
356 /**
357 * Determine whether a year is a leap year in the Islamic civil calendar
358 */
civilLeapYear(int32_t year)359 UBool IslamicCalendar::civilLeapYear(int32_t year)
360 {
361     return (14 + 11 * year) % 30 < 11;
362 }
363 
364 /**
365 * Return the day # on which the given year starts.  Days are counted
366 * from the Hijri epoch, origin 0.
367 */
yearStart(int32_t year) const368 int32_t IslamicCalendar::yearStart(int32_t year) const{
369     if (cType == CIVIL || cType == TBLA ||
370         (cType == UMALQURA && (year < UMALQURA_YEAR_START || year > UMALQURA_YEAR_END)))
371     {
372         return (year-1)*354 + ClockMath::floorDivide((3+11*year),30);
373     } else if(cType==ASTRONOMICAL){
374         return trueMonthStart(12*(year-1));
375     } else {
376         year -= UMALQURA_YEAR_START;
377         // rounded least-squares fit of the dates previously calculated from UMALQURA_MONTHLENGTH iteration
378         int32_t yrStartLinearEstimate = (int32_t)((354.36720 * (double)year) + 460322.05 + 0.5);
379         // need a slight correction to some
380         return yrStartLinearEstimate + umAlQuraYrStartEstimateFix[year];
381     }
382 }
383 
384 /**
385 * Return the day # on which the given month starts.  Days are counted
386 * from the Hijri epoch, origin 0.
387 *
388 * @param year  The hijri year
389 * @param month The hijri month, 0-based (assumed to be in range 0..11)
390 */
monthStart(int32_t year,int32_t month) const391 int32_t IslamicCalendar::monthStart(int32_t year, int32_t month) const {
392     if (cType == CIVIL || cType == TBLA) {
393         // This does not handle months out of the range 0..11
394         return (int32_t)uprv_ceil(29.5*month)
395             + (year-1)*354 + (int32_t)ClockMath::floorDivide((3+11*year),30);
396     } else if(cType==ASTRONOMICAL){
397         return trueMonthStart(12*(year-1) + month);
398     } else {
399         int32_t ms = yearStart(year);
400         for(int i=0; i< month; i++){
401             ms+= handleGetMonthLength(year, i);
402         }
403         return ms;
404     }
405 }
406 
407 /**
408 * Find the day number on which a particular month of the true/lunar
409 * Islamic calendar starts.
410 *
411 * @param month The month in question, origin 0 from the Hijri epoch
412 *
413 * @return The day number on which the given month starts.
414 */
trueMonthStart(int32_t month) const415 int32_t IslamicCalendar::trueMonthStart(int32_t month) const
416 {
417     UErrorCode status = U_ZERO_ERROR;
418     int32_t start = CalendarCache::get(&gMonthCache, month, status);
419 
420     if (start==0) {
421         // Make a guess at when the month started, using the average length
422         UDate origin = HIJRA_MILLIS
423             + uprv_floor(month * CalendarAstronomer::SYNODIC_MONTH) * kOneDay;
424 
425         // moonAge will fail due to memory allocation error
426         double age = moonAge(origin, status);
427         if (U_FAILURE(status)) {
428             goto trueMonthStartEnd;
429         }
430 
431         if (age >= 0) {
432             // The month has already started
433             do {
434                 origin -= kOneDay;
435                 age = moonAge(origin, status);
436                 if (U_FAILURE(status)) {
437                     goto trueMonthStartEnd;
438                 }
439             } while (age >= 0);
440         }
441         else {
442             // Preceding month has not ended yet.
443             do {
444                 origin += kOneDay;
445                 age = moonAge(origin, status);
446                 if (U_FAILURE(status)) {
447                     goto trueMonthStartEnd;
448                 }
449             } while (age < 0);
450         }
451         start = (int32_t)ClockMath::floorDivide((origin - HIJRA_MILLIS), (double)kOneDay) + 1;
452         CalendarCache::put(&gMonthCache, month, start, status);
453     }
454 trueMonthStartEnd :
455     if(U_FAILURE(status)) {
456         start = 0;
457     }
458     return start;
459 }
460 
461 /**
462 * Return the "age" of the moon at the given time; this is the difference
463 * in ecliptic latitude between the moon and the sun.  This method simply
464 * calls CalendarAstronomer.moonAge, converts to degrees,
465 * and adjusts the result to be in the range [-180, 180].
466 *
467 * @param time  The time at which the moon's age is desired,
468 *              in millis since 1/1/1970.
469 */
moonAge(UDate time,UErrorCode & status)470 double IslamicCalendar::moonAge(UDate time, UErrorCode &status)
471 {
472     double age = 0;
473 
474     umtx_lock(&astroLock);
475     if(gIslamicCalendarAstro == NULL) {
476         gIslamicCalendarAstro = new CalendarAstronomer();
477         if (gIslamicCalendarAstro == NULL) {
478             status = U_MEMORY_ALLOCATION_ERROR;
479             return age;
480         }
481         ucln_i18n_registerCleanup(UCLN_I18N_ISLAMIC_CALENDAR, calendar_islamic_cleanup);
482     }
483     gIslamicCalendarAstro->setTime(time);
484     age = gIslamicCalendarAstro->getMoonAge();
485     umtx_unlock(&astroLock);
486 
487     // Convert to degrees and normalize...
488     age = age * 180 / CalendarAstronomer::PI;
489     if (age > 180) {
490         age = age - 360;
491     }
492 
493     return age;
494 }
495 
496 //----------------------------------------------------------------------
497 // Calendar framework
498 //----------------------------------------------------------------------
499 
500 /**
501 * Return the length (in days) of the given month.
502 *
503 * @param year  The hijri year
504 * @param year  The hijri month, 0-based
505 * @draft ICU 2.4
506 */
handleGetMonthLength(int32_t extendedYear,int32_t month) const507 int32_t IslamicCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
508 
509     int32_t length = 0;
510 
511     if (cType == CIVIL || cType == TBLA ||
512         (cType == UMALQURA && (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END)) ) {
513         length = 29 + (month+1) % 2;
514         if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) {
515             length++;
516         }
517     } else if(cType == ASTRONOMICAL){
518         month = 12*(extendedYear-1) + month;
519         length =  trueMonthStart(month+1) - trueMonthStart(month) ;
520     } else {
521         length = getUmalqura_MonthLength(extendedYear - UMALQURA_YEAR_START, month);
522     }
523     return length;
524 }
525 
526 /**
527 * Return the number of days in the given Islamic year
528 * @draft ICU 2.4
529 */
handleGetYearLength(int32_t extendedYear) const530 int32_t IslamicCalendar::handleGetYearLength(int32_t extendedYear) const {
531     if (cType == CIVIL || cType == TBLA ||
532         (cType == UMALQURA && (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END)) ) {
533         return 354 + (civilLeapYear(extendedYear) ? 1 : 0);
534     } else if(cType == ASTRONOMICAL){
535         int32_t month = 12*(extendedYear-1);
536         return (trueMonthStart(month + 12) - trueMonthStart(month));
537     } else {
538         int len = 0;
539         for(int i=0; i<12; i++) {
540             len += handleGetMonthLength(extendedYear, i);
541         }
542         return len;
543     }
544 }
545 
546 //-------------------------------------------------------------------------
547 // Functions for converting from field values to milliseconds....
548 //-------------------------------------------------------------------------
549 
550 // Return JD of start of given month/year
551 // Calendar says:
552 // Get the Julian day of the day BEFORE the start of this year.
553 // If useMonth is true, get the day before the start of the month.
554 // Hence the -1
555 /**
556 * @draft ICU 2.4
557 */
handleComputeMonthStart(int32_t eyear,int32_t month,UBool) const558 int32_t IslamicCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */) const {
559     // This may be called by Calendar::handleComputeJulianDay with months out of the range
560     // 0..11. Need to handle that here since monthStart requires months in the range 0.11.
561     if (month > 11) {
562         eyear += (month / 12);
563         month %= 12;
564     } else if (month < 0) {
565         month++;
566         eyear += (month / 12) - 1;
567         month = (month % 12) + 11;
568     }
569     return monthStart(eyear, month) + ((cType == TBLA)? ASTRONOMICAL_EPOC: CIVIL_EPOC) - 1;
570 }
571 
572 //-------------------------------------------------------------------------
573 // Functions for converting from milliseconds to field values
574 //-------------------------------------------------------------------------
575 
576 /**
577 * @draft ICU 2.4
578 */
handleGetExtendedYear()579 int32_t IslamicCalendar::handleGetExtendedYear() {
580     int32_t year;
581     if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
582         year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
583     } else {
584         year = internalGet(UCAL_YEAR, 1); // Default to year 1
585     }
586     return year;
587 }
588 
589 /**
590 * Override Calendar to compute several fields specific to the Islamic
591 * calendar system.  These are:
592 *
593 * <ul><li>ERA
594 * <li>YEAR
595 * <li>MONTH
596 * <li>DAY_OF_MONTH
597 * <li>DAY_OF_YEAR
598 * <li>EXTENDED_YEAR</ul>
599 *
600 * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
601 * method is called. The getGregorianXxx() methods return Gregorian
602 * calendar equivalents for the given Julian day.
603 * @draft ICU 2.4
604 */
handleComputeFields(int32_t julianDay,UErrorCode & status)605 void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) {
606     int32_t year, month, dayOfMonth, dayOfYear;
607     int32_t startDate;
608     int32_t days = julianDay - CIVIL_EPOC;
609 
610     if (cType == CIVIL || cType == TBLA) {
611         if(cType == TBLA) {
612             days = julianDay - ASTRONOMICAL_EPOC;
613         }
614         // Use the civil calendar approximation, which is just arithmetic
615         year  = (int)ClockMath::floorDivide( (double)(30 * days + 10646) , 10631.0 );
616         month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 );
617         month = month<11?month:11;
618         startDate = monthStart(year, month);
619     } else if(cType == ASTRONOMICAL){
620         // Guess at the number of elapsed full months since the epoch
621         int32_t months = (int32_t)uprv_floor((double)days / CalendarAstronomer::SYNODIC_MONTH);
622 
623         startDate = (int32_t)uprv_floor(months * CalendarAstronomer::SYNODIC_MONTH);
624 
625         double age = moonAge(internalGetTime(), status);
626         if (U_FAILURE(status)) {
627             status = U_MEMORY_ALLOCATION_ERROR;
628             return;
629         }
630         if ( days - startDate >= 25 && age > 0) {
631             // If we're near the end of the month, assume next month and search backwards
632             months++;
633         }
634 
635         // Find out the last time that the new moon was actually visible at this longitude
636         // This returns midnight the night that the moon was visible at sunset.
637         while ((startDate = trueMonthStart(months)) > days) {
638             // If it was after the date in question, back up a month and try again
639             months--;
640         }
641 
642         year = months / 12 + 1;
643         month = months % 12;
644     } else if(cType == UMALQURA) {
645         int32_t umalquraStartdays = yearStart(UMALQURA_YEAR_START) ;
646         if( days < umalquraStartdays){
647                 //Use Civil calculation
648                 year  = (int)ClockMath::floorDivide( (double)(30 * days + 10646) , 10631.0 );
649                 month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 );
650                 month = month<11?month:11;
651                 startDate = monthStart(year, month);
652             }else{
653                 int y =UMALQURA_YEAR_START-1, m =0;
654                 long d = 1;
655                 while(d > 0){
656                     y++;
657                     d = days - yearStart(y) +1;
658                     if(d == handleGetYearLength(y)){
659                         m=11;
660                         break;
661                     }else if(d < handleGetYearLength(y) ){
662                         int monthLen = handleGetMonthLength(y, m);
663                         m=0;
664                         while(d > monthLen){
665                             d -= monthLen;
666                             m++;
667                             monthLen = handleGetMonthLength(y, m);
668                         }
669                         break;
670                     }
671                 }
672                 year = y;
673                 month = m;
674             }
675     } else { // invalid 'civil'
676       U_ASSERT(false); // should not get here, out of range
677       year=month=0;
678     }
679 
680     dayOfMonth = (days - monthStart(year, month)) + 1;
681 
682     // Now figure out the day of the year.
683     dayOfYear = (days - monthStart(year, 0)) + 1;
684 
685 
686     internalSet(UCAL_ERA, 0);
687     internalSet(UCAL_YEAR, year);
688     internalSet(UCAL_EXTENDED_YEAR, year);
689     internalSet(UCAL_MONTH, month);
690     internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
691     internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
692 }
693 
694 UBool
inDaylightTime(UErrorCode & status) const695 IslamicCalendar::inDaylightTime(UErrorCode& status) const
696 {
697     // copied from GregorianCalendar
698     if (U_FAILURE(status) || !getTimeZone().useDaylightTime())
699         return FALSE;
700 
701     // Force an update of the state of the Calendar.
702     ((IslamicCalendar*)this)->complete(status); // cast away const
703 
704     return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE);
705 }
706 
707 /**
708  * The system maintains a static default century start date and Year.  They are
709  * initialized the first time they are used.  Once the system default century date
710  * and year are set, they do not change.
711  */
712 static UDate           gSystemDefaultCenturyStart       = DBL_MIN;
713 static int32_t         gSystemDefaultCenturyStartYear   = -1;
714 static icu::UInitOnce  gSystemDefaultCenturyInit        = U_INITONCE_INITIALIZER;
715 
716 
haveDefaultCentury() const717 UBool IslamicCalendar::haveDefaultCentury() const
718 {
719     return TRUE;
720 }
721 
defaultCenturyStart() const722 UDate IslamicCalendar::defaultCenturyStart() const
723 {
724     // lazy-evaluate systemDefaultCenturyStart
725     umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
726     return gSystemDefaultCenturyStart;
727 }
728 
defaultCenturyStartYear() const729 int32_t IslamicCalendar::defaultCenturyStartYear() const
730 {
731     // lazy-evaluate systemDefaultCenturyStartYear
732     umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
733     return gSystemDefaultCenturyStartYear;
734 }
735 
736 
737 void U_CALLCONV
initializeSystemDefaultCentury()738 IslamicCalendar::initializeSystemDefaultCentury()
739 {
740     // initialize systemDefaultCentury and systemDefaultCenturyYear based
741     // on the current time.  They'll be set to 80 years before
742     // the current time.
743     UErrorCode status = U_ZERO_ERROR;
744     IslamicCalendar calendar(Locale("@calendar=islamic-civil"),status);
745     if (U_SUCCESS(status)) {
746         calendar.setTime(Calendar::getNow(), status);
747         calendar.add(UCAL_YEAR, -80, status);
748 
749         gSystemDefaultCenturyStart = calendar.getTime(status);
750         gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status);
751     }
752     // We have no recourse upon failure unless we want to propagate the failure
753     // out.
754 }
755 
756 
757 
758 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicCalendar)
759 
760 U_NAMESPACE_END
761 
762 #endif
763 
764