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