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