]>
Commit | Line | Data |
---|---|---|
46f4442e A |
1 | /* |
2 | * Copyright (C) 2003-2008, 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 += Math::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)Math::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 |