intl/icu/source/i18n/indiancal.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /*
     2  * Copyright (C) 2003-2009, International Business Machines Corporation
     3  * and others. All Rights Reserved.
     4  ******************************************************************************
     5  *
     6  * File INDIANCAL.CPP
     7  *****************************************************************************
     8  */
    10 #include "indiancal.h"
    11 #include <stdlib.h>
    12 #if !UCONFIG_NO_FORMATTING
    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"
    21 // Debugging
    22 #ifdef U_DEBUG_INDIANCAL
    23 #include <stdio.h>
    24 #include <stdarg.h>
    26 #endif
    28 U_NAMESPACE_BEGIN
    30 // Implementation of the IndianCalendar class
    32 //-------------------------------------------------------------------------
    33 // Constructors...
    34 //-------------------------------------------------------------------------
    37 Calendar* IndianCalendar::clone() const {
    38   return new IndianCalendar(*this);
    39 }
    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 }
    47 IndianCalendar::IndianCalendar(const IndianCalendar& other) : Calendar(other) {
    48 }
    50 IndianCalendar::~IndianCalendar()
    51 {
    52 }
    53 const char *IndianCalendar::getType() const { 
    54    return "indian";
    55 }
    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 };
    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;
    89 int32_t IndianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
    90   return LIMITS[field][limitType];
    91 }
    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 }
   101 //----------------------------------------------------------------------
   102 // Calendar framework
   103 //----------------------------------------------------------------------
   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    }
   116    if (isGregorianLeap(eyear + INDIAN_ERA_START) && month == 0) {
   117        return 31;
   118    }
   120    if (month >= 1 && month <= 5) {
   121        return 31;
   122    }
   124    return 30;
   125 }
   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);
   154    return julianDay;
   155 }
   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;
   185    gregorianDate[0] = year;
   186    gregorianDate[1] = month;
   187    gregorianDate[2] = day;
   189    return gregorianDate;
   190 }
   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;
   200    gyear = year + INDIAN_ERA_START;
   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    }
   212    if (month == 1) {
   213       jd = start + (date - 1);
   214    } else {
   215       jd = start + leapMonth;
   216       m = month - 2;
   218       //m = Math.min(m, 5);
   219       if (m > 5) {
   220           m = 5;
   221       }
   223       jd += m * 31;
   225       if (month >= 8) {
   226          m = month - 7;
   227          jd += m * 30;
   228       }
   229       jd += date - 1;
   230    }
   232    return jd;
   233 }
   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 {
   242    //month is 0 based; converting it to 1-based 
   243    int32_t imonth;
   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)ClockMath::floorDivide(month, 12, month);
   248    }
   250    if(month == 12){
   251        imonth = 1;
   252    } else {
   253        imonth = month + 1; 
   254    }
   256    double jd = IndianToJD(eyear ,imonth, 1);
   258    return (int32_t)jd;
   259 }
   261 //-------------------------------------------------------------------------
   262 // Functions for converting from milliseconds to field values
   263 //-------------------------------------------------------------------------
   265 int32_t IndianCalendar::handleGetExtendedYear() {
   266     int32_t year;
   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     }
   274     return year;
   275 }
   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];
   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)
   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     }
   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    }
   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 }    
   335 UBool
   336 IndianCalendar::inDaylightTime(UErrorCode& status) const
   337 {
   338     // copied from GregorianCalendar
   339     if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) {
   340         return FALSE;
   341     }
   343     // Force an update of the state of the Calendar.
   344     ((IndianCalendar*)this)->complete(status); // cast away const
   346     return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE);
   347 }
   349 // default century
   350 const UDate     IndianCalendar::fgSystemDefaultCentury          = DBL_MIN;
   351 const int32_t   IndianCalendar::fgSystemDefaultCenturyYear      = -1;
   353 UDate           IndianCalendar::fgSystemDefaultCenturyStart     = DBL_MIN;
   354 int32_t         IndianCalendar::fgSystemDefaultCenturyStartYear = -1;
   357 UBool IndianCalendar::haveDefaultCentury() const
   358 {
   359     return TRUE;
   360 }
   362 UDate IndianCalendar::defaultCenturyStart() const
   363 {
   364     return internalGetDefaultCenturyStart();
   365 }
   367 int32_t IndianCalendar::defaultCenturyStartYear() const
   368 {
   369     return internalGetDefaultCenturyStartYear();
   370 }
   372 UDate
   373 IndianCalendar::internalGetDefaultCenturyStart() const
   374 {
   375     // lazy-evaluate systemDefaultCenturyStart
   376     UBool needsUpdate;
   377     { 
   378         Mutex m;
   379         needsUpdate = (fgSystemDefaultCenturyStart == fgSystemDefaultCentury);
   380     }
   382     if (needsUpdate) {
   383         initializeSystemDefaultCentury();
   384     }
   386     // use defaultCenturyStart unless it's the flag value;
   387     // then use systemDefaultCenturyStart
   389     return fgSystemDefaultCenturyStart;
   390 }
   392 int32_t
   393 IndianCalendar::internalGetDefaultCenturyStartYear() const
   394 {
   395     // lazy-evaluate systemDefaultCenturyStartYear
   396     UBool needsUpdate;
   397     { 
   398         Mutex m;
   400         needsUpdate = (fgSystemDefaultCenturyStart == fgSystemDefaultCentury);
   401     }
   403     if (needsUpdate) {
   404         initializeSystemDefaultCentury();
   405     }
   407     // use defaultCenturyStart unless it's the flag value;
   408     // then use systemDefaultCenturyStartYear
   410     return    fgSystemDefaultCenturyStartYear;
   411 }
   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;
   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);
   428             UDate    newStart = calendar.getTime(status);
   429             int32_t  newYear  = calendar.get(UCAL_YEAR, status);
   431             {
   432                 Mutex m;
   434                 fgSystemDefaultCenturyStart = newStart;
   435                 fgSystemDefaultCenturyStartYear = newYear;
   436             }
   437         }
   439         // We have no recourse upon failure unless we want to propagate the failure
   440         // out.
   441     }
   442 }
   444 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IndianCalendar)
   446 U_NAMESPACE_END
   448 #endif

mercurial