intl/icu/source/i18n/islamcal.cpp

changeset 0
6474c204b198
     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 +

mercurial