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 28static 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> 34static void debug_islamcal_loc(const char *f, int32_t l) 35{ 36 fprintf(stderr, "%s:%d: ", f, l); 37} 38 39static 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 55static UMutex astroLock = U_MUTEX_INITIALIZER; // pod bay door lock 56static icu::CalendarCache *gMonthCache = NULL; 57static icu::CalendarAstronomer *gIslamicCalendarAstro = NULL; 58 59U_CDECL_BEGIN 60static 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} 71U_CDECL_END 72 73U_NAMESPACE_BEGIN 74 75// Implementation of the IslamicCalendar class 76 77/** 78 * Friday EPOC 79 */ 80static 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 */ 85static const int32_t ASTRONOMICAL_EPOC = 1948439; // CE 622 July 15 Thursday (Julian calendar) 86 87 88static const int32_t UMALQURA_YEAR_START = 1300; 89static const int32_t UMALQURA_YEAR_END = 1600; 90 91static 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 194int32_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 207const 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 231Calendar* IslamicCalendar::clone() const { 232 return new IslamicCalendar(*this); 233} 234 235IslamicCalendar::IslamicCalendar(const Locale& aLocale, UErrorCode& success, ECalculationType type) 236: Calendar(TimeZone::createDefault(), aLocale, success), 237cType(type) 238{ 239 setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. 240} 241 242IslamicCalendar::IslamicCalendar(const IslamicCalendar& other) : Calendar(other), cType(other.cType) { 243} 244 245IslamicCalendar::~IslamicCalendar() 246{ 247} 248 249void 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*/ 267UBool 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. 282static 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*/ 313int32_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 322static 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*/ 359UBool 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*/ 368int32_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*/ 391int32_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*/ 415int32_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 } 454trueMonthStartEnd : 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*/ 470double 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*/ 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 = (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 694UBool 695IslamicCalendar::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 */ 712static UDate gSystemDefaultCenturyStart = DBL_MIN; 713static int32_t gSystemDefaultCenturyStartYear = -1; 714static icu::UInitOnce gSystemDefaultCenturyInit = U_INITONCE_INITIALIZER; 715 716 717UBool IslamicCalendar::haveDefaultCentury() const 718{ 719 return TRUE; 720} 721 722UDate IslamicCalendar::defaultCenturyStart() const 723{ 724 // lazy-evaluate systemDefaultCenturyStart 725 umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); 726 return gSystemDefaultCenturyStart; 727} 728 729int32_t IslamicCalendar::defaultCenturyStartYear() const 730{ 731 // lazy-evaluate systemDefaultCenturyStartYear 732 umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); 733 return gSystemDefaultCenturyStartYear; 734} 735 736 737void U_CALLCONV 738IslamicCalendar::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 758UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicCalendar) 759 760U_NAMESPACE_END 761 762#endif 763 764