1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/islamcal.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,678 @@ 1.4 +/* 1.5 +****************************************************************************** 1.6 +* Copyright (C) 2003-2013, International Business Machines Corporation 1.7 +* and others. All Rights Reserved. 1.8 +****************************************************************************** 1.9 +* 1.10 +* File ISLAMCAL.H 1.11 +* 1.12 +* Modification History: 1.13 +* 1.14 +* Date Name Description 1.15 +* 10/14/2003 srl ported from java IslamicCalendar 1.16 +***************************************************************************** 1.17 +*/ 1.18 + 1.19 +#include "islamcal.h" 1.20 + 1.21 +#if !UCONFIG_NO_FORMATTING 1.22 + 1.23 +#include "umutex.h" 1.24 +#include <float.h> 1.25 +#include "gregoimp.h" // Math 1.26 +#include "astro.h" // CalendarAstronomer 1.27 +#include "uhash.h" 1.28 +#include "ucln_in.h" 1.29 +#include "uassert.h" 1.30 + 1.31 +static const UDate HIJRA_MILLIS = -42521587200000.0; // 7/16/622 AD 00:00 1.32 + 1.33 +// Debugging 1.34 +#ifdef U_DEBUG_ISLAMCAL 1.35 +# include <stdio.h> 1.36 +# include <stdarg.h> 1.37 +static void debug_islamcal_loc(const char *f, int32_t l) 1.38 +{ 1.39 + fprintf(stderr, "%s:%d: ", f, l); 1.40 +} 1.41 + 1.42 +static void debug_islamcal_msg(const char *pat, ...) 1.43 +{ 1.44 + va_list ap; 1.45 + va_start(ap, pat); 1.46 + vfprintf(stderr, pat, ap); 1.47 + fflush(stderr); 1.48 +} 1.49 +// must use double parens, i.e.: U_DEBUG_ISLAMCAL_MSG(("four is: %d",4)); 1.50 +#define U_DEBUG_ISLAMCAL_MSG(x) {debug_islamcal_loc(__FILE__,__LINE__);debug_islamcal_msg x;} 1.51 +#else 1.52 +#define U_DEBUG_ISLAMCAL_MSG(x) 1.53 +#endif 1.54 + 1.55 + 1.56 +// --- The cache -- 1.57 +// cache of months 1.58 +static UMutex astroLock = U_MUTEX_INITIALIZER; // pod bay door lock 1.59 +static icu::CalendarCache *gMonthCache = NULL; 1.60 +static icu::CalendarAstronomer *gIslamicCalendarAstro = NULL; 1.61 + 1.62 +U_CDECL_BEGIN 1.63 +static UBool calendar_islamic_cleanup(void) { 1.64 + if (gMonthCache) { 1.65 + delete gMonthCache; 1.66 + gMonthCache = NULL; 1.67 + } 1.68 + if (gIslamicCalendarAstro) { 1.69 + delete gIslamicCalendarAstro; 1.70 + gIslamicCalendarAstro = NULL; 1.71 + } 1.72 + return TRUE; 1.73 +} 1.74 +U_CDECL_END 1.75 + 1.76 +U_NAMESPACE_BEGIN 1.77 + 1.78 +// Implementation of the IslamicCalendar class 1.79 + 1.80 +/** 1.81 + * Friday EPOC 1.82 + */ 1.83 +static const int32_t CIVIL_EPOC = 1948440; 1.84 + 1.85 +/** 1.86 + * Thursday EPOC 1.87 + */ 1.88 +static const int32_t ASTRONOMICAL_EPOC = 1948439; 1.89 + 1.90 + 1.91 +static const int32_t UMALQURA_YEAR_START = 1318; 1.92 +static const int32_t UMALQURA_YEAR_END = 1480; 1.93 + 1.94 +static const int UMALQURA_MONTHLENGTH[] = { 1.95 + //* 1318 -1322 */ "0101 0111 0100", "1001 0111 0110", "0100 1011 0111", "0010 0101 0111", "0101 0010 1011", 1.96 + 0x0574, 0x0975, 0x06A7, 0x0257, 0x052B, 1.97 + //* 1323 -1327 */ "0110 1001 0101", "0110 1100 1010", "1010 1101 0101", "0101 0101 1011", "0010 0101 1101", 1.98 + 0x0695, 0x06CA, 0x0AD5, 0x055B, 0x025B, 1.99 + //* 1328 -1332 */ "1001 0010 1101", "1100 1001 0101", "1101 0100 1010", "1110 1010 0101", "0110 1101 0010", 1.100 + 0x092D, 0x0C95, 0x0D4A, 0x0E5B, 0x025B, 1.101 + //* 1333 -1337 */ "1010 1101 0101", "0101 0101 1010", "1010 1010 1011", "0100 0100 1011", "0110 1010 0101", 1.102 + 0x0AD5, 0x055A, 0x0AAB, 0x044B, 0x06A5, 1.103 + //* 1338 -1342 */ "0111 0101 0010", "1011 1010 1001", "0011 0111 0100", "1010 1011 0110", "0101 0101 0110", 1.104 + 0x0752, 0x0BA9, 0x0374, 0x0AB6, 0x0556, 1.105 + //* 1343 -1347 */ "1010 1010 1010", "1101 0101 0010", "1101 1010 1001", "0101 1101 0100", "1010 1110 1010", 1.106 + 0x0AAA, 0x0D52, 0x0DA9, 0x05D4, 0x0AEA, 1.107 + //* 1348 -1352 */ "0100 1101 1101", "0010 0110 1110", "1001 0010 1110", "1010 1010 0110", "1101 0101 0100", 1.108 + 0x04DD, 0x026E, 0x092E, 0x0AA6, 0x0D54, 1.109 + //* 1353 -1357 */ "0101 1010 1010", "0101 1011 0101", "0010 1011 0100", "1001 0011 0111", "0100 1001 1011", 1.110 + 0x05AA, 0x05B5, 0x02B4, 0x0937, 0x049B, 1.111 + //* 1358 -1362 */ "1010 0100 1011", "1011 0010 0101", "1011 0101 0100", "1011 0110 1010", "0101 0110 1101", 1.112 + 0x0A4B, 0x0B25, 0x0B54, 0x0B6A, 0x056D, 1.113 + //* 1363 -1367 */ "0100 1010 1101", "1010 0101 0101", "1101 0010 0101", "1110 1001 0010", "1110 1100 1001", 1.114 + 0x04AD, 0x0A55, 0x0D25, 0x0E92, 0x0EC9, 1.115 + //* 1368 -1372 */ "0110 1101 0100", "1010 1110 1010", "0101 0110 1011", "0100 1010 1011", "0110 1000 0101", 1.116 + 0x06D4, 0x0ADA, 0x056B, 0x04AB, 0x0685, 1.117 + //* 1373 -1377 */ "1011 0100 1001", "1011 1010 0100", "1011 1011 0010", "0101 1011 0101", "0010 1011 1010", 1.118 + 0x0B49, 0x0BA4, 0x0BB2, 0x05B5, 0x02BA, 1.119 + //* 1378 -1382 */ "1001 0101 1011", "0100 1010 1011", "0101 0101 0101", "0110 1011 0010", "0110 1101 1001", 1.120 + 0x095B, 0x04AB, 0x0555, 0x06B2, 0x06D9, 1.121 + //* 1383 -1387 */ "0010 1110 1100", "1001 0110 1110", "0100 1010 1110", "1010 0101 0110", "1101 0010 1010", 1.122 + 0x02EC, 0x096E, 0x04AE, 0x0A56, 0x0D2A, 1.123 + //* 1388 -1392 */ "1101 0101 0101", "0101 1010 1010", "1010 1011 0101", "0100 1011 1011", "0000 0101 1011", 1.124 + 0x0D55, 0x05AA, 0x0AB5, 0x04BB, 0x005B, 1.125 + //* 1393 -1397 */ "1001 0010 1011", "1010 1001 0101", "0011 0100 1010", "1011 1010 0101", "0101 1010 1010", 1.126 + 0x092B, 0x0A95, 0x034A, 0x0BA5, 0x05AA, 1.127 + //* 1398 -1402 */ "1010 1011 0101", "0101 0101 0110", "1010 1001 0110", "1101 0100 1010", "1110 1010 0101", 1.128 + 0x0AB5, 0x0556, 0x0A96, 0x0B4A, 0x0EA5, 1.129 + //* 1403 -1407 */ "0111 0101 0010", "0110 1110 1001", "0011 0110 1010", "1010 1010 1101", "0101 0101 0101", 1.130 + 0x0752, 0x06E9, 0x036A, 0x0AAD, 0x0555, 1.131 + //* 1408 -1412 */ "1010 1010 0101", "1011 0101 0010", "1011 1010 1001", "0101 1011 0100", "1001 1011 1010", 1.132 + 0x0AA5, 0x0B52, 0x0BA9, 0x05B4, 0x09BA, 1.133 + //* 1413 -1417 */ "0100 1101 1011", "0010 0101 1101", "0101 0010 1101", "1010 1010 0101", "1010 1101 0100", 1.134 + 0x04DB, 0x025D, 0x052D, 0x0AA5, 0x0AD4, 1.135 + //* 1418 -1422 */ "1010 1110 1010", "0101 0110 1101", "0100 1011 1101", "0010 0011 1101", "1001 0001 1101", 1.136 + 0x0AEA, 0x056D, 0x04BD, 0x023D, 0x091D, 1.137 + //* 1423 -1427 */ "1010 1001 0101", "1011 0100 1010", "1011 0101 1010", "0101 0110 1101", "0010 1011 0110", 1.138 + 0x0A95, 0x0B4A, 0x0B5A, 0x056D, 0x02B6, 1.139 + //* 1428 -1432 */ "1001 0011 1011", "0100 1001 1011", "0110 0101 0101", "0110 1010 1001", "0111 0101 0100", 1.140 + 0x093B, 0x049B, 0x0655, 0x06A9, 0x0754, 1.141 + //* 1433 -1437 */ "1011 0110 1010", "0101 0110 1100", "1010 1010 1101", "0101 0101 0101", "1011 0010 1001", 1.142 + 0x0B6A, 0x056C, 0x0AAD, 0x0555, 0x0B29, 1.143 + //* 1438 -1442 */ "1011 1001 0010", "1011 1010 1001", "0101 1101 0100", "1010 1101 1010", "0101 0101 1010", 1.144 + 0x0B92, 0x0BA9, 0x05D4, 0x0ADA, 0x055A, 1.145 + //* 1443 -1447 */ "1010 1010 1011", "0101 1001 0101", "0111 0100 1001", "0111 0110 0100", "1011 1010 1010", 1.146 + 0x0AAB, 0x0595, 0x0749, 0x0764, 0x0BAA, 1.147 + //* 1448 -1452 */ "0101 1011 0101", "0010 1011 0110", "1010 0101 0110", "1110 0100 1101", "1011 0010 0101", 1.148 + 0x05B5, 0x02B6, 0x0A56, 0x0E4D, 0x0B25, 1.149 + //* 1453 -1457 */ "1011 0101 0010", "1011 0110 1010", "0101 1010 1101", "0010 1010 1110", "1001 0010 1111", 1.150 + 0x0B52, 0x0B6A, 0x05AD, 0x02AE, 0x092F, 1.151 + //* 1458 -1462 */ "0100 1001 0111", "0110 0100 1011", "0110 1010 0101", "0110 1010 1100", "1010 1101 0110", 1.152 + 0x0497, 0x064B, 0x06A5, 0x06AC, 0x0AD6, 1.153 + //* 1463 -1467 */ "0101 0101 1101", "0100 1001 1101", "1010 0100 1101", "1101 0001 0110", "1101 1001 0101", 1.154 + 0x055D, 0x049D, 0x0A4D, 0x0D16, 0x0D95, 1.155 + //* 1468 -1472 */ "0101 1010 1010", "0101 1011 0101", "0010 1001 1010", "1001 0101 1011", "0100 1010 1100", 1.156 + 0x05AA, 0x05B5, 0x029A, 0x095B, 0x04AC, 1.157 + //* 1473 -1477 */ "0101 1001 0101", "0110 1100 1010", "0110 1110 0100", "1010 1110 1010", "0100 1111 0101", 1.158 + 0x0595, 0x06CA, 0x06E4, 0x0AEA, 0x04F5, 1.159 + //* 1478 -1480 */ "0010 1011 0110", "1001 0101 0110", "1010 1010 1010" 1.160 + 0x02B6, 0x0956, 0x0AAA 1.161 +}; 1.162 + 1.163 +int32_t getUmalqura_MonthLength(int32_t y, int32_t m) { 1.164 + int32_t mask = (int32_t) (0x01 << (11 - m)); // set mask for bit corresponding to month 1.165 + if((UMALQURA_MONTHLENGTH[y] & mask) == 0 ) 1.166 + return 29; 1.167 + else 1.168 + return 30; 1.169 + 1.170 +} 1.171 + 1.172 +//------------------------------------------------------------------------- 1.173 +// Constructors... 1.174 +//------------------------------------------------------------------------- 1.175 + 1.176 +const char *IslamicCalendar::getType() const { 1.177 + const char *sType = NULL; 1.178 + 1.179 + switch (cType) { 1.180 + case CIVIL: 1.181 + sType = "islamic-civil"; 1.182 + break; 1.183 + case ASTRONOMICAL: 1.184 + sType = "islamic"; 1.185 + break; 1.186 + case TBLA: 1.187 + sType = "islamic-tbla"; 1.188 + break; 1.189 + case UMALQURA: 1.190 + sType = "islamic-umalqura"; 1.191 + break; 1.192 + default: 1.193 + U_ASSERT(false); // out of range 1.194 + sType = "islamic"; // "islamic" is used as the generic type 1.195 + break; 1.196 + } 1.197 + return sType; 1.198 +} 1.199 + 1.200 +Calendar* IslamicCalendar::clone() const { 1.201 + return new IslamicCalendar(*this); 1.202 +} 1.203 + 1.204 +IslamicCalendar::IslamicCalendar(const Locale& aLocale, UErrorCode& success, ECalculationType type) 1.205 +: Calendar(TimeZone::createDefault(), aLocale, success), 1.206 +cType(type) 1.207 +{ 1.208 + setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. 1.209 +} 1.210 + 1.211 +IslamicCalendar::IslamicCalendar(const IslamicCalendar& other) : Calendar(other), cType(other.cType) { 1.212 +} 1.213 + 1.214 +IslamicCalendar::~IslamicCalendar() 1.215 +{ 1.216 +} 1.217 + 1.218 +void IslamicCalendar::setCalculationType(ECalculationType type, UErrorCode &status) 1.219 +{ 1.220 + if (cType != type) { 1.221 + // The fields of the calendar will become invalid, because the calendar 1.222 + // rules are different 1.223 + UDate m = getTimeInMillis(status); 1.224 + cType = type; 1.225 + clear(); 1.226 + setTimeInMillis(m, status); 1.227 + } 1.228 +} 1.229 + 1.230 +/** 1.231 +* Returns <code>true</code> if this object is using the fixed-cycle civil 1.232 +* calendar, or <code>false</code> if using the religious, astronomical 1.233 +* calendar. 1.234 +* @draft ICU 2.4 1.235 +*/ 1.236 +UBool IslamicCalendar::isCivil() { 1.237 + return (cType == CIVIL); 1.238 +} 1.239 + 1.240 +//------------------------------------------------------------------------- 1.241 +// Minimum / Maximum access functions 1.242 +//------------------------------------------------------------------------- 1.243 + 1.244 +// Note: Current IslamicCalendar implementation does not work 1.245 +// well with negative years. 1.246 + 1.247 +// TODO: In some cases the current ICU Islamic calendar implementation shows 1.248 +// a month as having 31 days. Since date parsing now uses range checks based 1.249 +// on the table below, we need to change the range for last day of month to 1.250 +// include 31 as a workaround until the implementation is fixed. 1.251 +static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { 1.252 + // Minimum Greatest Least Maximum 1.253 + // Minimum Maximum 1.254 + { 0, 0, 0, 0}, // ERA 1.255 + { 1, 1, 5000000, 5000000}, // YEAR 1.256 + { 0, 0, 11, 11}, // MONTH 1.257 + { 1, 1, 50, 51}, // WEEK_OF_YEAR 1.258 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH 1.259 + { 1, 1, 29, 31}, // DAY_OF_MONTH - 31 to workaround for cal implementation bug, should be 30 1.260 + { 1, 1, 354, 355}, // DAY_OF_YEAR 1.261 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK 1.262 + { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH 1.263 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM 1.264 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR 1.265 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY 1.266 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE 1.267 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND 1.268 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND 1.269 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET 1.270 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET 1.271 + { 1, 1, 5000000, 5000000}, // YEAR_WOY 1.272 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL 1.273 + { 1, 1, 5000000, 5000000}, // EXTENDED_YEAR 1.274 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY 1.275 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY 1.276 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH 1.277 +}; 1.278 + 1.279 +/** 1.280 +* @draft ICU 2.4 1.281 +*/ 1.282 +int32_t IslamicCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { 1.283 + return LIMITS[field][limitType]; 1.284 +} 1.285 + 1.286 +//------------------------------------------------------------------------- 1.287 +// Assorted calculation utilities 1.288 +// 1.289 + 1.290 +/** 1.291 +* Determine whether a year is a leap year in the Islamic civil calendar 1.292 +*/ 1.293 +UBool IslamicCalendar::civilLeapYear(int32_t year) 1.294 +{ 1.295 + return (14 + 11 * year) % 30 < 11; 1.296 +} 1.297 + 1.298 +/** 1.299 +* Return the day # on which the given year starts. Days are counted 1.300 +* from the Hijri epoch, origin 0. 1.301 +*/ 1.302 +int32_t IslamicCalendar::yearStart(int32_t year) const{ 1.303 + if (cType == CIVIL || cType == TBLA || 1.304 + (cType == UMALQURA && year < UMALQURA_YEAR_START)) 1.305 + { 1.306 + return (year-1)*354 + ClockMath::floorDivide((3+11*year),30); 1.307 + } else if(cType==ASTRONOMICAL){ 1.308 + return trueMonthStart(12*(year-1)); 1.309 + } else { 1.310 + int32_t ys = yearStart(UMALQURA_YEAR_START-1); 1.311 + ys+= handleGetYearLength(UMALQURA_YEAR_START-1); 1.312 + for(int i=UMALQURA_YEAR_START; i< year; i++){ 1.313 + ys+= handleGetYearLength(i); 1.314 + } 1.315 + return ys; 1.316 + } 1.317 +} 1.318 + 1.319 +/** 1.320 +* Return the day # on which the given month starts. Days are counted 1.321 +* from the Hijri epoch, origin 0. 1.322 +* 1.323 +* @param year The hijri year 1.324 +* @param year The hijri month, 0-based 1.325 +*/ 1.326 +int32_t IslamicCalendar::monthStart(int32_t year, int32_t month) const { 1.327 + if (cType == CIVIL || cType == TBLA) { 1.328 + return (int32_t)uprv_ceil(29.5*month) 1.329 + + (year-1)*354 + (int32_t)ClockMath::floorDivide((3+11*year),30); 1.330 + } else if(cType==ASTRONOMICAL){ 1.331 + return trueMonthStart(12*(year-1) + month); 1.332 + } else { 1.333 + int32_t ms = yearStart(year); 1.334 + for(int i=0; i< month; i++){ 1.335 + ms+= handleGetMonthLength(year, i); 1.336 + } 1.337 + return ms; 1.338 + } 1.339 +} 1.340 + 1.341 +/** 1.342 +* Find the day number on which a particular month of the true/lunar 1.343 +* Islamic calendar starts. 1.344 +* 1.345 +* @param month The month in question, origin 0 from the Hijri epoch 1.346 +* 1.347 +* @return The day number on which the given month starts. 1.348 +*/ 1.349 +int32_t IslamicCalendar::trueMonthStart(int32_t month) const 1.350 +{ 1.351 + UErrorCode status = U_ZERO_ERROR; 1.352 + int32_t start = CalendarCache::get(&gMonthCache, month, status); 1.353 + 1.354 + if (start==0) { 1.355 + // Make a guess at when the month started, using the average length 1.356 + UDate origin = HIJRA_MILLIS 1.357 + + uprv_floor(month * CalendarAstronomer::SYNODIC_MONTH) * kOneDay; 1.358 + 1.359 + // moonAge will fail due to memory allocation error 1.360 + double age = moonAge(origin, status); 1.361 + if (U_FAILURE(status)) { 1.362 + goto trueMonthStartEnd; 1.363 + } 1.364 + 1.365 + if (age >= 0) { 1.366 + // The month has already started 1.367 + do { 1.368 + origin -= kOneDay; 1.369 + age = moonAge(origin, status); 1.370 + if (U_FAILURE(status)) { 1.371 + goto trueMonthStartEnd; 1.372 + } 1.373 + } while (age >= 0); 1.374 + } 1.375 + else { 1.376 + // Preceding month has not ended yet. 1.377 + do { 1.378 + origin += kOneDay; 1.379 + age = moonAge(origin, status); 1.380 + if (U_FAILURE(status)) { 1.381 + goto trueMonthStartEnd; 1.382 + } 1.383 + } while (age < 0); 1.384 + } 1.385 + start = (int32_t)ClockMath::floorDivide((origin - HIJRA_MILLIS), (double)kOneDay) + 1; 1.386 + CalendarCache::put(&gMonthCache, month, start, status); 1.387 + } 1.388 +trueMonthStartEnd : 1.389 + if(U_FAILURE(status)) { 1.390 + start = 0; 1.391 + } 1.392 + return start; 1.393 +} 1.394 + 1.395 +/** 1.396 +* Return the "age" of the moon at the given time; this is the difference 1.397 +* in ecliptic latitude between the moon and the sun. This method simply 1.398 +* calls CalendarAstronomer.moonAge, converts to degrees, 1.399 +* and adjusts the result to be in the range [-180, 180]. 1.400 +* 1.401 +* @param time The time at which the moon's age is desired, 1.402 +* in millis since 1/1/1970. 1.403 +*/ 1.404 +double IslamicCalendar::moonAge(UDate time, UErrorCode &status) 1.405 +{ 1.406 + double age = 0; 1.407 + 1.408 + umtx_lock(&astroLock); 1.409 + if(gIslamicCalendarAstro == NULL) { 1.410 + gIslamicCalendarAstro = new CalendarAstronomer(); 1.411 + if (gIslamicCalendarAstro == NULL) { 1.412 + status = U_MEMORY_ALLOCATION_ERROR; 1.413 + return age; 1.414 + } 1.415 + ucln_i18n_registerCleanup(UCLN_I18N_ISLAMIC_CALENDAR, calendar_islamic_cleanup); 1.416 + } 1.417 + gIslamicCalendarAstro->setTime(time); 1.418 + age = gIslamicCalendarAstro->getMoonAge(); 1.419 + umtx_unlock(&astroLock); 1.420 + 1.421 + // Convert to degrees and normalize... 1.422 + age = age * 180 / CalendarAstronomer::PI; 1.423 + if (age > 180) { 1.424 + age = age - 360; 1.425 + } 1.426 + 1.427 + return age; 1.428 +} 1.429 + 1.430 +//---------------------------------------------------------------------- 1.431 +// Calendar framework 1.432 +//---------------------------------------------------------------------- 1.433 + 1.434 +/** 1.435 +* Return the length (in days) of the given month. 1.436 +* 1.437 +* @param year The hijri year 1.438 +* @param year The hijri month, 0-based 1.439 +* @draft ICU 2.4 1.440 +*/ 1.441 +int32_t IslamicCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { 1.442 + 1.443 + int32_t length = 0; 1.444 + 1.445 + if (cType == CIVIL || cType == TBLA || 1.446 + (cType == UMALQURA && (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END)) ) { 1.447 + length = 29 + (month+1) % 2; 1.448 + if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) { 1.449 + length++; 1.450 + } 1.451 + } else if(cType == ASTRONOMICAL){ 1.452 + month = 12*(extendedYear-1) + month; 1.453 + length = trueMonthStart(month+1) - trueMonthStart(month) ; 1.454 + } else { 1.455 + length = getUmalqura_MonthLength(extendedYear - UMALQURA_YEAR_START, month); 1.456 + } 1.457 + return length; 1.458 +} 1.459 + 1.460 +/** 1.461 +* Return the number of days in the given Islamic year 1.462 +* @draft ICU 2.4 1.463 +*/ 1.464 +int32_t IslamicCalendar::handleGetYearLength(int32_t extendedYear) const { 1.465 + if (cType == CIVIL || cType == TBLA || 1.466 + (cType == UMALQURA && (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END)) ) { 1.467 + return 354 + (civilLeapYear(extendedYear) ? 1 : 0); 1.468 + } else if(cType == ASTRONOMICAL){ 1.469 + int32_t month = 12*(extendedYear-1); 1.470 + return (trueMonthStart(month + 12) - trueMonthStart(month)); 1.471 + } else { 1.472 + int len = 0; 1.473 + for(int i=0; i<12; i++) 1.474 + len += handleGetMonthLength(extendedYear, i); 1.475 + return len; 1.476 + } 1.477 +} 1.478 + 1.479 +//------------------------------------------------------------------------- 1.480 +// Functions for converting from field values to milliseconds.... 1.481 +//------------------------------------------------------------------------- 1.482 + 1.483 +// Return JD of start of given month/year 1.484 +/** 1.485 +* @draft ICU 2.4 1.486 +*/ 1.487 +int32_t IslamicCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */) const { 1.488 + return monthStart(eyear, month) + 1948439; 1.489 +} 1.490 + 1.491 +//------------------------------------------------------------------------- 1.492 +// Functions for converting from milliseconds to field values 1.493 +//------------------------------------------------------------------------- 1.494 + 1.495 +/** 1.496 +* @draft ICU 2.4 1.497 +*/ 1.498 +int32_t IslamicCalendar::handleGetExtendedYear() { 1.499 + int32_t year; 1.500 + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { 1.501 + year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 1.502 + } else { 1.503 + year = internalGet(UCAL_YEAR, 1); // Default to year 1 1.504 + } 1.505 + return year; 1.506 +} 1.507 + 1.508 +/** 1.509 +* Override Calendar to compute several fields specific to the Islamic 1.510 +* calendar system. These are: 1.511 +* 1.512 +* <ul><li>ERA 1.513 +* <li>YEAR 1.514 +* <li>MONTH 1.515 +* <li>DAY_OF_MONTH 1.516 +* <li>DAY_OF_YEAR 1.517 +* <li>EXTENDED_YEAR</ul> 1.518 +* 1.519 +* The DAY_OF_WEEK and DOW_LOCAL fields are already set when this 1.520 +* method is called. The getGregorianXxx() methods return Gregorian 1.521 +* calendar equivalents for the given Julian day. 1.522 +* @draft ICU 2.4 1.523 +*/ 1.524 +void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { 1.525 + int32_t year, month, dayOfMonth, dayOfYear; 1.526 + UDate startDate; 1.527 + int32_t days = julianDay - CIVIL_EPOC; 1.528 + 1.529 + if (cType == CIVIL || cType == TBLA) { 1.530 + if(cType == TBLA) 1.531 + days = julianDay - ASTRONOMICAL_EPOC; 1.532 + // Use the civil calendar approximation, which is just arithmetic 1.533 + year = (int)ClockMath::floorDivide( (double)(30 * days + 10646) , 10631.0 ); 1.534 + month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 ); 1.535 + month = month<11?month:11; 1.536 + startDate = monthStart(year, month); 1.537 + } else if(cType == ASTRONOMICAL){ 1.538 + // Guess at the number of elapsed full months since the epoch 1.539 + int32_t months = (int32_t)uprv_floor((double)days / CalendarAstronomer::SYNODIC_MONTH); 1.540 + 1.541 + startDate = uprv_floor(months * CalendarAstronomer::SYNODIC_MONTH); 1.542 + 1.543 + double age = moonAge(internalGetTime(), status); 1.544 + if (U_FAILURE(status)) { 1.545 + status = U_MEMORY_ALLOCATION_ERROR; 1.546 + return; 1.547 + } 1.548 + if ( days - startDate >= 25 && age > 0) { 1.549 + // If we're near the end of the month, assume next month and search backwards 1.550 + months++; 1.551 + } 1.552 + 1.553 + // Find out the last time that the new moon was actually visible at this longitude 1.554 + // This returns midnight the night that the moon was visible at sunset. 1.555 + while ((startDate = trueMonthStart(months)) > days) { 1.556 + // If it was after the date in question, back up a month and try again 1.557 + months--; 1.558 + } 1.559 + 1.560 + year = months / 12 + 1; 1.561 + month = months % 12; 1.562 + } else if(cType == UMALQURA) { 1.563 + int32_t umalquraStartdays = yearStart(UMALQURA_YEAR_START) ; 1.564 + if( days < umalquraStartdays){ 1.565 + //Use Civil calculation 1.566 + year = (int)ClockMath::floorDivide( (double)(30 * days + 10646) , 10631.0 ); 1.567 + month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 ); 1.568 + month = month<11?month:11; 1.569 + startDate = monthStart(year, month); 1.570 + }else{ 1.571 + int y =UMALQURA_YEAR_START-1, m =0; 1.572 + long d = 1; 1.573 + while(d > 0){ 1.574 + y++; 1.575 + d = days - yearStart(y) +1; 1.576 + if(d == handleGetYearLength(y)){ 1.577 + m=11; 1.578 + break; 1.579 + }else if(d < handleGetYearLength(y) ){ 1.580 + int monthLen = handleGetMonthLength(y, m); 1.581 + m=0; 1.582 + while(d > monthLen){ 1.583 + d -= monthLen; 1.584 + m++; 1.585 + monthLen = handleGetMonthLength(y, m); 1.586 + } 1.587 + break; 1.588 + } 1.589 + } 1.590 + year = y; 1.591 + month = m; 1.592 + } 1.593 + } else { // invalid 'civil' 1.594 + U_ASSERT(false); // should not get here, out of range 1.595 + year=month=0; 1.596 + } 1.597 + 1.598 + dayOfMonth = (days - monthStart(year, month)) + 1; 1.599 + 1.600 + // Now figure out the day of the year. 1.601 + dayOfYear = (days - monthStart(year, 0) + 1); 1.602 + 1.603 + 1.604 + internalSet(UCAL_ERA, 0); 1.605 + internalSet(UCAL_YEAR, year); 1.606 + internalSet(UCAL_EXTENDED_YEAR, year); 1.607 + internalSet(UCAL_MONTH, month); 1.608 + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); 1.609 + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); 1.610 +} 1.611 + 1.612 +UBool 1.613 +IslamicCalendar::inDaylightTime(UErrorCode& status) const 1.614 +{ 1.615 + // copied from GregorianCalendar 1.616 + if (U_FAILURE(status) || (&(getTimeZone()) == NULL && !getTimeZone().useDaylightTime())) 1.617 + return FALSE; 1.618 + 1.619 + // Force an update of the state of the Calendar. 1.620 + ((IslamicCalendar*)this)->complete(status); // cast away const 1.621 + 1.622 + return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE); 1.623 +} 1.624 + 1.625 +/** 1.626 + * The system maintains a static default century start date and Year. They are 1.627 + * initialized the first time they are used. Once the system default century date 1.628 + * and year are set, they do not change. 1.629 + */ 1.630 +static UDate gSystemDefaultCenturyStart = DBL_MIN; 1.631 +static int32_t gSystemDefaultCenturyStartYear = -1; 1.632 +static icu::UInitOnce gSystemDefaultCenturyInit = U_INITONCE_INITIALIZER; 1.633 + 1.634 + 1.635 +UBool IslamicCalendar::haveDefaultCentury() const 1.636 +{ 1.637 + return TRUE; 1.638 +} 1.639 + 1.640 +UDate IslamicCalendar::defaultCenturyStart() const 1.641 +{ 1.642 + // lazy-evaluate systemDefaultCenturyStart 1.643 + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); 1.644 + return gSystemDefaultCenturyStart; 1.645 +} 1.646 + 1.647 +int32_t IslamicCalendar::defaultCenturyStartYear() const 1.648 +{ 1.649 + // lazy-evaluate systemDefaultCenturyStartYear 1.650 + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); 1.651 + return gSystemDefaultCenturyStartYear; 1.652 +} 1.653 + 1.654 + 1.655 +void U_CALLCONV 1.656 +IslamicCalendar::initializeSystemDefaultCentury() 1.657 +{ 1.658 + // initialize systemDefaultCentury and systemDefaultCenturyYear based 1.659 + // on the current time. They'll be set to 80 years before 1.660 + // the current time. 1.661 + UErrorCode status = U_ZERO_ERROR; 1.662 + IslamicCalendar calendar(Locale("@calendar=islamic-civil"),status); 1.663 + if (U_SUCCESS(status)) { 1.664 + calendar.setTime(Calendar::getNow(), status); 1.665 + calendar.add(UCAL_YEAR, -80, status); 1.666 + 1.667 + gSystemDefaultCenturyStart = calendar.getTime(status); 1.668 + gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); 1.669 + } 1.670 + // We have no recourse upon failure unless we want to propagate the failure 1.671 + // out. 1.672 +} 1.673 + 1.674 + 1.675 + 1.676 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicCalendar) 1.677 + 1.678 +U_NAMESPACE_END 1.679 + 1.680 +#endif 1.681 +