intl/icu/source/i18n/indiancal.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial