Coverage Report

Created: 2025-06-13 06:38

/src/icu/icu4c/source/i18n/islamcal.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2016 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
/*
4
******************************************************************************
5
* Copyright (C) 2003-2015, International Business Machines Corporation
6
* and others. All Rights Reserved.
7
******************************************************************************
8
*
9
* File ISLAMCAL.H
10
*
11
* Modification History:
12
*
13
*   Date        Name        Description
14
*   10/14/2003  srl         ported from java IslamicCalendar
15
*****************************************************************************
16
*/
17
18
#include "islamcal.h"
19
20
#if !UCONFIG_NO_FORMATTING
21
22
#include "umutex.h"
23
#include <float.h>
24
#include "gregoimp.h" // Math
25
#include "astro.h" // CalendarAstronomer
26
#include "uhash.h"
27
#include "ucln_in.h"
28
#include "uassert.h"
29
30
static const UDate HIJRA_MILLIS = -42521587200000.0;    // 7/16/622 AD 00:00
31
32
// Debugging
33
#ifdef U_DEBUG_ISLAMCAL
34
# include <stdio.h>
35
# include <stdarg.h>
36
static void debug_islamcal_loc(const char *f, int32_t l)
37
{
38
    fprintf(stderr, "%s:%d: ", f, l);
39
}
40
41
static void debug_islamcal_msg(const char *pat, ...)
42
{
43
    va_list ap;
44
    va_start(ap, pat);
45
    vfprintf(stderr, pat, ap);
46
    fflush(stderr);
47
}
48
// must use double parens, i.e.:  U_DEBUG_ISLAMCAL_MSG(("four is: %d",4));
49
#define U_DEBUG_ISLAMCAL_MSG(x) {debug_islamcal_loc(__FILE__,__LINE__);debug_islamcal_msg x;}
50
#else
51
#define U_DEBUG_ISLAMCAL_MSG(x)
52
#endif
53
54
55
// --- The cache --
56
// cache of months
57
static icu::CalendarCache *gMonthCache = nullptr;
58
59
U_CDECL_BEGIN
60
0
static UBool calendar_islamic_cleanup() {
61
0
    if (gMonthCache) {
62
0
        delete gMonthCache;
63
0
        gMonthCache = nullptr;
64
0
    }
65
0
    return true;
66
0
}
67
U_CDECL_END
68
69
U_NAMESPACE_BEGIN
70
71
// Implementation of the IslamicCalendar class
72
73
/**
74
 * Friday EPOC
75
 */
76
static const int32_t CIVIL_EPOC = 1948440; // CE 622 July 16 Friday (Julian calendar) / CE 622 July 19 (Gregorian calendar)
77
78
/**
79
  * Thursday EPOC
80
  */
81
static const int32_t ASTRONOMICAL_EPOC = 1948439; // CE 622 July 15 Thursday (Julian calendar)
82
83
84
static const int32_t UMALQURA_YEAR_START = 1300;
85
static const int32_t UMALQURA_YEAR_END = 1600;
86
87
static const int UMALQURA_MONTHLENGTH[] = {
88
    //* 1300 -1302 */ "1010 1010 1010", "1101 0101 0100", "1110 1100 1001",
89
                            0x0AAA,           0x0D54,           0x0EC9,
90
    //* 1303 -1307 */ "0110 1101 0100", "0110 1110 1010", "0011 0110 1100", "1010 1010 1101", "0101 0101 0101",
91
                            0x06D4,           0x06EA,           0x036C,           0x0AAD,           0x0555,
92
    //* 1308 -1312 */ "0110 1010 1001", "0111 1001 0010", "1011 1010 1001", "0101 1101 0100", "1010 1101 1010",
93
                            0x06A9,           0x0792,           0x0BA9,           0x05D4,           0x0ADA,
94
    //* 1313 -1317 */ "0101 0101 1100", "1101 0010 1101", "0110 1001 0101", "0111 0100 1010", "1011 0101 0100",
95
                            0x055C,           0x0D2D,           0x0695,           0x074A,           0x0B54,
96
    //* 1318 -1322 */ "1011 0110 1010", "0101 1010 1101", "0100 1010 1110", "1010 0100 1111", "0101 0001 0111",
97
                            0x0B6A,           0x05AD,           0x04AE,           0x0A4F,           0x0517,
98
    //* 1323 -1327 */ "0110 1000 1011", "0110 1010 0101", "1010 1101 0101", "0010 1101 0110", "1001 0101 1011",
99
                            0x068B,           0x06A5,           0x0AD5,           0x02D6,           0x095B,
100
    //* 1328 -1332 */ "0100 1001 1101", "1010 0100 1101", "1101 0010 0110", "1101 1001 0101", "0101 1010 1100",
101
                            0x049D,           0x0A4D,           0x0D26,           0x0D95,           0x05AC,
102
    //* 1333 -1337 */ "1001 1011 0110", "0010 1011 1010", "1010 0101 1011", "0101 0010 1011", "1010 1001 0101",
103
                            0x09B6,           0x02BA,           0x0A5B,           0x052B,           0x0A95,
104
    //* 1338 -1342 */ "0110 1100 1010", "1010 1110 1001", "0010 1111 0100", "1001 0111 0110", "0010 1011 0110",
105
                            0x06CA,           0x0AE9,           0x02F4,           0x0976,           0x02B6,
106
    //* 1343 -1347 */ "1001 0101 0110", "1010 1100 1010", "1011 1010 0100", "1011 1101 0010", "0101 1101 1001",
107
                            0x0956,           0x0ACA,           0x0BA4,           0x0BD2,           0x05D9,
108
    //* 1348 -1352 */ "0010 1101 1100", "1001 0110 1101", "0101 0100 1101", "1010 1010 0101", "1011 0101 0010",
109
                            0x02DC,           0x096D,           0x054D,           0x0AA5,           0x0B52,
110
    //* 1353 -1357 */ "1011 1010 0101", "0101 1011 0100", "1001 1011 0110", "0101 0101 0111", "0010 1001 0111",
111
                            0x0BA5,           0x05B4,           0x09B6,           0x0557,           0x0297,
112
    //* 1358 -1362 */ "0101 0100 1011", "0110 1010 0011", "0111 0101 0010", "1011 0110 0101", "0101 0110 1010",
113
                            0x054B,           0x06A3,           0x0752,           0x0B65,           0x056A,
114
    //* 1363 -1367 */ "1010 1010 1011", "0101 0010 1011", "1100 1001 0101", "1101 0100 1010", "1101 1010 0101",
115
                            0x0AAB,           0x052B,           0x0C95,           0x0D4A,           0x0DA5,
116
    //* 1368 -1372 */ "0101 1100 1010", "1010 1101 0110", "1001 0101 0111", "0100 1010 1011", "1001 0100 1011",
117
                            0x05CA,           0x0AD6,           0x0957,           0x04AB,           0x094B,
118
    //* 1373 -1377 */ "1010 1010 0101", "1011 0101 0010", "1011 0110 1010", "0101 0111 0101", "0010 0111 0110",
119
                            0x0AA5,           0x0B52,           0x0B6A,           0x0575,           0x0276,
120
    //* 1378 -1382 */ "1000 1011 0111", "0100 0101 1011", "0101 0101 0101", "0101 1010 1001", "0101 1011 0100",
121
                            0x08B7,           0x045B,           0x0555,           0x05A9,           0x05B4,
122
    //* 1383 -1387 */ "1001 1101 1010", "0100 1101 1101", "0010 0110 1110", "1001 0011 0110", "1010 1010 1010",
123
                            0x09DA,           0x04DD,           0x026E,           0x0936,           0x0AAA,
124
    //* 1388 -1392 */ "1101 0101 0100", "1101 1011 0010", "0101 1101 0101", "0010 1101 1010", "1001 0101 1011",
125
                            0x0D54,           0x0DB2,           0x05D5,           0x02DA,           0x095B,
126
    //* 1393 -1397 */ "0100 1010 1011", "1010 0101 0101", "1011 0100 1001", "1011 0110 0100", "1011 0111 0001",
127
                            0x04AB,           0x0A55,           0x0B49,           0x0B64,           0x0B71,
128
    //* 1398 -1402 */ "0101 1011 0100", "1010 1011 0101", "1010 0101 0101", "1101 0010 0101", "1110 1001 0010",
129
                            0x05B4,           0x0AB5,           0x0A55,           0x0D25,           0x0E92,
130
    //* 1403 -1407 */ "1110 1100 1001", "0110 1101 0100", "1010 1110 1001", "1001 0110 1011", "0100 1010 1011",
131
                            0x0EC9,           0x06D4,           0x0AE9,           0x096B,           0x04AB,
132
    //* 1408 -1412 */ "1010 1001 0011", "1101 0100 1001", "1101 1010 0100", "1101 1011 0010", "1010 1011 1001",
133
                            0x0A93,           0x0D49,         0x0DA4,           0x0DB2,           0x0AB9,
134
    //* 1413 -1417 */ "0100 1011 1010", "1010 0101 1011", "0101 0010 1011", "1010 1001 0101", "1011 0010 1010",
135
                            0x04BA,           0x0A5B,           0x052B,           0x0A95,           0x0B2A,
136
    //* 1418 -1422 */ "1011 0101 0101", "0101 0101 1100", "0100 1011 1101", "0010 0011 1101", "1001 0001 1101",
137
                            0x0B55,           0x055C,           0x04BD,           0x023D,           0x091D,
138
    //* 1423 -1427 */ "1010 1001 0101", "1011 0100 1010", "1011 0101 1010", "0101 0110 1101", "0010 1011 0110",
139
                            0x0A95,           0x0B4A,           0x0B5A,           0x056D,           0x02B6,
140
    //* 1428 -1432 */ "1001 0011 1011", "0100 1001 1011", "0110 0101 0101", "0110 1010 1001", "0111 0101 0100",
141
                            0x093B,           0x049B,           0x0655,           0x06A9,           0x0754,
142
    //* 1433 -1437 */ "1011 0110 1010", "0101 0110 1100", "1010 1010 1101", "0101 0101 0101", "1011 0010 1001",
143
                            0x0B6A,           0x056C,           0x0AAD,           0x0555,           0x0B29,
144
    //* 1438 -1442 */ "1011 1001 0010", "1011 1010 1001", "0101 1101 0100", "1010 1101 1010", "0101 0101 1010",
145
                            0x0B92,           0x0BA9,           0x05D4,           0x0ADA,           0x055A,
146
    //* 1443 -1447 */ "1010 1010 1011", "0101 1001 0101", "0111 0100 1001", "0111 0110 0100", "1011 1010 1010",
147
                            0x0AAB,           0x0595,           0x0749,           0x0764,           0x0BAA,
148
    //* 1448 -1452 */ "0101 1011 0101", "0010 1011 0110", "1010 0101 0110", "1110 0100 1101", "1011 0010 0101",
149
                            0x05B5,           0x02B6,           0x0A56,           0x0E4D,           0x0B25,
150
    //* 1453 -1457 */ "1011 0101 0010", "1011 0110 1010", "0101 1010 1101", "0010 1010 1110", "1001 0010 1111",
151
                            0x0B52,           0x0B6A,           0x05AD,           0x02AE,           0x092F,
152
    //* 1458 -1462 */ "0100 1001 0111", "0110 0100 1011", "0110 1010 0101", "0110 1010 1100", "1010 1101 0110",
153
                            0x0497,           0x064B,           0x06A5,           0x06AC,           0x0AD6,
154
    //* 1463 -1467 */ "0101 0101 1101", "0100 1001 1101", "1010 0100 1101", "1101 0001 0110", "1101 1001 0101",
155
                            0x055D,           0x049D,           0x0A4D,           0x0D16,           0x0D95,
156
    //* 1468 -1472 */ "0101 1010 1010", "0101 1011 0101", "0010 1101 1010", "1001 0101 1011", "0100 1010 1101",
157
                            0x05AA,           0x05B5,           0x02DA,           0x095B,           0x04AD,
158
    //* 1473 -1477 */ "0101 1001 0101", "0110 1100 1010", "0110 1110 0100", "1010 1110 1010", "0100 1111 0101",
159
                            0x0595,           0x06CA,           0x06E4,           0x0AEA,           0x04F5,
160
    //* 1478 -1482 */ "0010 1011 0110", "1001 0101 0110", "1010 1010 1010", "1011 0101 0100", "1011 1101 0010",
161
                            0x02B6,           0x0956,           0x0AAA,           0x0B54,           0x0BD2,
162
    //* 1483 -1487 */ "0101 1101 1001", "0010 1110 1010", "1001 0110 1101", "0100 1010 1101", "1010 1001 0101",
163
                            0x05D9,           0x02EA,           0x096D,           0x04AD,           0x0A95,
164
    //* 1488 -1492 */ "1011 0100 1010", "1011 1010 0101", "0101 1011 0010", "1001 1011 0101", "0100 1101 0110",
165
                            0x0B4A,           0x0BA5,           0x05B2,           0x09B5,           0x04D6,
166
    //* 1493 -1497 */ "1010 1001 0111", "0101 0100 0111", "0110 1001 0011", "0111 0100 1001", "1011 0101 0101",
167
                            0x0A97,           0x0547,           0x0693,           0x0749,           0x0B55,
168
    //* 1498 -1508 */ "0101 0110 1010", "1010 0110 1011", "0101 0010 1011", "1010 1000 1011", "1101 0100 0110", "1101 1010 0011", "0101 1100 1010", "1010 1101 0110", "0100 1101 1011", "0010 0110 1011", "1001 0100 1011",
169
                            0x056A,           0x0A6B,           0x052B,           0x0A8B,           0x0D46,           0x0DA3,           0x05CA,           0x0AD6,           0x04DB,           0x026B,           0x094B,
170
    //* 1509 -1519 */ "1010 1010 0101", "1011 0101 0010", "1011 0110 1001", "0101 0111 0101", "0001 0111 0110", "1000 1011 0111", "0010 0101 1011", "0101 0010 1011", "0101 0110 0101", "0101 1011 0100", "1001 1101 1010",
171
                            0x0AA5,           0x0B52,           0x0B69,           0x0575,           0x0176,           0x08B7,           0x025B,           0x052B,           0x0565,           0x05B4,           0x09DA,
172
    //* 1520 -1530 */ "0100 1110 1101", "0001 0110 1101", "1000 1011 0110", "1010 1010 0110", "1101 0101 0010", "1101 1010 1001", "0101 1101 0100", "1010 1101 1010", "1001 0101 1011", "0100 1010 1011", "0110 0101 0011",
173
                            0x04ED,           0x016D,           0x08B6,           0x0AA6,           0x0D52,           0x0DA9,           0x05D4,           0x0ADA,           0x095B,           0x04AB,           0x0653,
174
    //* 1531 -1541 */ "0111 0010 1001", "0111 0110 0010", "1011 1010 1001", "0101 1011 0010", "1010 1011 0101", "0101 0101 0101", "1011 0010 0101", "1101 1001 0010", "1110 1100 1001", "0110 1101 0010", "1010 1110 1001",
175
                            0x0729,           0x0762,           0x0BA9,           0x05B2,           0x0AB5,           0x0555,           0x0B25,           0x0D92,           0x0EC9,           0x06D2,           0x0AE9,
176
    //* 1542 -1552 */ "0101 0110 1011", "0100 1010 1011", "1010 0101 0101", "1101 0010 1001", "1101 0101 0100", "1101 1010 1010", "1001 1011 0101", "0100 1011 1010", "1010 0011 1011", "0100 1001 1011", "1010 0100 1101",
177
                            0x056B,           0x04AB,           0x0A55,           0x0D29,           0x0D54,           0x0DAA,           0x09B5,           0x04BA,           0x0A3B,           0x049B,           0x0A4D,
178
    //* 1553 -1563 */ "1010 1010 1010", "1010 1101 0101", "0010 1101 1010", "1001 0101 1101", "0100 0101 1110", "1010 0010 1110", "1100 1001 1010", "1101 0101 0101", "0110 1011 0010", "0110 1011 1001", "0100 1011 1010",
179
                            0x0AAA,           0x0AD5,           0x02DA,           0x095D,           0x045E,           0x0A2E,           0x0C9A,           0x0D55,           0x06B2,           0x06B9,           0x04BA,
180
    //* 1564 -1574 */ "1010 0101 1101", "0101 0010 1101", "1010 1001 0101", "1011 0101 0010", "1011 1010 1000", "1011 1011 0100", "0101 1011 1001", "0010 1101 1010", "1001 0101 1010", "1011 0100 1010", "1101 1010 0100",
181
                            0x0A5D,           0x052D,           0x0A95,           0x0B52,           0x0BA8,           0x0BB4,           0x05B9,           0x02DA,           0x095A,           0x0B4A,           0x0DA4,
182
    //* 1575 -1585 */ "1110 1101 0001", "0110 1110 1000", "1011 0110 1010", "0101 0110 1101", "0101 0011 0101", "0110 1001 0101", "1101 0100 1010", "1101 1010 1000", "1101 1101 0100", "0110 1101 1010", "0101 0101 1011",
183
                            0x0ED1,           0x06E8,           0x0B6A,           0x056D,           0x0535,           0x0695,           0x0D4A,           0x0DA8,           0x0DD4,           0x06DA,           0x055B,
184
    //* 1586 -1596 */ "0010 1001 1101", "0110 0010 1011", "1011 0001 0101", "1011 0100 1010", "1011 1001 0101", "0101 1010 1010", "1010 1010 1110", "1001 0010 1110", "1100 1000 1111", "0101 0010 0111", "0110 1001 0101",
185
                            0x029D,           0x062B,           0x0B15,           0x0B4A,           0x0B95,           0x05AA,           0x0AAE,           0x092E,           0x0C8F,           0x0527,           0x0695,
186
    //* 1597 -1600 */ "0110 1010 1010", "1010 1101 0110", "0101 0101 1101", "0010 1001 1101", };
187
                            0x06AA,           0x0AD6,           0x055D,           0x029D
188
};
189
190
//-------------------------------------------------------------------------
191
// Constructors...
192
//-------------------------------------------------------------------------
193
194
3
const char *IslamicCalendar::getType() const {
195
3
    return "islamic";
196
3
}
197
198
2.46k
IslamicCalendar* IslamicCalendar::clone() const {
199
2.46k
    return new IslamicCalendar(*this);
200
2.46k
}
201
202
IslamicCalendar::IslamicCalendar(const Locale& aLocale, UErrorCode& success)
203
413
:   Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success)
204
413
{
205
413
}
206
207
IslamicCalendar::~IslamicCalendar()
208
5.97k
{
209
5.97k
}
210
//-------------------------------------------------------------------------
211
// Minimum / Maximum access functions
212
//-------------------------------------------------------------------------
213
214
// Note: Current IslamicCalendar implementation does not work
215
// well with negative years.
216
217
// TODO: In some cases the current ICU Islamic calendar implementation shows
218
// a month as having 31 days. Since date parsing now uses range checks based
219
// on the table below, we need to change the range for last day of month to
220
// include 31 as a workaround until the implementation is fixed.
221
static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
222
    // Minimum  Greatest    Least  Maximum
223
    //           Minimum  Maximum
224
    {        0,        0,        0,        0}, // ERA
225
    {        1,        1,  5000000,  5000000}, // YEAR
226
    {        0,        0,       11,       11}, // MONTH
227
    {        1,        1,       50,       51}, // WEEK_OF_YEAR
228
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
229
    {        1,        1,       29,       31}, // DAY_OF_MONTH - 31 to workaround for cal implementation bug, should be 30
230
    {        1,        1,      354,      355}, // DAY_OF_YEAR
231
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
232
    {       -1,       -1,        5,        5}, // DAY_OF_WEEK_IN_MONTH
233
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
234
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
235
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
236
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
237
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
238
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
239
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
240
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
241
    {        1,        1,  5000000,  5000000}, // YEAR_WOY
242
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
243
    {        1,        1,  5000000,  5000000}, // EXTENDED_YEAR
244
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
245
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
246
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
247
    {        0,        0,       11,       11}, // ORDINAL_MONTH
248
};
249
250
/**
251
* @draft ICU 2.4
252
*/
253
15.9k
int32_t IslamicCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
254
15.9k
    return LIMITS[field][limitType];
255
15.9k
}
256
257
//-------------------------------------------------------------------------
258
// Assorted calculation utilities
259
//
260
261
namespace {
262
263
// we could compress this down more if we need to
264
static const int8_t umAlQuraYrStartEstimateFix[] = {
265
     0,  0, -1,  0, -1,  0,  0,  0,  0,  0, // 1300..
266
    -1,  0,  0,  0,  0,  0,  0,  0, -1,  0, // 1310..
267
     1,  0,  1,  1,  0,  0,  0,  0,  1,  0, // 1320..
268
     0,  0,  0,  0,  0,  0,  1,  0,  0,  0, // 1330..
269
     0,  0,  1,  0,  0, -1, -1,  0,  0,  0, // 1340..
270
     1,  0,  0, -1,  0,  0,  0,  1,  1,  0, // 1350..
271
     0,  0,  0,  0,  0,  0,  0, -1,  0,  0, // 1360..
272
     0,  1,  1,  0,  0, -1,  0,  1,  0,  1, // 1370..
273
     1,  0,  0, -1,  0,  1,  0,  0,  0, -1, // 1380..
274
     0,  1,  0,  1,  0,  0,  0, -1,  0,  0, // 1390..
275
     0,  0, -1, -1,  0, -1,  0,  1,  0,  0, // 1400..
276
     0, -1,  0,  0,  0,  1,  0,  0,  0,  0, // 1410..
277
     0,  1,  0,  0, -1, -1,  0,  0,  0,  1, // 1420..
278
     0,  0, -1, -1,  0, -1,  0,  0, -1, -1, // 1430..
279
     0, -1,  0, -1,  0,  0, -1, -1,  0,  0, // 1440..
280
     0,  0,  0,  0, -1,  0,  1,  0,  1,  1, // 1450..
281
     0,  0, -1,  0,  1,  0,  0,  0,  0,  0, // 1460..
282
     1,  0,  1,  0,  0,  0, -1,  0,  1,  0, // 1470..
283
     0, -1, -1,  0,  0,  0,  1,  0,  0,  0, // 1480..
284
     0,  0,  0,  0,  1,  0,  0,  0,  0,  0, // 1490..
285
     1,  0,  0, -1,  0,  0,  0,  1,  1,  0, // 1500..
286
     0, -1,  0,  1,  0,  1,  1,  0,  0,  0, // 1510..
287
     0,  1,  0,  0,  0, -1,  0,  0,  0,  1, // 1520..
288
     0,  0,  0, -1,  0,  0,  0,  0,  0, -1, // 1530..
289
     0, -1,  0,  1,  0,  0,  0, -1,  0,  1, // 1540..
290
     0,  1,  0,  0,  0,  0,  0,  1,  0,  0, // 1550..
291
    -1,  0,  0,  0,  0,  1,  0,  0,  0, -1, // 1560..
292
     0,  0,  0,  0, -1, -1,  0, -1,  0,  1, // 1570..
293
     0,  0, -1, -1,  0,  0,  1,  1,  0,  0, // 1580..
294
    -1,  0,  0,  0,  0,  1,  0,  0,  0,  0, // 1590..
295
     1 // 1600
296
};
297
298
/**
299
* Determine whether a year is a leap year in the Islamic civil calendar
300
*/
301
31.2k
inline bool civilLeapYear(int32_t year) {
302
31.2k
    return (14 + 11 * year) % 30 < 11;
303
31.2k
}
304
305
int32_t trueMonthStart(int32_t month, UErrorCode& status);
306
307
} // namespace
308
309
/**
310
* Return the day # on which the given year starts.  Days are counted
311
* from the Hijri epoch, origin 0.
312
*/
313
0
int64_t IslamicCalendar::yearStart(int32_t year, UErrorCode& status) const {
314
0
    return trueMonthStart(12*(year-1), status);
315
0
}
316
317
/**
318
* Return the day # on which the given month starts.  Days are counted
319
* from the Hijri epoch, origin 0.
320
*
321
* @param year  The hijri year
322
* @param month The hijri month, 0-based (assumed to be in range 0..11)
323
*/
324
25.3k
int64_t IslamicCalendar::monthStart(int32_t year, int32_t month, UErrorCode& status) const {
325
25.3k
    if (U_FAILURE(status)) {
326
0
        return 0;
327
0
    }
328
25.3k
    int32_t temp;
329
25.3k
    if (uprv_add32_overflow(year, -1, &temp) ||
330
25.3k
        uprv_mul32_overflow(temp, 12, &temp) ||
331
25.3k
        uprv_add32_overflow(temp, month, &month)) {
332
87
        status = U_ILLEGAL_ARGUMENT_ERROR;
333
87
        return 0;
334
87
    }
335
336
25.2k
    return trueMonthStart(month, status);
337
25.3k
}
338
339
namespace {
340
/**
341
 * Return the "age" of the moon at the given time; this is the difference
342
 * in ecliptic latitude between the moon and the sun.  This method simply
343
 * calls CalendarAstronomer.moonAge, converts to degrees,
344
 * and adjusts the resultto be in the range [-180, 180].
345
 *
346
 * @param time  The time at which the moon's age is desired,
347
 *             in millis since 1/1/1970.
348
 */
349
double moonAge(UDate time);
350
351
/**
352
* Find the day number on which a particular month of the true/lunar
353
* Islamic calendar starts.
354
*
355
* @param month The month in question, origin 0 from the Hijri epoch
356
*
357
* @return The day number on which the given month starts.
358
*/
359
61.5k
int32_t trueMonthStart(int32_t month, UErrorCode& status) {
360
61.5k
    if (U_FAILURE(status)) {
361
190
        return 0;
362
190
    }
363
61.3k
    ucln_i18n_registerCleanup(UCLN_I18N_ISLAMIC_CALENDAR, calendar_islamic_cleanup);
364
61.3k
    int64_t start = CalendarCache::get(&gMonthCache, month, status);
365
366
61.3k
    if (U_SUCCESS(status) && start==0) {
367
        // Make a guess at when the month started, using the average length
368
6.91k
        UDate origin = HIJRA_MILLIS 
369
6.91k
            + uprv_floor(month * CalendarAstronomer::SYNODIC_MONTH) * kOneDay;
370
371
        // moonAge will fail due to memory allocation error
372
6.91k
        double age = moonAge(origin);
373
374
6.91k
        if (age >= 0) {
375
            // The month has already started
376
28.1k
            do {
377
28.1k
                origin -= kOneDay;
378
28.1k
                age = moonAge(origin);
379
28.1k
            } while (age >= 0);
380
4.81k
        }
381
2.10k
        else {
382
            // Preceding month has not ended yet.
383
20.8k
            do {
384
20.8k
                origin += kOneDay;
385
20.8k
                age = moonAge(origin);
386
20.8k
            } while (age < 0);
387
2.10k
        }
388
6.91k
        start = ClockMath::floorDivideInt64(
389
6.91k
            static_cast<int64_t>(static_cast<int64_t>(origin) - HIJRA_MILLIS), static_cast<int64_t>(kOneDay)) + 1;
390
6.91k
        CalendarCache::put(&gMonthCache, month, start, status);
391
6.91k
    }
392
61.3k
    if(U_FAILURE(status)) {
393
0
        start = 0;
394
0
    }
395
61.3k
    return start;
396
61.5k
}
397
398
66.3k
double moonAge(UDate time) {
399
    // Convert to degrees and normalize...
400
66.3k
    double age = CalendarAstronomer(time).getMoonAge() * 180 / CalendarAstronomer::PI;
401
66.3k
    if (age > 180) {
402
28.5k
        age = age - 360;
403
28.5k
    }
404
405
66.3k
    return age;
406
66.3k
}
407
408
}  // namespace
409
//----------------------------------------------------------------------
410
// Calendar framework
411
//----------------------------------------------------------------------
412
413
/**
414
* Return the length (in days) of the given month.
415
*
416
* @param year  The hijri year
417
* @param year  The hijri month, 0-based
418
* @draft ICU 2.4
419
*/
420
int32_t IslamicCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month,
421
2.22k
                                              UErrorCode& status) const {
422
2.22k
    month = 12*(extendedYear-1) + month;
423
2.22k
    int32_t len = trueMonthStart(month+1, status) - trueMonthStart(month, status) ;
424
2.22k
    if (U_FAILURE(status)) {
425
77
        return 0;
426
77
    }
427
2.14k
    return len;
428
2.22k
}
429
430
namespace {
431
432
10.3k
int32_t yearLength(int32_t extendedYear, UErrorCode& status) {
433
10.3k
    int32_t month = 12*(extendedYear-1);
434
10.3k
    int32_t length = trueMonthStart(month + 12, status) - trueMonthStart(month, status);
435
10.3k
    if (U_FAILURE(status)) {
436
18
        return 0;
437
18
    }
438
10.3k
    return length;
439
10.3k
}
440
441
} // namepsace
442
/**
443
* Return the number of days in the given Islamic year
444
* @draft ICU 2.4
445
*/
446
10.3k
int32_t IslamicCalendar::handleGetYearLength(int32_t extendedYear, UErrorCode& status) const {
447
10.3k
    return yearLength(extendedYear, status);
448
10.3k
}
449
450
//-------------------------------------------------------------------------
451
// Functions for converting from field values to milliseconds....
452
//-------------------------------------------------------------------------
453
454
// Return JD of start of given month/year
455
// Calendar says:
456
// Get the Julian day of the day BEFORE the start of this year.
457
// If useMonth is true, get the day before the start of the month.
458
// Hence the -1
459
/**
460
* @draft ICU 2.4
461
*/
462
int64_t IslamicCalendar::handleComputeMonthStart(int32_t eyear, int32_t month,
463
                                                 UBool /* useMonth */,
464
10.6k
                                                 UErrorCode& status) const {
465
10.6k
    if (U_FAILURE(status)) {
466
0
        return 0;
467
0
    }
468
    // This may be called by Calendar::handleComputeJulianDay with months out of the range
469
    // 0..11. Need to handle that here since monthStart requires months in the range 0.11.
470
10.6k
    if (month > 11) {
471
2.17k
        if (uprv_add32_overflow(eyear, (month / 12), &eyear)) {
472
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
473
0
            return 0;
474
0
        }
475
2.17k
        month %= 12;
476
8.46k
    } else if (month < 0) {
477
3.47k
        month++;
478
3.47k
        if (uprv_add32_overflow(eyear, (month / 12) - 1, &eyear)) {
479
10
            status = U_ILLEGAL_ARGUMENT_ERROR;
480
10
            return 0;
481
10
        }
482
3.46k
        month = (month % 12) + 11;
483
3.46k
    }
484
10.6k
    return monthStart(eyear, month, status) + getEpoc() - 1;
485
10.6k
}
486
487
//-------------------------------------------------------------------------
488
// Functions for converting from milliseconds to field values
489
//-------------------------------------------------------------------------
490
491
/**
492
* @draft ICU 2.4
493
*/
494
10.1k
int32_t IslamicCalendar::handleGetExtendedYear(UErrorCode& /* status */) {
495
10.1k
    if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
496
8.24k
        return internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
497
8.24k
    }
498
1.94k
    return internalGet(UCAL_YEAR, 1); // Default to year 1
499
10.1k
}
500
501
/**
502
* Override Calendar to compute several fields specific to the Islamic
503
* calendar system.  These are:
504
*
505
* <ul><li>ERA
506
* <li>YEAR
507
* <li>MONTH
508
* <li>DAY_OF_MONTH
509
* <li>DAY_OF_YEAR
510
* <li>EXTENDED_YEAR</ul>
511
* 
512
* The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
513
* method is called. The getGregorianXxx() methods return Gregorian
514
* calendar equivalents for the given Julian day.
515
* @draft ICU 2.4
516
*/
517
10.3k
void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) {
518
10.3k
    if (U_FAILURE(status)) {
519
19
        return;
520
19
    }
521
10.3k
    int32_t days = julianDay - getEpoc();
522
523
    // Guess at the number of elapsed full months since the epoch
524
10.3k
    int32_t month = static_cast<int32_t>(uprv_floor(static_cast<double>(days) / CalendarAstronomer::SYNODIC_MONTH));
525
526
10.3k
    int32_t startDate = static_cast<int32_t>(uprv_floor(month * CalendarAstronomer::SYNODIC_MONTH));
527
528
10.3k
    double age = moonAge(internalGetTime());
529
10.3k
    if ( days - startDate >= 25 && age > 0) {
530
        // If we're near the end of the month, assume next month and search backwards
531
4.55k
        month++;
532
4.55k
    }
533
534
    // Find out the last time that the new moon was actually visible at this longitude
535
    // This returns midnight the night that the moon was visible at sunset.
536
11.0k
    while ((startDate = trueMonthStart(month, status)) > days) {
537
680
        if (U_FAILURE(status)) {
538
0
            return;
539
0
        }
540
        // If it was after the date in question, back up a month and try again
541
680
        month--;
542
680
    }
543
10.3k
    if (U_FAILURE(status)) {
544
0
        return;
545
0
    }
546
547
10.3k
    int32_t year = month >=  0 ? ((month / 12) + 1) : ((month + 1 ) / 12);
548
10.3k
    month = ((month % 12) + 12 ) % 12;
549
10.3k
    int64_t dayOfMonth = (days - monthStart(year, month, status)) + 1;
550
10.3k
    if (U_FAILURE(status)) {
551
0
        return;
552
0
    }
553
10.3k
    if (dayOfMonth > INT32_MAX || dayOfMonth < INT32_MIN) {
554
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
555
0
        return;
556
0
    }
557
558
    // Now figure out the day of the year.
559
10.3k
    int64_t dayOfYear = (days - monthStart(year, 0, status)) + 1;
560
10.3k
    if (U_FAILURE(status)) {
561
0
        return;
562
0
    }
563
10.3k
    if (dayOfYear > INT32_MAX || dayOfYear < INT32_MIN) {
564
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
565
0
        return;
566
0
    }
567
568
10.3k
    internalSet(UCAL_ERA, 0);
569
10.3k
    internalSet(UCAL_YEAR, year);
570
10.3k
    internalSet(UCAL_EXTENDED_YEAR, year);
571
10.3k
    internalSet(UCAL_MONTH, month);
572
10.3k
    internalSet(UCAL_ORDINAL_MONTH, month);
573
10.3k
    internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
574
10.3k
    internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
575
10.3k
}
576
577
36.4k
int32_t IslamicCalendar::getEpoc() const {
578
36.4k
    return CIVIL_EPOC;
579
36.4k
}
580
581
0
static int32_t gregoYearFromIslamicStart(int32_t year) {
582
    // ad hoc conversion, improve under #10752
583
    // rough est for now, ok for grego 1846-2138,
584
    // otherwise occasionally wrong (for 3% of years)
585
0
    int cycle, offset, shift = 0;
586
0
    if (year >= 1397) {
587
0
        cycle = (year - 1397) / 67;
588
0
        offset = (year - 1397) % 67;
589
0
        shift = 2*cycle + ((offset >= 33)? 1: 0);
590
0
    } else {
591
0
        cycle = (year - 1396) / 67 - 1;
592
0
        offset = -(year - 1396) % 67;
593
0
        shift = 2*cycle + ((offset <= 33)? 1: 0);
594
0
    }
595
0
    return year + 579 - shift;
596
0
}
597
598
int32_t IslamicCalendar::getRelatedYear(UErrorCode &status) const
599
0
{
600
0
    int32_t year = get(UCAL_EXTENDED_YEAR, status);
601
0
    if (U_FAILURE(status)) {
602
0
        return 0;
603
0
    }
604
0
    return gregoYearFromIslamicStart(year);
605
0
}
606
607
void IslamicCalendar::setRelatedYear(int32_t year)
608
0
{
609
    // ad hoc conversion, improve under #10752
610
    // rough est for now, ok for grego 1846-2138,
611
    // otherwise occasionally wrong (for 3% of years)
612
0
    int cycle, offset, shift = 0;
613
0
    if (year >= 1977) {
614
0
        cycle = (year - 1977) / 65;
615
0
        offset = (year - 1977) % 65;
616
0
        shift = 2*cycle + ((offset >= 32)? 1: 0);
617
0
    } else {
618
0
        cycle = (year - 1976) / 65 - 1;
619
0
        offset = -(year - 1976) % 65;
620
0
        shift = 2*cycle + ((offset <= 32)? 1: 0);
621
0
    }
622
0
    year = year - 579 + shift;
623
0
    set(UCAL_EXTENDED_YEAR, year);
624
0
}
625
626
IMPL_SYSTEM_DEFAULT_CENTURY(IslamicCalendar, "@calendar=islamic-civil")
627
628
bool
629
IslamicCalendar::inTemporalLeapYear(UErrorCode &status) const
630
0
{
631
0
    int32_t days = getActualMaximum(UCAL_DAY_OF_YEAR, status);
632
0
    if (U_FAILURE(status)) {
633
0
        return false;
634
0
    }
635
0
    return days == 355;
636
0
}
637
638
/*****************************************************************************
639
 * IslamicCivilCalendar
640
 *****************************************************************************/
641
IslamicCivilCalendar::IslamicCivilCalendar(const Locale& aLocale, UErrorCode& success)
642
256
    : IslamicCalendar(aLocale, success)
643
256
{
644
256
}
645
646
IslamicCivilCalendar::~IslamicCivilCalendar()
647
{
648
}
649
650
0
const char *IslamicCivilCalendar::getType() const {
651
0
    return "islamic-civil";
652
0
}
653
654
869
IslamicCivilCalendar* IslamicCivilCalendar::clone() const {
655
869
    return new IslamicCivilCalendar(*this);
656
869
}
657
658
/**
659
* Return the day # on which the given year starts.  Days are counted
660
* from the Hijri epoch, origin 0.
661
*/
662
45.5k
int64_t IslamicCivilCalendar::yearStart(int32_t year, UErrorCode& /* status */) const {
663
45.5k
    return 354LL * (year-1LL) + ClockMath::floorDivideInt64(3 + 11LL * year, 30LL);
664
45.5k
}
665
666
/**
667
* Return the day # on which the given month starts.  Days are counted
668
* from the Hijri epoch, origin 0.
669
*
670
* @param year  The hijri year
671
* @param month The hijri month, 0-based (assumed to be in range 0..11)
672
*/
673
13.8k
int64_t IslamicCivilCalendar::monthStart(int32_t year, int32_t month, UErrorCode& /*status*/) const {
674
    // This does not handle months out of the range 0..11
675
13.8k
    return static_cast<int64_t>(
676
13.8k
        uprv_ceil(29.5*month) + 354LL*(year-1LL) +
677
13.8k
        ClockMath::floorDivideInt64(
678
13.8k
             11LL*static_cast<int64_t>(year) + 3LL, 30LL));
679
13.8k
}
680
681
/**
682
* Return the length (in days) of the given month.
683
*
684
* @param year  The hijri year
685
* @param year  The hijri month, 0-based
686
* @draft ICU 2.4
687
*/
688
int32_t IslamicCivilCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month,
689
60.2k
                                                   UErrorCode& /* status */) const {
690
60.2k
    int32_t length = 29 + (month+1) % 2;
691
60.2k
    if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) {
692
663
        length++;
693
663
    }
694
60.2k
    return length;
695
60.2k
}
696
697
/**
698
* Return the number of days in the given Islamic year
699
* @draft ICU 2.4
700
*/
701
29.9k
int32_t IslamicCivilCalendar::handleGetYearLength(int32_t extendedYear, UErrorCode& status) const {
702
29.9k
    if (U_FAILURE(status)) return 0;
703
29.9k
    return 354 + (civilLeapYear(extendedYear) ? 1 : 0);
704
29.9k
}
705
706
/**
707
* Override Calendar to compute several fields specific to the Islamic
708
* calendar system.  These are:
709
*
710
* <ul><li>ERA
711
* <li>YEAR
712
* <li>MONTH
713
* <li>DAY_OF_MONTH
714
* <li>DAY_OF_YEAR
715
* <li>EXTENDED_YEAR</ul>
716
* 
717
* The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
718
* method is called. The getGregorianXxx() methods return Gregorian
719
* calendar equivalents for the given Julian day.
720
* @draft ICU 2.4
721
*/
722
10.4k
void IslamicCivilCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) {
723
10.4k
    if (U_FAILURE(status)) {
724
25
        return;
725
25
    }
726
10.3k
    int32_t days = julianDay - getEpoc();
727
728
    // Use the civil calendar approximation, which is just arithmetic
729
10.3k
    int64_t year  =
730
10.3k
        ClockMath::floorDivideInt64(30LL * days + 10646LL, 10631LL);
731
10.3k
    int32_t month = static_cast<int32_t>(
732
10.3k
        uprv_ceil((days - 29 - yearStart(year, status)) / 29.5 ));
733
10.3k
    if (U_FAILURE(status)) {
734
0
        return;
735
0
    }
736
10.3k
    month = month<11?month:11;
737
738
10.3k
    int64_t dayOfMonth = (days - monthStart(year, month, status)) + 1;
739
10.3k
    if (U_FAILURE(status)) {
740
0
        return;
741
0
    }
742
10.3k
    if (dayOfMonth > INT32_MAX || dayOfMonth < INT32_MIN) {
743
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
744
0
        return;
745
0
    }
746
747
    // Now figure out the day of the year.
748
10.3k
    int64_t dayOfYear = (days - monthStart(year, 0, status)) + 1;
749
10.3k
    if (U_FAILURE(status)) {
750
0
        return;
751
0
    }
752
10.3k
    if (dayOfYear > INT32_MAX || dayOfYear < INT32_MIN) {
753
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
754
0
        return;
755
0
    }
756
757
10.3k
    internalSet(UCAL_ERA, 0);
758
10.3k
    internalSet(UCAL_YEAR, year);
759
10.3k
    internalSet(UCAL_EXTENDED_YEAR, year);
760
10.3k
    internalSet(UCAL_MONTH, month);
761
10.3k
    internalSet(UCAL_ORDINAL_MONTH, month);
762
10.3k
    internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
763
10.3k
    internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
764
10.3k
}
765
/*****************************************************************************
766
 * IslamicTBLACalendar
767
 *****************************************************************************/
768
IslamicTBLACalendar::IslamicTBLACalendar(const Locale& aLocale, UErrorCode& success)
769
60
    : IslamicCivilCalendar(aLocale, success)
770
60
{
771
60
}
772
773
IslamicTBLACalendar::~IslamicTBLACalendar()
774
{
775
}
776
777
0
const char *IslamicTBLACalendar::getType() const {
778
0
    return "islamic-tbla";
779
0
}
780
781
618
IslamicTBLACalendar* IslamicTBLACalendar::clone() const {
782
618
    return new IslamicTBLACalendar(*this);
783
618
}
784
785
3.84k
int32_t IslamicTBLACalendar::getEpoc() const {
786
3.84k
    return ASTRONOMICAL_EPOC;
787
3.84k
}
788
789
/*****************************************************************************
790
 * IslamicUmalquraCalendar
791
 *****************************************************************************/
792
IslamicUmalquraCalendar::IslamicUmalquraCalendar(const Locale& aLocale, UErrorCode& success)
793
124
    : IslamicCivilCalendar(aLocale, success)
794
124
{
795
124
}
796
797
IslamicUmalquraCalendar::~IslamicUmalquraCalendar()
798
{
799
}
800
801
0
const char *IslamicUmalquraCalendar::getType() const {
802
0
    return "islamic-umalqura";
803
0
}
804
805
1.72k
IslamicUmalquraCalendar* IslamicUmalquraCalendar::clone() const {
806
1.72k
    return new IslamicUmalquraCalendar(*this);
807
1.72k
}
808
809
/**
810
* Return the day # on which the given year starts.  Days are counted
811
* from the Hijri epoch, origin 0.
812
*/
813
43.7k
int64_t IslamicUmalquraCalendar::yearStart(int32_t year, UErrorCode& status) const {
814
43.7k
    if (year < UMALQURA_YEAR_START || year > UMALQURA_YEAR_END) {
815
39.9k
        return IslamicCivilCalendar::yearStart(year, status);
816
39.9k
    }
817
3.78k
    year -= UMALQURA_YEAR_START;
818
    // rounded least-squares fit of the dates previously calculated from UMALQURA_MONTHLENGTH iteration
819
3.78k
    int64_t yrStartLinearEstimate = static_cast<int64_t>(
820
3.78k
        (354.36720 * static_cast<double>(year)) + 460322.05 + 0.5);
821
    // need a slight correction to some
822
3.78k
    return yrStartLinearEstimate + umAlQuraYrStartEstimateFix[year];
823
43.7k
}
824
825
/**
826
* Return the day # on which the given month starts.  Days are counted
827
* from the Hijri epoch, origin 0.
828
*
829
* @param year  The hijri year
830
* @param month The hijri month, 0-based (assumed to be in range 0..11)
831
*/
832
21.2k
int64_t IslamicUmalquraCalendar::monthStart(int32_t year, int32_t month, UErrorCode& status) const {
833
21.2k
    int64_t ms = yearStart(year, status);
834
21.2k
    if (U_FAILURE(status)) {
835
0
        return 0;
836
0
    }
837
67.1k
    for(int i=0; i< month; i++){
838
45.9k
        ms+= handleGetMonthLength(year, i, status);
839
45.9k
        if (U_FAILURE(status)) {
840
0
            return 0;
841
0
        }
842
45.9k
    }
843
21.2k
    return ms;
844
21.2k
}
845
846
/**
847
* Return the length (in days) of the given month.
848
*
849
* @param year  The hijri year
850
* @param year  The hijri month, 0-based
851
*/
852
int32_t IslamicUmalquraCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month,
853
101k
                                                      UErrorCode& status) const {
854
101k
    if (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END) {
855
58.8k
        return IslamicCivilCalendar::handleGetMonthLength(extendedYear, month, status);
856
58.8k
    }
857
42.8k
    int32_t length = 29;
858
42.8k
    int32_t mask = static_cast<int32_t>(0x01 << (11 - month)); // set mask for bit corresponding to month
859
42.8k
    int32_t index = extendedYear - UMALQURA_YEAR_START;
860
42.8k
    if ((UMALQURA_MONTHLENGTH[index] & mask) != 0) {
861
24.5k
        length++;
862
24.5k
    }
863
42.8k
    return length;
864
101k
}
865
866
26.7k
int32_t IslamicUmalquraCalendar::yearLength(int32_t extendedYear, UErrorCode& status) const {
867
26.7k
    if (extendedYear<UMALQURA_YEAR_START || extendedYear>UMALQURA_YEAR_END) {
868
24.3k
        return IslamicCivilCalendar::handleGetYearLength(extendedYear, status);
869
24.3k
    }
870
2.37k
    int length = 0;
871
30.8k
    for(int i=0; i<12; i++) {
872
28.5k
        length += handleGetMonthLength(extendedYear, i, status);
873
28.5k
        if (U_FAILURE(status)) {
874
0
            return 0;
875
0
        }
876
28.5k
    }
877
2.37k
    return length;
878
2.37k
}
879
880
/**
881
* Return the number of days in the given Islamic year
882
* @draft ICU 2.4
883
*/
884
8.99k
int32_t IslamicUmalquraCalendar::handleGetYearLength(int32_t extendedYear, UErrorCode& status) const {
885
8.99k
    return yearLength(extendedYear, status);
886
8.99k
}
887
888
/**
889
* Override Calendar to compute several fields specific to the Islamic
890
* calendar system.  These are:
891
*
892
* <ul><li>ERA
893
* <li>YEAR
894
* <li>MONTH
895
* <li>DAY_OF_MONTH
896
* <li>DAY_OF_YEAR
897
* <li>EXTENDED_YEAR</ul>
898
* 
899
* The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
900
* method is called. The getGregorianXxx() methods return Gregorian
901
* calendar equivalents for the given Julian day.
902
* @draft ICU 2.4
903
*/
904
8.98k
void IslamicUmalquraCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) {
905
8.98k
    if (U_FAILURE(status)) {
906
20
        return;
907
20
    }
908
8.96k
    int64_t year;
909
8.96k
    int32_t month;
910
8.96k
    int32_t days = julianDay - getEpoc();
911
912
8.96k
    static int64_t kUmalquraStart = yearStart(UMALQURA_YEAR_START, status);
913
8.96k
    if (U_FAILURE(status)) {
914
0
        return;
915
0
    }
916
8.96k
    if (days < kUmalquraStart) {
917
4.79k
        IslamicCivilCalendar::handleComputeFields(julianDay, status);
918
4.79k
        return;
919
4.79k
    }
920
    // Estimate a value y which is closer to but not greater than the year.
921
    // It is the inverse function of the logic inside
922
    // IslamicUmalquraCalendar::yearStart().
923
4.16k
    year = ((static_cast<double>(days) - (460322.05 + 0.5)) / 354.36720) + UMALQURA_YEAR_START - 1;
924
4.16k
    month = 0;
925
4.16k
    int32_t d = 1;
926
    // need a slight correction to some
927
17.7k
    while (d > 0) {
928
17.7k
        d = days - yearStart(++year, status) + 1;
929
17.7k
        int32_t length = yearLength(year, status);
930
17.7k
        if (U_FAILURE(status)) {
931
0
            return;
932
0
        }
933
17.7k
        if (d == length) {
934
267
            month = 11;
935
267
            break;
936
267
        }
937
17.4k
        if (d < length){
938
3.90k
            int32_t monthLen = handleGetMonthLength(year, month, status);
939
3.90k
            for (month = 0;
940
25.7k
                 d > monthLen;
941
21.8k
                 monthLen = handleGetMonthLength(year, ++month, status)) {
942
21.8k
                if (U_FAILURE(status)) {
943
0
                    return;
944
0
                }
945
21.8k
                d -= monthLen;
946
21.8k
            }
947
3.90k
            break;
948
3.90k
        }
949
17.4k
    }
950
951
4.16k
    int32_t dayOfMonth = monthStart(year, month, status);
952
4.16k
    int32_t dayOfYear = monthStart(year, 0, status);
953
4.16k
    if (U_FAILURE(status)) {
954
0
        return;
955
0
    }
956
4.16k
    if (uprv_mul32_overflow(dayOfMonth, -1, &dayOfMonth) ||
957
4.16k
        uprv_add32_overflow(dayOfMonth, days, &dayOfMonth) ||
958
4.16k
        uprv_add32_overflow(dayOfMonth, 1, &dayOfMonth) ||
959
        // Now figure out the day of the year.
960
4.16k
        uprv_mul32_overflow(dayOfYear, -1, &dayOfYear) ||
961
4.16k
        uprv_add32_overflow(dayOfYear, days, &dayOfYear) ||
962
4.16k
        uprv_add32_overflow(dayOfYear, 1, &dayOfYear)) {
963
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
964
0
        return;
965
0
    }
966
967
4.16k
    internalSet(UCAL_ERA, 0);
968
4.16k
    internalSet(UCAL_YEAR, year);
969
4.16k
    internalSet(UCAL_EXTENDED_YEAR, year);
970
4.16k
    internalSet(UCAL_MONTH, month);
971
4.16k
    internalSet(UCAL_ORDINAL_MONTH, month);
972
4.16k
    internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
973
4.16k
    internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
974
4.16k
}
975
/*****************************************************************************
976
 * IslamicRGSACalendar
977
 *****************************************************************************/
978
IslamicRGSACalendar::IslamicRGSACalendar(const Locale& aLocale, UErrorCode& success)
979
0
    : IslamicCalendar(aLocale, success)
980
0
{
981
0
}
982
983
IslamicRGSACalendar::~IslamicRGSACalendar()
984
{
985
}
986
987
0
const char *IslamicRGSACalendar::getType() const {
988
0
    return "islamic-rgsa";
989
0
}
990
991
0
IslamicRGSACalendar* IslamicRGSACalendar::clone() const {
992
0
    return new IslamicRGSACalendar(*this);
993
0
}
994
995
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicCalendar)
996
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicCivilCalendar)
997
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicUmalquraCalendar)
998
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicTBLACalendar)
999
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicRGSACalendar)
1000
1001
U_NAMESPACE_END
1002
1003
#endif
1004