intl/icu/source/i18n/islamcal.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /*
michael@0 2 ******************************************************************************
michael@0 3 * Copyright (C) 2003-2013, International Business Machines Corporation
michael@0 4 * and others. All Rights Reserved.
michael@0 5 ******************************************************************************
michael@0 6 *
michael@0 7 * File ISLAMCAL.H
michael@0 8 *
michael@0 9 * Modification History:
michael@0 10 *
michael@0 11 * Date Name Description
michael@0 12 * 10/14/2003 srl ported from java IslamicCalendar
michael@0 13 *****************************************************************************
michael@0 14 */
michael@0 15
michael@0 16 #include "islamcal.h"
michael@0 17
michael@0 18 #if !UCONFIG_NO_FORMATTING
michael@0 19
michael@0 20 #include "umutex.h"
michael@0 21 #include <float.h>
michael@0 22 #include "gregoimp.h" // Math
michael@0 23 #include "astro.h" // CalendarAstronomer
michael@0 24 #include "uhash.h"
michael@0 25 #include "ucln_in.h"
michael@0 26 #include "uassert.h"
michael@0 27
michael@0 28 static const UDate HIJRA_MILLIS = -42521587200000.0; // 7/16/622 AD 00:00
michael@0 29
michael@0 30 // Debugging
michael@0 31 #ifdef U_DEBUG_ISLAMCAL
michael@0 32 # include <stdio.h>
michael@0 33 # include <stdarg.h>
michael@0 34 static void debug_islamcal_loc(const char *f, int32_t l)
michael@0 35 {
michael@0 36 fprintf(stderr, "%s:%d: ", f, l);
michael@0 37 }
michael@0 38
michael@0 39 static void debug_islamcal_msg(const char *pat, ...)
michael@0 40 {
michael@0 41 va_list ap;
michael@0 42 va_start(ap, pat);
michael@0 43 vfprintf(stderr, pat, ap);
michael@0 44 fflush(stderr);
michael@0 45 }
michael@0 46 // must use double parens, i.e.: U_DEBUG_ISLAMCAL_MSG(("four is: %d",4));
michael@0 47 #define U_DEBUG_ISLAMCAL_MSG(x) {debug_islamcal_loc(__FILE__,__LINE__);debug_islamcal_msg x;}
michael@0 48 #else
michael@0 49 #define U_DEBUG_ISLAMCAL_MSG(x)
michael@0 50 #endif
michael@0 51
michael@0 52
michael@0 53 // --- The cache --
michael@0 54 // cache of months
michael@0 55 static UMutex astroLock = U_MUTEX_INITIALIZER; // pod bay door lock
michael@0 56 static icu::CalendarCache *gMonthCache = NULL;
michael@0 57 static icu::CalendarAstronomer *gIslamicCalendarAstro = NULL;
michael@0 58
michael@0 59 U_CDECL_BEGIN
michael@0 60 static UBool calendar_islamic_cleanup(void) {
michael@0 61 if (gMonthCache) {
michael@0 62 delete gMonthCache;
michael@0 63 gMonthCache = NULL;
michael@0 64 }
michael@0 65 if (gIslamicCalendarAstro) {
michael@0 66 delete gIslamicCalendarAstro;
michael@0 67 gIslamicCalendarAstro = NULL;
michael@0 68 }
michael@0 69 return TRUE;
michael@0 70 }
michael@0 71 U_CDECL_END
michael@0 72
michael@0 73 U_NAMESPACE_BEGIN
michael@0 74
michael@0 75 // Implementation of the IslamicCalendar class
michael@0 76
michael@0 77 /**
michael@0 78 * Friday EPOC
michael@0 79 */
michael@0 80 static const int32_t CIVIL_EPOC = 1948440;
michael@0 81
michael@0 82 /**
michael@0 83 * Thursday EPOC
michael@0 84 */
michael@0 85 static const int32_t ASTRONOMICAL_EPOC = 1948439;
michael@0 86
michael@0 87
michael@0 88 static const int32_t UMALQURA_YEAR_START = 1318;
michael@0 89 static const int32_t UMALQURA_YEAR_END = 1480;
michael@0 90
michael@0 91 static const int UMALQURA_MONTHLENGTH[] = {
michael@0 92 //* 1318 -1322 */ "0101 0111 0100", "1001 0111 0110", "0100 1011 0111", "0010 0101 0111", "0101 0010 1011",
michael@0 93 0x0574, 0x0975, 0x06A7, 0x0257, 0x052B,
michael@0 94 //* 1323 -1327 */ "0110 1001 0101", "0110 1100 1010", "1010 1101 0101", "0101 0101 1011", "0010 0101 1101",
michael@0 95 0x0695, 0x06CA, 0x0AD5, 0x055B, 0x025B,
michael@0 96 //* 1328 -1332 */ "1001 0010 1101", "1100 1001 0101", "1101 0100 1010", "1110 1010 0101", "0110 1101 0010",
michael@0 97 0x092D, 0x0C95, 0x0D4A, 0x0E5B, 0x025B,
michael@0 98 //* 1333 -1337 */ "1010 1101 0101", "0101 0101 1010", "1010 1010 1011", "0100 0100 1011", "0110 1010 0101",
michael@0 99 0x0AD5, 0x055A, 0x0AAB, 0x044B, 0x06A5,
michael@0 100 //* 1338 -1342 */ "0111 0101 0010", "1011 1010 1001", "0011 0111 0100", "1010 1011 0110", "0101 0101 0110",
michael@0 101 0x0752, 0x0BA9, 0x0374, 0x0AB6, 0x0556,
michael@0 102 //* 1343 -1347 */ "1010 1010 1010", "1101 0101 0010", "1101 1010 1001", "0101 1101 0100", "1010 1110 1010",
michael@0 103 0x0AAA, 0x0D52, 0x0DA9, 0x05D4, 0x0AEA,
michael@0 104 //* 1348 -1352 */ "0100 1101 1101", "0010 0110 1110", "1001 0010 1110", "1010 1010 0110", "1101 0101 0100",
michael@0 105 0x04DD, 0x026E, 0x092E, 0x0AA6, 0x0D54,
michael@0 106 //* 1353 -1357 */ "0101 1010 1010", "0101 1011 0101", "0010 1011 0100", "1001 0011 0111", "0100 1001 1011",
michael@0 107 0x05AA, 0x05B5, 0x02B4, 0x0937, 0x049B,
michael@0 108 //* 1358 -1362 */ "1010 0100 1011", "1011 0010 0101", "1011 0101 0100", "1011 0110 1010", "0101 0110 1101",
michael@0 109 0x0A4B, 0x0B25, 0x0B54, 0x0B6A, 0x056D,
michael@0 110 //* 1363 -1367 */ "0100 1010 1101", "1010 0101 0101", "1101 0010 0101", "1110 1001 0010", "1110 1100 1001",
michael@0 111 0x04AD, 0x0A55, 0x0D25, 0x0E92, 0x0EC9,
michael@0 112 //* 1368 -1372 */ "0110 1101 0100", "1010 1110 1010", "0101 0110 1011", "0100 1010 1011", "0110 1000 0101",
michael@0 113 0x06D4, 0x0ADA, 0x056B, 0x04AB, 0x0685,
michael@0 114 //* 1373 -1377 */ "1011 0100 1001", "1011 1010 0100", "1011 1011 0010", "0101 1011 0101", "0010 1011 1010",
michael@0 115 0x0B49, 0x0BA4, 0x0BB2, 0x05B5, 0x02BA,
michael@0 116 //* 1378 -1382 */ "1001 0101 1011", "0100 1010 1011", "0101 0101 0101", "0110 1011 0010", "0110 1101 1001",
michael@0 117 0x095B, 0x04AB, 0x0555, 0x06B2, 0x06D9,
michael@0 118 //* 1383 -1387 */ "0010 1110 1100", "1001 0110 1110", "0100 1010 1110", "1010 0101 0110", "1101 0010 1010",
michael@0 119 0x02EC, 0x096E, 0x04AE, 0x0A56, 0x0D2A,
michael@0 120 //* 1388 -1392 */ "1101 0101 0101", "0101 1010 1010", "1010 1011 0101", "0100 1011 1011", "0000 0101 1011",
michael@0 121 0x0D55, 0x05AA, 0x0AB5, 0x04BB, 0x005B,
michael@0 122 //* 1393 -1397 */ "1001 0010 1011", "1010 1001 0101", "0011 0100 1010", "1011 1010 0101", "0101 1010 1010",
michael@0 123 0x092B, 0x0A95, 0x034A, 0x0BA5, 0x05AA,
michael@0 124 //* 1398 -1402 */ "1010 1011 0101", "0101 0101 0110", "1010 1001 0110", "1101 0100 1010", "1110 1010 0101",
michael@0 125 0x0AB5, 0x0556, 0x0A96, 0x0B4A, 0x0EA5,
michael@0 126 //* 1403 -1407 */ "0111 0101 0010", "0110 1110 1001", "0011 0110 1010", "1010 1010 1101", "0101 0101 0101",
michael@0 127 0x0752, 0x06E9, 0x036A, 0x0AAD, 0x0555,
michael@0 128 //* 1408 -1412 */ "1010 1010 0101", "1011 0101 0010", "1011 1010 1001", "0101 1011 0100", "1001 1011 1010",
michael@0 129 0x0AA5, 0x0B52, 0x0BA9, 0x05B4, 0x09BA,
michael@0 130 //* 1413 -1417 */ "0100 1101 1011", "0010 0101 1101", "0101 0010 1101", "1010 1010 0101", "1010 1101 0100",
michael@0 131 0x04DB, 0x025D, 0x052D, 0x0AA5, 0x0AD4,
michael@0 132 //* 1418 -1422 */ "1010 1110 1010", "0101 0110 1101", "0100 1011 1101", "0010 0011 1101", "1001 0001 1101",
michael@0 133 0x0AEA, 0x056D, 0x04BD, 0x023D, 0x091D,
michael@0 134 //* 1423 -1427 */ "1010 1001 0101", "1011 0100 1010", "1011 0101 1010", "0101 0110 1101", "0010 1011 0110",
michael@0 135 0x0A95, 0x0B4A, 0x0B5A, 0x056D, 0x02B6,
michael@0 136 //* 1428 -1432 */ "1001 0011 1011", "0100 1001 1011", "0110 0101 0101", "0110 1010 1001", "0111 0101 0100",
michael@0 137 0x093B, 0x049B, 0x0655, 0x06A9, 0x0754,
michael@0 138 //* 1433 -1437 */ "1011 0110 1010", "0101 0110 1100", "1010 1010 1101", "0101 0101 0101", "1011 0010 1001",
michael@0 139 0x0B6A, 0x056C, 0x0AAD, 0x0555, 0x0B29,
michael@0 140 //* 1438 -1442 */ "1011 1001 0010", "1011 1010 1001", "0101 1101 0100", "1010 1101 1010", "0101 0101 1010",
michael@0 141 0x0B92, 0x0BA9, 0x05D4, 0x0ADA, 0x055A,
michael@0 142 //* 1443 -1447 */ "1010 1010 1011", "0101 1001 0101", "0111 0100 1001", "0111 0110 0100", "1011 1010 1010",
michael@0 143 0x0AAB, 0x0595, 0x0749, 0x0764, 0x0BAA,
michael@0 144 //* 1448 -1452 */ "0101 1011 0101", "0010 1011 0110", "1010 0101 0110", "1110 0100 1101", "1011 0010 0101",
michael@0 145 0x05B5, 0x02B6, 0x0A56, 0x0E4D, 0x0B25,
michael@0 146 //* 1453 -1457 */ "1011 0101 0010", "1011 0110 1010", "0101 1010 1101", "0010 1010 1110", "1001 0010 1111",
michael@0 147 0x0B52, 0x0B6A, 0x05AD, 0x02AE, 0x092F,
michael@0 148 //* 1458 -1462 */ "0100 1001 0111", "0110 0100 1011", "0110 1010 0101", "0110 1010 1100", "1010 1101 0110",
michael@0 149 0x0497, 0x064B, 0x06A5, 0x06AC, 0x0AD6,
michael@0 150 //* 1463 -1467 */ "0101 0101 1101", "0100 1001 1101", "1010 0100 1101", "1101 0001 0110", "1101 1001 0101",
michael@0 151 0x055D, 0x049D, 0x0A4D, 0x0D16, 0x0D95,
michael@0 152 //* 1468 -1472 */ "0101 1010 1010", "0101 1011 0101", "0010 1001 1010", "1001 0101 1011", "0100 1010 1100",
michael@0 153 0x05AA, 0x05B5, 0x029A, 0x095B, 0x04AC,
michael@0 154 //* 1473 -1477 */ "0101 1001 0101", "0110 1100 1010", "0110 1110 0100", "1010 1110 1010", "0100 1111 0101",
michael@0 155 0x0595, 0x06CA, 0x06E4, 0x0AEA, 0x04F5,
michael@0 156 //* 1478 -1480 */ "0010 1011 0110", "1001 0101 0110", "1010 1010 1010"
michael@0 157 0x02B6, 0x0956, 0x0AAA
michael@0 158 };
michael@0 159
michael@0 160 int32_t getUmalqura_MonthLength(int32_t y, int32_t m) {
michael@0 161 int32_t mask = (int32_t) (0x01 << (11 - m)); // set mask for bit corresponding to month
michael@0 162 if((UMALQURA_MONTHLENGTH[y] & mask) == 0 )
michael@0 163 return 29;
michael@0 164 else
michael@0 165 return 30;
michael@0 166
michael@0 167 }
michael@0 168
michael@0 169 //-------------------------------------------------------------------------
michael@0 170 // Constructors...
michael@0 171 //-------------------------------------------------------------------------
michael@0 172
michael@0 173 const char *IslamicCalendar::getType() const {
michael@0 174 const char *sType = NULL;
michael@0 175
michael@0 176 switch (cType) {
michael@0 177 case CIVIL:
michael@0 178 sType = "islamic-civil";
michael@0 179 break;
michael@0 180 case ASTRONOMICAL:
michael@0 181 sType = "islamic";
michael@0 182 break;
michael@0 183 case TBLA:
michael@0 184 sType = "islamic-tbla";
michael@0 185 break;
michael@0 186 case UMALQURA:
michael@0 187 sType = "islamic-umalqura";
michael@0 188 break;
michael@0 189 default:
michael@0 190 U_ASSERT(false); // out of range
michael@0 191 sType = "islamic"; // "islamic" is used as the generic type
michael@0 192 break;
michael@0 193 }
michael@0 194 return sType;
michael@0 195 }
michael@0 196
michael@0 197 Calendar* IslamicCalendar::clone() const {
michael@0 198 return new IslamicCalendar(*this);
michael@0 199 }
michael@0 200
michael@0 201 IslamicCalendar::IslamicCalendar(const Locale& aLocale, UErrorCode& success, ECalculationType type)
michael@0 202 : Calendar(TimeZone::createDefault(), aLocale, success),
michael@0 203 cType(type)
michael@0 204 {
michael@0 205 setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
michael@0 206 }
michael@0 207
michael@0 208 IslamicCalendar::IslamicCalendar(const IslamicCalendar& other) : Calendar(other), cType(other.cType) {
michael@0 209 }
michael@0 210
michael@0 211 IslamicCalendar::~IslamicCalendar()
michael@0 212 {
michael@0 213 }
michael@0 214
michael@0 215 void IslamicCalendar::setCalculationType(ECalculationType type, UErrorCode &status)
michael@0 216 {
michael@0 217 if (cType != type) {
michael@0 218 // The fields of the calendar will become invalid, because the calendar
michael@0 219 // rules are different
michael@0 220 UDate m = getTimeInMillis(status);
michael@0 221 cType = type;
michael@0 222 clear();
michael@0 223 setTimeInMillis(m, status);
michael@0 224 }
michael@0 225 }
michael@0 226
michael@0 227 /**
michael@0 228 * Returns <code>true</code> if this object is using the fixed-cycle civil
michael@0 229 * calendar, or <code>false</code> if using the religious, astronomical
michael@0 230 * calendar.
michael@0 231 * @draft ICU 2.4
michael@0 232 */
michael@0 233 UBool IslamicCalendar::isCivil() {
michael@0 234 return (cType == CIVIL);
michael@0 235 }
michael@0 236
michael@0 237 //-------------------------------------------------------------------------
michael@0 238 // Minimum / Maximum access functions
michael@0 239 //-------------------------------------------------------------------------
michael@0 240
michael@0 241 // Note: Current IslamicCalendar implementation does not work
michael@0 242 // well with negative years.
michael@0 243
michael@0 244 // TODO: In some cases the current ICU Islamic calendar implementation shows
michael@0 245 // a month as having 31 days. Since date parsing now uses range checks based
michael@0 246 // on the table below, we need to change the range for last day of month to
michael@0 247 // include 31 as a workaround until the implementation is fixed.
michael@0 248 static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
michael@0 249 // Minimum Greatest Least Maximum
michael@0 250 // Minimum Maximum
michael@0 251 { 0, 0, 0, 0}, // ERA
michael@0 252 { 1, 1, 5000000, 5000000}, // YEAR
michael@0 253 { 0, 0, 11, 11}, // MONTH
michael@0 254 { 1, 1, 50, 51}, // WEEK_OF_YEAR
michael@0 255 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
michael@0 256 { 1, 1, 29, 31}, // DAY_OF_MONTH - 31 to workaround for cal implementation bug, should be 30
michael@0 257 { 1, 1, 354, 355}, // DAY_OF_YEAR
michael@0 258 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
michael@0 259 { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH
michael@0 260 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
michael@0 261 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
michael@0 262 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
michael@0 263 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
michael@0 264 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
michael@0 265 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
michael@0 266 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
michael@0 267 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
michael@0 268 { 1, 1, 5000000, 5000000}, // YEAR_WOY
michael@0 269 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
michael@0 270 { 1, 1, 5000000, 5000000}, // EXTENDED_YEAR
michael@0 271 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
michael@0 272 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
michael@0 273 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
michael@0 274 };
michael@0 275
michael@0 276 /**
michael@0 277 * @draft ICU 2.4
michael@0 278 */
michael@0 279 int32_t IslamicCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
michael@0 280 return LIMITS[field][limitType];
michael@0 281 }
michael@0 282
michael@0 283 //-------------------------------------------------------------------------
michael@0 284 // Assorted calculation utilities
michael@0 285 //
michael@0 286
michael@0 287 /**
michael@0 288 * Determine whether a year is a leap year in the Islamic civil calendar
michael@0 289 */
michael@0 290 UBool IslamicCalendar::civilLeapYear(int32_t year)
michael@0 291 {
michael@0 292 return (14 + 11 * year) % 30 < 11;
michael@0 293 }
michael@0 294
michael@0 295 /**
michael@0 296 * Return the day # on which the given year starts. Days are counted
michael@0 297 * from the Hijri epoch, origin 0.
michael@0 298 */
michael@0 299 int32_t IslamicCalendar::yearStart(int32_t year) const{
michael@0 300 if (cType == CIVIL || cType == TBLA ||
michael@0 301 (cType == UMALQURA && year < UMALQURA_YEAR_START))
michael@0 302 {
michael@0 303 return (year-1)*354 + ClockMath::floorDivide((3+11*year),30);
michael@0 304 } else if(cType==ASTRONOMICAL){
michael@0 305 return trueMonthStart(12*(year-1));
michael@0 306 } else {
michael@0 307 int32_t ys = yearStart(UMALQURA_YEAR_START-1);
michael@0 308 ys+= handleGetYearLength(UMALQURA_YEAR_START-1);
michael@0 309 for(int i=UMALQURA_YEAR_START; i< year; i++){
michael@0 310 ys+= handleGetYearLength(i);
michael@0 311 }
michael@0 312 return ys;
michael@0 313 }
michael@0 314 }
michael@0 315
michael@0 316 /**
michael@0 317 * Return the day # on which the given month starts. Days are counted
michael@0 318 * from the Hijri epoch, origin 0.
michael@0 319 *
michael@0 320 * @param year The hijri year
michael@0 321 * @param year The hijri month, 0-based
michael@0 322 */
michael@0 323 int32_t IslamicCalendar::monthStart(int32_t year, int32_t month) const {
michael@0 324 if (cType == CIVIL || cType == TBLA) {
michael@0 325 return (int32_t)uprv_ceil(29.5*month)
michael@0 326 + (year-1)*354 + (int32_t)ClockMath::floorDivide((3+11*year),30);
michael@0 327 } else if(cType==ASTRONOMICAL){
michael@0 328 return trueMonthStart(12*(year-1) + month);
michael@0 329 } else {
michael@0 330 int32_t ms = yearStart(year);
michael@0 331 for(int i=0; i< month; i++){
michael@0 332 ms+= handleGetMonthLength(year, i);
michael@0 333 }
michael@0 334 return ms;
michael@0 335 }
michael@0 336 }
michael@0 337
michael@0 338 /**
michael@0 339 * Find the day number on which a particular month of the true/lunar
michael@0 340 * Islamic calendar starts.
michael@0 341 *
michael@0 342 * @param month The month in question, origin 0 from the Hijri epoch
michael@0 343 *
michael@0 344 * @return The day number on which the given month starts.
michael@0 345 */
michael@0 346 int32_t IslamicCalendar::trueMonthStart(int32_t month) const
michael@0 347 {
michael@0 348 UErrorCode status = U_ZERO_ERROR;
michael@0 349 int32_t start = CalendarCache::get(&gMonthCache, month, status);
michael@0 350
michael@0 351 if (start==0) {
michael@0 352 // Make a guess at when the month started, using the average length
michael@0 353 UDate origin = HIJRA_MILLIS
michael@0 354 + uprv_floor(month * CalendarAstronomer::SYNODIC_MONTH) * kOneDay;
michael@0 355
michael@0 356 // moonAge will fail due to memory allocation error
michael@0 357 double age = moonAge(origin, status);
michael@0 358 if (U_FAILURE(status)) {
michael@0 359 goto trueMonthStartEnd;
michael@0 360 }
michael@0 361
michael@0 362 if (age >= 0) {
michael@0 363 // The month has already started
michael@0 364 do {
michael@0 365 origin -= kOneDay;
michael@0 366 age = moonAge(origin, status);
michael@0 367 if (U_FAILURE(status)) {
michael@0 368 goto trueMonthStartEnd;
michael@0 369 }
michael@0 370 } while (age >= 0);
michael@0 371 }
michael@0 372 else {
michael@0 373 // Preceding month has not ended yet.
michael@0 374 do {
michael@0 375 origin += kOneDay;
michael@0 376 age = moonAge(origin, status);
michael@0 377 if (U_FAILURE(status)) {
michael@0 378 goto trueMonthStartEnd;
michael@0 379 }
michael@0 380 } while (age < 0);
michael@0 381 }
michael@0 382 start = (int32_t)ClockMath::floorDivide((origin - HIJRA_MILLIS), (double)kOneDay) + 1;
michael@0 383 CalendarCache::put(&gMonthCache, month, start, status);
michael@0 384 }
michael@0 385 trueMonthStartEnd :
michael@0 386 if(U_FAILURE(status)) {
michael@0 387 start = 0;
michael@0 388 }
michael@0 389 return start;
michael@0 390 }
michael@0 391
michael@0 392 /**
michael@0 393 * Return the "age" of the moon at the given time; this is the difference
michael@0 394 * in ecliptic latitude between the moon and the sun. This method simply
michael@0 395 * calls CalendarAstronomer.moonAge, converts to degrees,
michael@0 396 * and adjusts the result to be in the range [-180, 180].
michael@0 397 *
michael@0 398 * @param time The time at which the moon's age is desired,
michael@0 399 * in millis since 1/1/1970.
michael@0 400 */
michael@0 401 double IslamicCalendar::moonAge(UDate time, UErrorCode &status)
michael@0 402 {
michael@0 403 double age = 0;
michael@0 404
michael@0 405 umtx_lock(&astroLock);
michael@0 406 if(gIslamicCalendarAstro == NULL) {
michael@0 407 gIslamicCalendarAstro = new CalendarAstronomer();
michael@0 408 if (gIslamicCalendarAstro == NULL) {
michael@0 409 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 410 return age;
michael@0 411 }
michael@0 412 ucln_i18n_registerCleanup(UCLN_I18N_ISLAMIC_CALENDAR, calendar_islamic_cleanup);
michael@0 413 }
michael@0 414 gIslamicCalendarAstro->setTime(time);
michael@0 415 age = gIslamicCalendarAstro->getMoonAge();
michael@0 416 umtx_unlock(&astroLock);
michael@0 417
michael@0 418 // Convert to degrees and normalize...
michael@0 419 age = age * 180 / CalendarAstronomer::PI;
michael@0 420 if (age > 180) {
michael@0 421 age = age - 360;
michael@0 422 }
michael@0 423
michael@0 424 return age;
michael@0 425 }
michael@0 426
michael@0 427 //----------------------------------------------------------------------
michael@0 428 // Calendar framework
michael@0 429 //----------------------------------------------------------------------
michael@0 430
michael@0 431 /**
michael@0 432 * Return the length (in days) of the given month.
michael@0 433 *
michael@0 434 * @param year The hijri year
michael@0 435 * @param year The hijri month, 0-based
michael@0 436 * @draft ICU 2.4
michael@0 437 */
michael@0 438 int32_t IslamicCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
michael@0 439
michael@0 440 int32_t length = 0;
michael@0 441
michael@0 442 if (cType == CIVIL || cType == TBLA ||
michael@0 443 (cType == UMALQURA && (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END)) ) {
michael@0 444 length = 29 + (month+1) % 2;
michael@0 445 if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) {
michael@0 446 length++;
michael@0 447 }
michael@0 448 } else if(cType == ASTRONOMICAL){
michael@0 449 month = 12*(extendedYear-1) + month;
michael@0 450 length = trueMonthStart(month+1) - trueMonthStart(month) ;
michael@0 451 } else {
michael@0 452 length = getUmalqura_MonthLength(extendedYear - UMALQURA_YEAR_START, month);
michael@0 453 }
michael@0 454 return length;
michael@0 455 }
michael@0 456
michael@0 457 /**
michael@0 458 * Return the number of days in the given Islamic year
michael@0 459 * @draft ICU 2.4
michael@0 460 */
michael@0 461 int32_t IslamicCalendar::handleGetYearLength(int32_t extendedYear) const {
michael@0 462 if (cType == CIVIL || cType == TBLA ||
michael@0 463 (cType == UMALQURA && (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END)) ) {
michael@0 464 return 354 + (civilLeapYear(extendedYear) ? 1 : 0);
michael@0 465 } else if(cType == ASTRONOMICAL){
michael@0 466 int32_t month = 12*(extendedYear-1);
michael@0 467 return (trueMonthStart(month + 12) - trueMonthStart(month));
michael@0 468 } else {
michael@0 469 int len = 0;
michael@0 470 for(int i=0; i<12; i++)
michael@0 471 len += handleGetMonthLength(extendedYear, i);
michael@0 472 return len;
michael@0 473 }
michael@0 474 }
michael@0 475
michael@0 476 //-------------------------------------------------------------------------
michael@0 477 // Functions for converting from field values to milliseconds....
michael@0 478 //-------------------------------------------------------------------------
michael@0 479
michael@0 480 // Return JD of start of given month/year
michael@0 481 /**
michael@0 482 * @draft ICU 2.4
michael@0 483 */
michael@0 484 int32_t IslamicCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */) const {
michael@0 485 return monthStart(eyear, month) + 1948439;
michael@0 486 }
michael@0 487
michael@0 488 //-------------------------------------------------------------------------
michael@0 489 // Functions for converting from milliseconds to field values
michael@0 490 //-------------------------------------------------------------------------
michael@0 491
michael@0 492 /**
michael@0 493 * @draft ICU 2.4
michael@0 494 */
michael@0 495 int32_t IslamicCalendar::handleGetExtendedYear() {
michael@0 496 int32_t year;
michael@0 497 if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
michael@0 498 year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
michael@0 499 } else {
michael@0 500 year = internalGet(UCAL_YEAR, 1); // Default to year 1
michael@0 501 }
michael@0 502 return year;
michael@0 503 }
michael@0 504
michael@0 505 /**
michael@0 506 * Override Calendar to compute several fields specific to the Islamic
michael@0 507 * calendar system. These are:
michael@0 508 *
michael@0 509 * <ul><li>ERA
michael@0 510 * <li>YEAR
michael@0 511 * <li>MONTH
michael@0 512 * <li>DAY_OF_MONTH
michael@0 513 * <li>DAY_OF_YEAR
michael@0 514 * <li>EXTENDED_YEAR</ul>
michael@0 515 *
michael@0 516 * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
michael@0 517 * method is called. The getGregorianXxx() methods return Gregorian
michael@0 518 * calendar equivalents for the given Julian day.
michael@0 519 * @draft ICU 2.4
michael@0 520 */
michael@0 521 void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) {
michael@0 522 int32_t year, month, dayOfMonth, dayOfYear;
michael@0 523 UDate startDate;
michael@0 524 int32_t days = julianDay - CIVIL_EPOC;
michael@0 525
michael@0 526 if (cType == CIVIL || cType == TBLA) {
michael@0 527 if(cType == TBLA)
michael@0 528 days = julianDay - ASTRONOMICAL_EPOC;
michael@0 529 // Use the civil calendar approximation, which is just arithmetic
michael@0 530 year = (int)ClockMath::floorDivide( (double)(30 * days + 10646) , 10631.0 );
michael@0 531 month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 );
michael@0 532 month = month<11?month:11;
michael@0 533 startDate = monthStart(year, month);
michael@0 534 } else if(cType == ASTRONOMICAL){
michael@0 535 // Guess at the number of elapsed full months since the epoch
michael@0 536 int32_t months = (int32_t)uprv_floor((double)days / CalendarAstronomer::SYNODIC_MONTH);
michael@0 537
michael@0 538 startDate = uprv_floor(months * CalendarAstronomer::SYNODIC_MONTH);
michael@0 539
michael@0 540 double age = moonAge(internalGetTime(), status);
michael@0 541 if (U_FAILURE(status)) {
michael@0 542 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 543 return;
michael@0 544 }
michael@0 545 if ( days - startDate >= 25 && age > 0) {
michael@0 546 // If we're near the end of the month, assume next month and search backwards
michael@0 547 months++;
michael@0 548 }
michael@0 549
michael@0 550 // Find out the last time that the new moon was actually visible at this longitude
michael@0 551 // This returns midnight the night that the moon was visible at sunset.
michael@0 552 while ((startDate = trueMonthStart(months)) > days) {
michael@0 553 // If it was after the date in question, back up a month and try again
michael@0 554 months--;
michael@0 555 }
michael@0 556
michael@0 557 year = months / 12 + 1;
michael@0 558 month = months % 12;
michael@0 559 } else if(cType == UMALQURA) {
michael@0 560 int32_t umalquraStartdays = yearStart(UMALQURA_YEAR_START) ;
michael@0 561 if( days < umalquraStartdays){
michael@0 562 //Use Civil calculation
michael@0 563 year = (int)ClockMath::floorDivide( (double)(30 * days + 10646) , 10631.0 );
michael@0 564 month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 );
michael@0 565 month = month<11?month:11;
michael@0 566 startDate = monthStart(year, month);
michael@0 567 }else{
michael@0 568 int y =UMALQURA_YEAR_START-1, m =0;
michael@0 569 long d = 1;
michael@0 570 while(d > 0){
michael@0 571 y++;
michael@0 572 d = days - yearStart(y) +1;
michael@0 573 if(d == handleGetYearLength(y)){
michael@0 574 m=11;
michael@0 575 break;
michael@0 576 }else if(d < handleGetYearLength(y) ){
michael@0 577 int monthLen = handleGetMonthLength(y, m);
michael@0 578 m=0;
michael@0 579 while(d > monthLen){
michael@0 580 d -= monthLen;
michael@0 581 m++;
michael@0 582 monthLen = handleGetMonthLength(y, m);
michael@0 583 }
michael@0 584 break;
michael@0 585 }
michael@0 586 }
michael@0 587 year = y;
michael@0 588 month = m;
michael@0 589 }
michael@0 590 } else { // invalid 'civil'
michael@0 591 U_ASSERT(false); // should not get here, out of range
michael@0 592 year=month=0;
michael@0 593 }
michael@0 594
michael@0 595 dayOfMonth = (days - monthStart(year, month)) + 1;
michael@0 596
michael@0 597 // Now figure out the day of the year.
michael@0 598 dayOfYear = (days - monthStart(year, 0) + 1);
michael@0 599
michael@0 600
michael@0 601 internalSet(UCAL_ERA, 0);
michael@0 602 internalSet(UCAL_YEAR, year);
michael@0 603 internalSet(UCAL_EXTENDED_YEAR, year);
michael@0 604 internalSet(UCAL_MONTH, month);
michael@0 605 internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
michael@0 606 internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
michael@0 607 }
michael@0 608
michael@0 609 UBool
michael@0 610 IslamicCalendar::inDaylightTime(UErrorCode& status) const
michael@0 611 {
michael@0 612 // copied from GregorianCalendar
michael@0 613 if (U_FAILURE(status) || (&(getTimeZone()) == NULL && !getTimeZone().useDaylightTime()))
michael@0 614 return FALSE;
michael@0 615
michael@0 616 // Force an update of the state of the Calendar.
michael@0 617 ((IslamicCalendar*)this)->complete(status); // cast away const
michael@0 618
michael@0 619 return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE);
michael@0 620 }
michael@0 621
michael@0 622 /**
michael@0 623 * The system maintains a static default century start date and Year. They are
michael@0 624 * initialized the first time they are used. Once the system default century date
michael@0 625 * and year are set, they do not change.
michael@0 626 */
michael@0 627 static UDate gSystemDefaultCenturyStart = DBL_MIN;
michael@0 628 static int32_t gSystemDefaultCenturyStartYear = -1;
michael@0 629 static icu::UInitOnce gSystemDefaultCenturyInit = U_INITONCE_INITIALIZER;
michael@0 630
michael@0 631
michael@0 632 UBool IslamicCalendar::haveDefaultCentury() const
michael@0 633 {
michael@0 634 return TRUE;
michael@0 635 }
michael@0 636
michael@0 637 UDate IslamicCalendar::defaultCenturyStart() const
michael@0 638 {
michael@0 639 // lazy-evaluate systemDefaultCenturyStart
michael@0 640 umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
michael@0 641 return gSystemDefaultCenturyStart;
michael@0 642 }
michael@0 643
michael@0 644 int32_t IslamicCalendar::defaultCenturyStartYear() const
michael@0 645 {
michael@0 646 // lazy-evaluate systemDefaultCenturyStartYear
michael@0 647 umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
michael@0 648 return gSystemDefaultCenturyStartYear;
michael@0 649 }
michael@0 650
michael@0 651
michael@0 652 void U_CALLCONV
michael@0 653 IslamicCalendar::initializeSystemDefaultCentury()
michael@0 654 {
michael@0 655 // initialize systemDefaultCentury and systemDefaultCenturyYear based
michael@0 656 // on the current time. They'll be set to 80 years before
michael@0 657 // the current time.
michael@0 658 UErrorCode status = U_ZERO_ERROR;
michael@0 659 IslamicCalendar calendar(Locale("@calendar=islamic-civil"),status);
michael@0 660 if (U_SUCCESS(status)) {
michael@0 661 calendar.setTime(Calendar::getNow(), status);
michael@0 662 calendar.add(UCAL_YEAR, -80, status);
michael@0 663
michael@0 664 gSystemDefaultCenturyStart = calendar.getTime(status);
michael@0 665 gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status);
michael@0 666 }
michael@0 667 // We have no recourse upon failure unless we want to propagate the failure
michael@0 668 // out.
michael@0 669 }
michael@0 670
michael@0 671
michael@0 672
michael@0 673 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicCalendar)
michael@0 674
michael@0 675 U_NAMESPACE_END
michael@0 676
michael@0 677 #endif
michael@0 678

mercurial