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
30static 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>
36static void debug_islamcal_loc(const char *f, int32_t l)
37{
38 fprintf(stderr, "%s:%d: ", f, l);
39}
40
41static 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
57static icu::CalendarCache *gMonthCache = NULL;
58static icu::CalendarAstronomer *gIslamicCalendarAstro = NULL;
59
60U_CDECL_BEGIN
61static 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}
72U_CDECL_END
73
74U_NAMESPACE_BEGIN
75
76// Implementation of the IslamicCalendar class
77
78/**
79 * Friday EPOC
80 */
81static 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 */
86static const int32_t ASTRONOMICAL_EPOC = 1948439; // CE 622 July 15 Thursday (Julian calendar)
87
88
89static const int32_t UMALQURA_YEAR_START = 1300;
90static const int32_t UMALQURA_YEAR_END = 1600;
91
92static 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
195int32_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
208const 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
230IslamicCalendar* IslamicCalendar::clone() const {
231 return new IslamicCalendar(*this);
232}
233
234IslamicCalendar::IslamicCalendar(const Locale& aLocale, UErrorCode& success, ECalculationType type)
235: Calendar(TimeZone::createDefault(), aLocale, success),
236cType(type)
237{
238 setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
239}
240
241IslamicCalendar::IslamicCalendar(const IslamicCalendar& other) : Calendar(other), cType(other.cType) {
242}
243
244IslamicCalendar::~IslamicCalendar()
245{
246}
247
248void 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*/
266UBool 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.
281static 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*/
312int32_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
321static 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*/
358UBool 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*/
367int32_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*year),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*/
390int32_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*year),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*/
414int32_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((origin - HIJRA_MILLIS), (double)kOneDay) + 1;
451 CalendarCache::put(&gMonthCache, month, start, status);
452 }
453trueMonthStartEnd :
454 if(U_FAILURE(status)) {
455 start = 0;
456 }
457 return start;
458}
459
460/**
461* Return the "age" of the moon at the given time; this is the difference
462* in ecliptic latitude between the moon and the sun. This method simply
463* calls CalendarAstronomer.moonAge, converts to degrees,
464* and adjusts the result to be in the range [-180, 180].
465*
466* @param time The time at which the moon's age is desired,
467* in millis since 1/1/1970.
468*/
469double IslamicCalendar::moonAge(UDate time, UErrorCode &status)
470{
471 double age = 0;
472
473 static UMutex astroLock; // pod bay door lock
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*/
507int32_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*/
530int32_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*/
558int32_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*/
579int32_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*/
605void 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 = (int32_t)ClockMath::floorDivide(30 * (int64_t)days + 10646, (int64_t)10631);
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 UPRV_UNREACHABLE; // should not get here, out of range
677 }
678
679 dayOfMonth = (days - monthStart(year, month)) + 1;
680
681 // Now figure out the day of the year.
682 dayOfYear = (days - monthStart(year, 0)) + 1;
683
684
685 internalSet(UCAL_ERA, 0);
686 internalSet(UCAL_YEAR, year);
687 internalSet(UCAL_EXTENDED_YEAR, year);
688 internalSet(UCAL_MONTH, month);
689 internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
690 internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
691}
692
693UBool
694IslamicCalendar::inDaylightTime(UErrorCode& status) const
695{
696 // copied from GregorianCalendar
697 if (U_FAILURE(status) || !getTimeZone().useDaylightTime())
698 return FALSE;
699
700 // Force an update of the state of the Calendar.
701 ((IslamicCalendar*)this)->complete(status); // cast away const
702
703 return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE);
704}
705
706/**
707 * The system maintains a static default century start date and Year. They are
708 * initialized the first time they are used. Once the system default century date
709 * and year are set, they do not change.
710 */
711static UDate gSystemDefaultCenturyStart = DBL_MIN;
712static int32_t gSystemDefaultCenturyStartYear = -1;
713static icu::UInitOnce gSystemDefaultCenturyInit = U_INITONCE_INITIALIZER;
714
715
716UBool IslamicCalendar::haveDefaultCentury() const
717{
718 return TRUE;
719}
720
721UDate IslamicCalendar::defaultCenturyStart() const
722{
723 // lazy-evaluate systemDefaultCenturyStart
724 umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
725 return gSystemDefaultCenturyStart;
726}
727
728int32_t IslamicCalendar::defaultCenturyStartYear() const
729{
730 // lazy-evaluate systemDefaultCenturyStartYear
731 umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
732 return gSystemDefaultCenturyStartYear;
733}
734
735
736U_CFUNC void U_CALLCONV
737IslamicCalendar::initializeSystemDefaultCentury()
738{
739 // initialize systemDefaultCentury and systemDefaultCenturyYear based
740 // on the current time. They'll be set to 80 years before
741 // the current time.
742 UErrorCode status = U_ZERO_ERROR;
743 IslamicCalendar calendar(Locale("@calendar=islamic-civil"),status);
744 if (U_SUCCESS(status)) {
745 calendar.setTime(Calendar::getNow(), status);
746 calendar.add(UCAL_YEAR, -80, status);
747
748 gSystemDefaultCenturyStart = calendar.getTime(status);
749 gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status);
750 }
751 // We have no recourse upon failure unless we want to propagate the failure
752 // out.
753}
754
755
756
757UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicCalendar)
758
759U_NAMESPACE_END
760
761#endif
762
763