|
1 /* |
|
2 * Copyright (C) 2003-2009, International Business Machines Corporation |
|
3 * and others. All Rights Reserved. |
|
4 ****************************************************************************** |
|
5 * |
|
6 * File INDIANCAL.CPP |
|
7 ***************************************************************************** |
|
8 */ |
|
9 |
|
10 #include "indiancal.h" |
|
11 #include <stdlib.h> |
|
12 #if !UCONFIG_NO_FORMATTING |
|
13 |
|
14 #include "mutex.h" |
|
15 #include <float.h> |
|
16 #include "gregoimp.h" // Math |
|
17 #include "astro.h" // CalendarAstronomer |
|
18 #include "uhash.h" |
|
19 #include "ucln_in.h" |
|
20 |
|
21 // Debugging |
|
22 #ifdef U_DEBUG_INDIANCAL |
|
23 #include <stdio.h> |
|
24 #include <stdarg.h> |
|
25 |
|
26 #endif |
|
27 |
|
28 U_NAMESPACE_BEGIN |
|
29 |
|
30 // Implementation of the IndianCalendar class |
|
31 |
|
32 //------------------------------------------------------------------------- |
|
33 // Constructors... |
|
34 //------------------------------------------------------------------------- |
|
35 |
|
36 |
|
37 Calendar* IndianCalendar::clone() const { |
|
38 return new IndianCalendar(*this); |
|
39 } |
|
40 |
|
41 IndianCalendar::IndianCalendar(const Locale& aLocale, UErrorCode& success) |
|
42 : Calendar(TimeZone::createDefault(), aLocale, success) |
|
43 { |
|
44 setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. |
|
45 } |
|
46 |
|
47 IndianCalendar::IndianCalendar(const IndianCalendar& other) : Calendar(other) { |
|
48 } |
|
49 |
|
50 IndianCalendar::~IndianCalendar() |
|
51 { |
|
52 } |
|
53 const char *IndianCalendar::getType() const { |
|
54 return "indian"; |
|
55 } |
|
56 |
|
57 static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { |
|
58 // Minimum Greatest Least Maximum |
|
59 // Minimum Maximum |
|
60 { 0, 0, 0, 0}, // ERA |
|
61 { -5000000, -5000000, 5000000, 5000000}, // YEAR |
|
62 { 0, 0, 11, 11}, // MONTH |
|
63 { 1, 1, 52, 53}, // WEEK_OF_YEAR |
|
64 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH |
|
65 { 1, 1, 30, 31}, // DAY_OF_MONTH |
|
66 { 1, 1, 365, 366}, // DAY_OF_YEAR |
|
67 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK |
|
68 { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH |
|
69 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM |
|
70 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR |
|
71 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY |
|
72 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE |
|
73 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND |
|
74 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND |
|
75 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET |
|
76 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET |
|
77 { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY |
|
78 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL |
|
79 { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR |
|
80 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY |
|
81 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY |
|
82 {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH |
|
83 }; |
|
84 |
|
85 static const double JULIAN_EPOCH = 1721425.5; |
|
86 static const int32_t INDIAN_ERA_START = 78; |
|
87 static const int32_t INDIAN_YEAR_START = 80; |
|
88 |
|
89 int32_t IndianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { |
|
90 return LIMITS[field][limitType]; |
|
91 } |
|
92 |
|
93 /* |
|
94 * Determine whether the given gregorian year is a Leap year |
|
95 */ |
|
96 static UBool isGregorianLeap(int32_t year) |
|
97 { |
|
98 return ((year % 4) == 0) && (!(((year % 100) == 0) && ((year % 400) != 0))); |
|
99 } |
|
100 |
|
101 //---------------------------------------------------------------------- |
|
102 // Calendar framework |
|
103 //---------------------------------------------------------------------- |
|
104 |
|
105 /* |
|
106 * Return the length (in days) of the given month. |
|
107 * |
|
108 * @param eyear The year in Saka Era |
|
109 * @param month The month(0-based) in Indian calendar |
|
110 */ |
|
111 int32_t IndianCalendar::handleGetMonthLength(int32_t eyear, int32_t month) const { |
|
112 if (month < 0 || month > 11) { |
|
113 eyear += ClockMath::floorDivide(month, 12, month); |
|
114 } |
|
115 |
|
116 if (isGregorianLeap(eyear + INDIAN_ERA_START) && month == 0) { |
|
117 return 31; |
|
118 } |
|
119 |
|
120 if (month >= 1 && month <= 5) { |
|
121 return 31; |
|
122 } |
|
123 |
|
124 return 30; |
|
125 } |
|
126 |
|
127 /* |
|
128 * Return the number of days in the given Indian year |
|
129 * |
|
130 * @param eyear The year in Saka Era. |
|
131 */ |
|
132 int32_t IndianCalendar::handleGetYearLength(int32_t eyear) const { |
|
133 return isGregorianLeap(eyear + INDIAN_ERA_START) ? 366 : 365; |
|
134 } |
|
135 /* |
|
136 * Returns the Julian Day corresponding to gregorian date |
|
137 * |
|
138 * @param year The Gregorian year |
|
139 * @param month The month in Gregorian Year |
|
140 * @param date The date in Gregorian day in month |
|
141 */ |
|
142 static double gregorianToJD(int32_t year, int32_t month, int32_t date) { |
|
143 double julianDay = (JULIAN_EPOCH - 1) + |
|
144 (365 * (year - 1)) + |
|
145 uprv_floor((year - 1) / 4) + |
|
146 (-uprv_floor((year - 1) / 100)) + |
|
147 uprv_floor((year - 1) / 400) + |
|
148 uprv_floor((((367 * month) - 362) / 12) + |
|
149 ((month <= 2) ? 0 : |
|
150 (isGregorianLeap(year) ? -1 : -2) |
|
151 ) + |
|
152 date); |
|
153 |
|
154 return julianDay; |
|
155 } |
|
156 |
|
157 /* |
|
158 * Returns the Gregorian Date corresponding to a given Julian Day |
|
159 * @param jd The Julian Day |
|
160 */ |
|
161 static int32_t* jdToGregorian(double jd, int32_t gregorianDate[3]) { |
|
162 double wjd, depoch, quadricent, dqc, cent, dcent, quad, dquad, yindex, yearday, leapadj; |
|
163 int32_t year, month, day; |
|
164 wjd = uprv_floor(jd - 0.5) + 0.5; |
|
165 depoch = wjd - JULIAN_EPOCH; |
|
166 quadricent = uprv_floor(depoch / 146097); |
|
167 dqc = (int32_t)uprv_floor(depoch) % 146097; |
|
168 cent = uprv_floor(dqc / 36524); |
|
169 dcent = (int32_t)uprv_floor(dqc) % 36524; |
|
170 quad = uprv_floor(dcent / 1461); |
|
171 dquad = (int32_t)uprv_floor(dcent) % 1461; |
|
172 yindex = uprv_floor(dquad / 365); |
|
173 year = (int32_t)((quadricent * 400) + (cent * 100) + (quad * 4) + yindex); |
|
174 if (!((cent == 4) || (yindex == 4))) { |
|
175 year++; |
|
176 } |
|
177 yearday = wjd - gregorianToJD(year, 1, 1); |
|
178 leapadj = ((wjd < gregorianToJD(year, 3, 1)) ? 0 |
|
179 : |
|
180 (isGregorianLeap(year) ? 1 : 2) |
|
181 ); |
|
182 month = (int32_t)uprv_floor((((yearday + leapadj) * 12) + 373) / 367); |
|
183 day = (int32_t)(wjd - gregorianToJD(year, month, 1)) + 1; |
|
184 |
|
185 gregorianDate[0] = year; |
|
186 gregorianDate[1] = month; |
|
187 gregorianDate[2] = day; |
|
188 |
|
189 return gregorianDate; |
|
190 } |
|
191 |
|
192 |
|
193 //------------------------------------------------------------------------- |
|
194 // Functions for converting from field values to milliseconds.... |
|
195 //------------------------------------------------------------------------- |
|
196 static double IndianToJD(int32_t year, int32_t month, int32_t date) { |
|
197 int32_t leapMonth, gyear, m; |
|
198 double start, jd; |
|
199 |
|
200 gyear = year + INDIAN_ERA_START; |
|
201 |
|
202 |
|
203 if(isGregorianLeap(gyear)) { |
|
204 leapMonth = 31; |
|
205 start = gregorianToJD(gyear, 3, 21); |
|
206 } |
|
207 else { |
|
208 leapMonth = 30; |
|
209 start = gregorianToJD(gyear, 3, 22); |
|
210 } |
|
211 |
|
212 if (month == 1) { |
|
213 jd = start + (date - 1); |
|
214 } else { |
|
215 jd = start + leapMonth; |
|
216 m = month - 2; |
|
217 |
|
218 //m = Math.min(m, 5); |
|
219 if (m > 5) { |
|
220 m = 5; |
|
221 } |
|
222 |
|
223 jd += m * 31; |
|
224 |
|
225 if (month >= 8) { |
|
226 m = month - 7; |
|
227 jd += m * 30; |
|
228 } |
|
229 jd += date - 1; |
|
230 } |
|
231 |
|
232 return jd; |
|
233 } |
|
234 |
|
235 /* |
|
236 * Return JD of start of given month/year of Indian Calendar |
|
237 * @param eyear The year in Indian Calendar measured from Saka Era (78 AD). |
|
238 * @param month The month in Indian calendar |
|
239 */ |
|
240 int32_t IndianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */ ) const { |
|
241 |
|
242 //month is 0 based; converting it to 1-based |
|
243 int32_t imonth; |
|
244 |
|
245 // If the month is out of range, adjust it into range, and adjust the extended eyar accordingly |
|
246 if (month < 0 || month > 11) { |
|
247 eyear += (int32_t)ClockMath::floorDivide(month, 12, month); |
|
248 } |
|
249 |
|
250 if(month == 12){ |
|
251 imonth = 1; |
|
252 } else { |
|
253 imonth = month + 1; |
|
254 } |
|
255 |
|
256 double jd = IndianToJD(eyear ,imonth, 1); |
|
257 |
|
258 return (int32_t)jd; |
|
259 } |
|
260 |
|
261 //------------------------------------------------------------------------- |
|
262 // Functions for converting from milliseconds to field values |
|
263 //------------------------------------------------------------------------- |
|
264 |
|
265 int32_t IndianCalendar::handleGetExtendedYear() { |
|
266 int32_t year; |
|
267 |
|
268 if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { |
|
269 year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 |
|
270 } else { |
|
271 year = internalGet(UCAL_YEAR, 1); // Default to year 1 |
|
272 } |
|
273 |
|
274 return year; |
|
275 } |
|
276 |
|
277 /* |
|
278 * Override Calendar to compute several fields specific to the Indian |
|
279 * calendar system. These are: |
|
280 * |
|
281 * <ul><li>ERA |
|
282 * <li>YEAR |
|
283 * <li>MONTH |
|
284 * <li>DAY_OF_MONTH |
|
285 * <li>EXTENDED_YEAR</ul> |
|
286 * |
|
287 * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this |
|
288 * method is called. The getGregorianXxx() methods return Gregorian |
|
289 * calendar equivalents for the given Julian day. |
|
290 */ |
|
291 void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& /* status */) { |
|
292 double jdAtStartOfGregYear; |
|
293 int32_t leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday; |
|
294 int32_t gregorianYear; // Stores gregorian date corresponding to Julian day; |
|
295 int32_t gd[3]; |
|
296 |
|
297 gregorianYear = jdToGregorian(julianDay, gd)[0]; // Gregorian date for Julian day |
|
298 IndianYear = gregorianYear - INDIAN_ERA_START; // Year in Saka era |
|
299 jdAtStartOfGregYear = gregorianToJD(gregorianYear, 1, 1); // JD at start of Gregorian year |
|
300 yday = (int32_t)(julianDay - jdAtStartOfGregYear); // Day number in Gregorian year (starting from 0) |
|
301 |
|
302 if (yday < INDIAN_YEAR_START) { |
|
303 // Day is at the end of the preceding Saka year |
|
304 IndianYear -= 1; |
|
305 leapMonth = isGregorianLeap(gregorianYear - 1) ? 31 : 30; // Days in leapMonth this year, previous Gregorian year |
|
306 yday += leapMonth + (31 * 5) + (30 * 3) + 10; |
|
307 } else { |
|
308 leapMonth = isGregorianLeap(gregorianYear) ? 31 : 30; // Days in leapMonth this year |
|
309 yday -= INDIAN_YEAR_START; |
|
310 } |
|
311 |
|
312 if (yday < leapMonth) { |
|
313 IndianMonth = 0; |
|
314 IndianDayOfMonth = yday + 1; |
|
315 } else { |
|
316 mday = yday - leapMonth; |
|
317 if (mday < (31 * 5)) { |
|
318 IndianMonth = (int32_t)uprv_floor(mday / 31) + 1; |
|
319 IndianDayOfMonth = (mday % 31) + 1; |
|
320 } else { |
|
321 mday -= 31 * 5; |
|
322 IndianMonth = (int32_t)uprv_floor(mday / 30) + 6; |
|
323 IndianDayOfMonth = (mday % 30) + 1; |
|
324 } |
|
325 } |
|
326 |
|
327 internalSet(UCAL_ERA, 0); |
|
328 internalSet(UCAL_EXTENDED_YEAR, IndianYear); |
|
329 internalSet(UCAL_YEAR, IndianYear); |
|
330 internalSet(UCAL_MONTH, IndianMonth); |
|
331 internalSet(UCAL_DAY_OF_MONTH, IndianDayOfMonth); |
|
332 internalSet(UCAL_DAY_OF_YEAR, yday + 1); // yday is 0-based |
|
333 } |
|
334 |
|
335 UBool |
|
336 IndianCalendar::inDaylightTime(UErrorCode& status) const |
|
337 { |
|
338 // copied from GregorianCalendar |
|
339 if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) { |
|
340 return FALSE; |
|
341 } |
|
342 |
|
343 // Force an update of the state of the Calendar. |
|
344 ((IndianCalendar*)this)->complete(status); // cast away const |
|
345 |
|
346 return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE); |
|
347 } |
|
348 |
|
349 // default century |
|
350 const UDate IndianCalendar::fgSystemDefaultCentury = DBL_MIN; |
|
351 const int32_t IndianCalendar::fgSystemDefaultCenturyYear = -1; |
|
352 |
|
353 UDate IndianCalendar::fgSystemDefaultCenturyStart = DBL_MIN; |
|
354 int32_t IndianCalendar::fgSystemDefaultCenturyStartYear = -1; |
|
355 |
|
356 |
|
357 UBool IndianCalendar::haveDefaultCentury() const |
|
358 { |
|
359 return TRUE; |
|
360 } |
|
361 |
|
362 UDate IndianCalendar::defaultCenturyStart() const |
|
363 { |
|
364 return internalGetDefaultCenturyStart(); |
|
365 } |
|
366 |
|
367 int32_t IndianCalendar::defaultCenturyStartYear() const |
|
368 { |
|
369 return internalGetDefaultCenturyStartYear(); |
|
370 } |
|
371 |
|
372 UDate |
|
373 IndianCalendar::internalGetDefaultCenturyStart() const |
|
374 { |
|
375 // lazy-evaluate systemDefaultCenturyStart |
|
376 UBool needsUpdate; |
|
377 { |
|
378 Mutex m; |
|
379 needsUpdate = (fgSystemDefaultCenturyStart == fgSystemDefaultCentury); |
|
380 } |
|
381 |
|
382 if (needsUpdate) { |
|
383 initializeSystemDefaultCentury(); |
|
384 } |
|
385 |
|
386 // use defaultCenturyStart unless it's the flag value; |
|
387 // then use systemDefaultCenturyStart |
|
388 |
|
389 return fgSystemDefaultCenturyStart; |
|
390 } |
|
391 |
|
392 int32_t |
|
393 IndianCalendar::internalGetDefaultCenturyStartYear() const |
|
394 { |
|
395 // lazy-evaluate systemDefaultCenturyStartYear |
|
396 UBool needsUpdate; |
|
397 { |
|
398 Mutex m; |
|
399 |
|
400 needsUpdate = (fgSystemDefaultCenturyStart == fgSystemDefaultCentury); |
|
401 } |
|
402 |
|
403 if (needsUpdate) { |
|
404 initializeSystemDefaultCentury(); |
|
405 } |
|
406 |
|
407 // use defaultCenturyStart unless it's the flag value; |
|
408 // then use systemDefaultCenturyStartYear |
|
409 |
|
410 return fgSystemDefaultCenturyStartYear; |
|
411 } |
|
412 |
|
413 void |
|
414 IndianCalendar::initializeSystemDefaultCentury() |
|
415 { |
|
416 // initialize systemDefaultCentury and systemDefaultCenturyYear based |
|
417 // on the current time. They'll be set to 80 years before |
|
418 // the current time. |
|
419 // No point in locking as it should be idempotent. |
|
420 if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury) { |
|
421 UErrorCode status = U_ZERO_ERROR; |
|
422 |
|
423 IndianCalendar calendar(Locale("@calendar=Indian"),status); |
|
424 if (U_SUCCESS(status)) { |
|
425 calendar.setTime(Calendar::getNow(), status); |
|
426 calendar.add(UCAL_YEAR, -80, status); |
|
427 |
|
428 UDate newStart = calendar.getTime(status); |
|
429 int32_t newYear = calendar.get(UCAL_YEAR, status); |
|
430 |
|
431 { |
|
432 Mutex m; |
|
433 |
|
434 fgSystemDefaultCenturyStart = newStart; |
|
435 fgSystemDefaultCenturyStartYear = newYear; |
|
436 } |
|
437 } |
|
438 |
|
439 // We have no recourse upon failure unless we want to propagate the failure |
|
440 // out. |
|
441 } |
|
442 } |
|
443 |
|
444 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IndianCalendar) |
|
445 |
|
446 U_NAMESPACE_END |
|
447 |
|
448 #endif |
|
449 |