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