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