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