OLD | NEW |
(Empty) | |
| 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 lim
itType) 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, yearda
y, 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, UB
ool /* 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 extend
ed 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& /* stat
us */) { |
| 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 er
a |
| 299 jdAtStartOfGregYear = gregorianToJD(gregorianYear, 1, 1); // JD at start of
Gregorian year |
| 300 yday = (int32_t)(julianDay - jdAtStartOfGregYear); // Day number in G
regorian 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 lea
pMonth this year, previous Gregorian year |
| 306 yday += leapMonth + (31 * 5) + (30 * 3) + 10; |
| 307 } else { |
| 308 leapMonth = isGregorianLeap(gregorianYear) ? 31 : 30; // Days in leapMon
th 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) : FAL
SE); |
| 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 fail
ure |
| 440 // out. |
| 441 } |
| 442 } |
| 443 |
| 444 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IndianCalendar) |
| 445 |
| 446 U_NAMESPACE_END |
| 447 |
| 448 #endif |
| 449 |
OLD | NEW |