intl/icu/source/i18n/indiancal.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/intl/icu/source/i18n/indiancal.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,449 @@
     1.4 +/*
     1.5 + * Copyright (C) 2003-2009, International Business Machines Corporation
     1.6 + * and others. All Rights Reserved.
     1.7 + ******************************************************************************
     1.8 + *
     1.9 + * File INDIANCAL.CPP
    1.10 + *****************************************************************************
    1.11 + */
    1.12 +
    1.13 +#include "indiancal.h"
    1.14 +#include <stdlib.h>
    1.15 +#if !UCONFIG_NO_FORMATTING
    1.16 +
    1.17 +#include "mutex.h"
    1.18 +#include <float.h>
    1.19 +#include "gregoimp.h" // Math
    1.20 +#include "astro.h" // CalendarAstronomer
    1.21 +#include "uhash.h"
    1.22 +#include "ucln_in.h"
    1.23 +
    1.24 +// Debugging
    1.25 +#ifdef U_DEBUG_INDIANCAL
    1.26 +#include <stdio.h>
    1.27 +#include <stdarg.h>
    1.28 +
    1.29 +#endif
    1.30 +
    1.31 +U_NAMESPACE_BEGIN
    1.32 +
    1.33 +// Implementation of the IndianCalendar class
    1.34 +
    1.35 +//-------------------------------------------------------------------------
    1.36 +// Constructors...
    1.37 +//-------------------------------------------------------------------------
    1.38 +
    1.39 +
    1.40 +Calendar* IndianCalendar::clone() const {
    1.41 +  return new IndianCalendar(*this);
    1.42 +}
    1.43 +
    1.44 +IndianCalendar::IndianCalendar(const Locale& aLocale, UErrorCode& success)
    1.45 +  :   Calendar(TimeZone::createDefault(), aLocale, success)
    1.46 +{
    1.47 +  setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
    1.48 +}
    1.49 +
    1.50 +IndianCalendar::IndianCalendar(const IndianCalendar& other) : Calendar(other) {
    1.51 +}
    1.52 +
    1.53 +IndianCalendar::~IndianCalendar()
    1.54 +{
    1.55 +}
    1.56 +const char *IndianCalendar::getType() const { 
    1.57 +   return "indian";
    1.58 +}
    1.59 +  
    1.60 +static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
    1.61 +    // Minimum  Greatest     Least   Maximum
    1.62 +    //           Minimum   Maximum
    1.63 +    {        0,        0,        0,        0}, // ERA
    1.64 +    { -5000000, -5000000,  5000000,  5000000}, // YEAR
    1.65 +    {        0,        0,       11,       11}, // MONTH
    1.66 +    {        1,        1,       52,       53}, // WEEK_OF_YEAR
    1.67 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
    1.68 +    {        1,        1,       30,       31}, // DAY_OF_MONTH
    1.69 +    {        1,        1,      365,      366}, // DAY_OF_YEAR
    1.70 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
    1.71 +    {       -1,       -1,        5,        5}, // DAY_OF_WEEK_IN_MONTH
    1.72 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
    1.73 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
    1.74 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
    1.75 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
    1.76 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
    1.77 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
    1.78 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
    1.79 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
    1.80 +    { -5000000, -5000000,  5000000,  5000000}, // YEAR_WOY
    1.81 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
    1.82 +    { -5000000, -5000000,  5000000,  5000000}, // EXTENDED_YEAR
    1.83 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
    1.84 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
    1.85 +    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
    1.86 +};
    1.87 +
    1.88 +static const double JULIAN_EPOCH = 1721425.5;
    1.89 +static const int32_t INDIAN_ERA_START  = 78;
    1.90 +static const int32_t INDIAN_YEAR_START = 80;
    1.91 +
    1.92 +int32_t IndianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
    1.93 +  return LIMITS[field][limitType];
    1.94 +}
    1.95 +
    1.96 +/*
    1.97 + * Determine whether the given gregorian year is a Leap year 
    1.98 + */
    1.99 +static UBool isGregorianLeap(int32_t year)
   1.100 +{
   1.101 +    return ((year % 4) == 0) && (!(((year % 100) == 0) && ((year % 400) != 0))); 
   1.102 +}
   1.103 +  
   1.104 +//----------------------------------------------------------------------
   1.105 +// Calendar framework
   1.106 +//----------------------------------------------------------------------
   1.107 +
   1.108 +/*
   1.109 + * Return the length (in days) of the given month.
   1.110 + *
   1.111 + * @param eyear  The year in Saka Era
   1.112 + * @param month  The month(0-based) in Indian calendar
   1.113 + */
   1.114 +int32_t IndianCalendar::handleGetMonthLength(int32_t eyear, int32_t month) const {
   1.115 +   if (month < 0 || month > 11) {
   1.116 +      eyear += ClockMath::floorDivide(month, 12, month);
   1.117 +   }
   1.118 +
   1.119 +   if (isGregorianLeap(eyear + INDIAN_ERA_START) && month == 0) {
   1.120 +       return 31;
   1.121 +   }
   1.122 +
   1.123 +   if (month >= 1 && month <= 5) {
   1.124 +       return 31;
   1.125 +   }
   1.126 +
   1.127 +   return 30;
   1.128 +}
   1.129 +
   1.130 +/*
   1.131 + * Return the number of days in the given Indian year
   1.132 + *
   1.133 + * @param eyear The year in Saka Era.
   1.134 + */
   1.135 +int32_t IndianCalendar::handleGetYearLength(int32_t eyear) const {
   1.136 +    return isGregorianLeap(eyear + INDIAN_ERA_START) ? 366 : 365;
   1.137 +}
   1.138 +/*
   1.139 + * Returns the Julian Day corresponding to gregorian date
   1.140 + *
   1.141 + * @param year The Gregorian year
   1.142 + * @param month The month in Gregorian Year
   1.143 + * @param date The date in Gregorian day in month
   1.144 + */
   1.145 +static double gregorianToJD(int32_t year, int32_t month, int32_t date) {
   1.146 +   double julianDay = (JULIAN_EPOCH - 1) +
   1.147 +      (365 * (year - 1)) +
   1.148 +      uprv_floor((year - 1) / 4) +
   1.149 +      (-uprv_floor((year - 1) / 100)) +
   1.150 +      uprv_floor((year - 1) / 400) +
   1.151 +      uprv_floor((((367 * month) - 362) / 12) +
   1.152 +            ((month <= 2) ? 0 :
   1.153 +             (isGregorianLeap(year) ? -1 : -2)
   1.154 +            ) +
   1.155 +            date);
   1.156 +
   1.157 +   return julianDay;
   1.158 +}
   1.159 +
   1.160 +/*
   1.161 + * Returns the Gregorian Date corresponding to a given Julian Day
   1.162 + * @param jd The Julian Day
   1.163 + */
   1.164 +static int32_t* jdToGregorian(double jd, int32_t gregorianDate[3]) {
   1.165 +   double wjd, depoch, quadricent, dqc, cent, dcent, quad, dquad, yindex, yearday, leapadj;
   1.166 +   int32_t year, month, day;
   1.167 +   wjd = uprv_floor(jd - 0.5) + 0.5;
   1.168 +   depoch = wjd - JULIAN_EPOCH;
   1.169 +   quadricent = uprv_floor(depoch / 146097);
   1.170 +   dqc = (int32_t)uprv_floor(depoch) % 146097;
   1.171 +   cent = uprv_floor(dqc / 36524);
   1.172 +   dcent = (int32_t)uprv_floor(dqc) % 36524;
   1.173 +   quad = uprv_floor(dcent / 1461);
   1.174 +   dquad = (int32_t)uprv_floor(dcent) % 1461;
   1.175 +   yindex = uprv_floor(dquad / 365);
   1.176 +   year = (int32_t)((quadricent * 400) + (cent * 100) + (quad * 4) + yindex);
   1.177 +   if (!((cent == 4) || (yindex == 4))) {
   1.178 +      year++;
   1.179 +   }
   1.180 +   yearday = wjd - gregorianToJD(year, 1, 1);
   1.181 +   leapadj = ((wjd < gregorianToJD(year, 3, 1)) ? 0
   1.182 +         :
   1.183 +         (isGregorianLeap(year) ? 1 : 2)
   1.184 +         );
   1.185 +   month = (int32_t)uprv_floor((((yearday + leapadj) * 12) + 373) / 367);
   1.186 +   day = (int32_t)(wjd - gregorianToJD(year, month, 1)) + 1;
   1.187 +
   1.188 +   gregorianDate[0] = year;
   1.189 +   gregorianDate[1] = month;
   1.190 +   gregorianDate[2] = day;
   1.191 +
   1.192 +   return gregorianDate;
   1.193 +}
   1.194 +
   1.195 +   
   1.196 +//-------------------------------------------------------------------------
   1.197 +// Functions for converting from field values to milliseconds....
   1.198 +//-------------------------------------------------------------------------
   1.199 +static double IndianToJD(int32_t year, int32_t month, int32_t date) {
   1.200 +   int32_t leapMonth, gyear, m;
   1.201 +   double start, jd;
   1.202 +
   1.203 +   gyear = year + INDIAN_ERA_START;
   1.204 +
   1.205 +
   1.206 +   if(isGregorianLeap(gyear)) {
   1.207 +      leapMonth = 31;
   1.208 +      start = gregorianToJD(gyear, 3, 21);
   1.209 +   } 
   1.210 +   else {
   1.211 +      leapMonth = 30;
   1.212 +      start = gregorianToJD(gyear, 3, 22);
   1.213 +   }
   1.214 +
   1.215 +   if (month == 1) {
   1.216 +      jd = start + (date - 1);
   1.217 +   } else {
   1.218 +      jd = start + leapMonth;
   1.219 +      m = month - 2;
   1.220 +
   1.221 +      //m = Math.min(m, 5);
   1.222 +      if (m > 5) {
   1.223 +          m = 5;
   1.224 +      }
   1.225 +
   1.226 +      jd += m * 31;
   1.227 +
   1.228 +      if (month >= 8) {
   1.229 +         m = month - 7;
   1.230 +         jd += m * 30;
   1.231 +      }
   1.232 +      jd += date - 1;
   1.233 +   }
   1.234 +
   1.235 +   return jd;
   1.236 +}
   1.237 +
   1.238 +/*
   1.239 + * Return JD of start of given month/year of Indian Calendar
   1.240 + * @param eyear The year in Indian Calendar measured from Saka Era (78 AD).
   1.241 + * @param month The month in Indian calendar
   1.242 + */
   1.243 +int32_t IndianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /* useMonth */ ) const {
   1.244 +
   1.245 +   //month is 0 based; converting it to 1-based 
   1.246 +   int32_t imonth;
   1.247 +
   1.248 +    // If the month is out of range, adjust it into range, and adjust the extended eyar accordingly
   1.249 +   if (month < 0 || month > 11) {
   1.250 +      eyear += (int32_t)ClockMath::floorDivide(month, 12, month);
   1.251 +   }
   1.252 +
   1.253 +   if(month == 12){
   1.254 +       imonth = 1;
   1.255 +   } else {
   1.256 +       imonth = month + 1; 
   1.257 +   }
   1.258 +   
   1.259 +   double jd = IndianToJD(eyear ,imonth, 1);
   1.260 +
   1.261 +   return (int32_t)jd;
   1.262 +}
   1.263 +
   1.264 +//-------------------------------------------------------------------------
   1.265 +// Functions for converting from milliseconds to field values
   1.266 +//-------------------------------------------------------------------------
   1.267 +
   1.268 +int32_t IndianCalendar::handleGetExtendedYear() {
   1.269 +    int32_t year;
   1.270 +
   1.271 +    if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
   1.272 +        year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
   1.273 +    } else {
   1.274 +        year = internalGet(UCAL_YEAR, 1); // Default to year 1
   1.275 +    }
   1.276 +
   1.277 +    return year;
   1.278 +}
   1.279 +
   1.280 +/*
   1.281 + * Override Calendar to compute several fields specific to the Indian
   1.282 + * calendar system.  These are:
   1.283 + *
   1.284 + * <ul><li>ERA
   1.285 + * <li>YEAR
   1.286 + * <li>MONTH
   1.287 + * <li>DAY_OF_MONTH
   1.288 + * <li>EXTENDED_YEAR</ul>
   1.289 + * 
   1.290 + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
   1.291 + * method is called. The getGregorianXxx() methods return Gregorian
   1.292 + * calendar equivalents for the given Julian day.
   1.293 + */
   1.294 +void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode&  /* status */) {
   1.295 +    double jdAtStartOfGregYear;
   1.296 +    int32_t leapMonth, IndianYear, yday, IndianMonth, IndianDayOfMonth, mday;
   1.297 +    int32_t gregorianYear;      // Stores gregorian date corresponding to Julian day;
   1.298 +    int32_t gd[3];
   1.299 +
   1.300 +    gregorianYear = jdToGregorian(julianDay, gd)[0];          // Gregorian date for Julian day
   1.301 +    IndianYear = gregorianYear - INDIAN_ERA_START;            // Year in Saka era
   1.302 +    jdAtStartOfGregYear = gregorianToJD(gregorianYear, 1, 1); // JD at start of Gregorian year
   1.303 +    yday = (int32_t)(julianDay - jdAtStartOfGregYear);        // Day number in Gregorian year (starting from 0)
   1.304 +
   1.305 +    if (yday < INDIAN_YEAR_START) {
   1.306 +        // Day is at the end of the preceding Saka year
   1.307 +        IndianYear -= 1;
   1.308 +        leapMonth = isGregorianLeap(gregorianYear - 1) ? 31 : 30; // Days in leapMonth this year, previous Gregorian year
   1.309 +        yday += leapMonth + (31 * 5) + (30 * 3) + 10;
   1.310 +    } else {
   1.311 +        leapMonth = isGregorianLeap(gregorianYear) ? 31 : 30; // Days in leapMonth this year
   1.312 +        yday -= INDIAN_YEAR_START;
   1.313 +    }
   1.314 +
   1.315 +    if (yday < leapMonth) {
   1.316 +        IndianMonth = 0;
   1.317 +        IndianDayOfMonth = yday + 1;
   1.318 +    } else {
   1.319 +        mday = yday - leapMonth;
   1.320 +        if (mday < (31 * 5)) {
   1.321 +            IndianMonth = (int32_t)uprv_floor(mday / 31) + 1;
   1.322 +            IndianDayOfMonth = (mday % 31) + 1;
   1.323 +        } else {
   1.324 +            mday -= 31 * 5;
   1.325 +            IndianMonth = (int32_t)uprv_floor(mday / 30) + 6;
   1.326 +            IndianDayOfMonth = (mday % 30) + 1;
   1.327 +        }
   1.328 +   }
   1.329 +
   1.330 +   internalSet(UCAL_ERA, 0);
   1.331 +   internalSet(UCAL_EXTENDED_YEAR, IndianYear);
   1.332 +   internalSet(UCAL_YEAR, IndianYear);
   1.333 +   internalSet(UCAL_MONTH, IndianMonth);
   1.334 +   internalSet(UCAL_DAY_OF_MONTH, IndianDayOfMonth);
   1.335 +   internalSet(UCAL_DAY_OF_YEAR, yday + 1); // yday is 0-based
   1.336 +}    
   1.337 +
   1.338 +UBool
   1.339 +IndianCalendar::inDaylightTime(UErrorCode& status) const
   1.340 +{
   1.341 +    // copied from GregorianCalendar
   1.342 +    if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) {
   1.343 +        return FALSE;
   1.344 +    }
   1.345 +
   1.346 +    // Force an update of the state of the Calendar.
   1.347 +    ((IndianCalendar*)this)->complete(status); // cast away const
   1.348 +
   1.349 +    return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE);
   1.350 +}
   1.351 +
   1.352 +// default century
   1.353 +const UDate     IndianCalendar::fgSystemDefaultCentury          = DBL_MIN;
   1.354 +const int32_t   IndianCalendar::fgSystemDefaultCenturyYear      = -1;
   1.355 +
   1.356 +UDate           IndianCalendar::fgSystemDefaultCenturyStart     = DBL_MIN;
   1.357 +int32_t         IndianCalendar::fgSystemDefaultCenturyStartYear = -1;
   1.358 +
   1.359 +
   1.360 +UBool IndianCalendar::haveDefaultCentury() const
   1.361 +{
   1.362 +    return TRUE;
   1.363 +}
   1.364 +
   1.365 +UDate IndianCalendar::defaultCenturyStart() const
   1.366 +{
   1.367 +    return internalGetDefaultCenturyStart();
   1.368 +}
   1.369 +
   1.370 +int32_t IndianCalendar::defaultCenturyStartYear() const
   1.371 +{
   1.372 +    return internalGetDefaultCenturyStartYear();
   1.373 +}
   1.374 +
   1.375 +UDate
   1.376 +IndianCalendar::internalGetDefaultCenturyStart() const
   1.377 +{
   1.378 +    // lazy-evaluate systemDefaultCenturyStart
   1.379 +    UBool needsUpdate;
   1.380 +    { 
   1.381 +        Mutex m;
   1.382 +        needsUpdate = (fgSystemDefaultCenturyStart == fgSystemDefaultCentury);
   1.383 +    }
   1.384 +
   1.385 +    if (needsUpdate) {
   1.386 +        initializeSystemDefaultCentury();
   1.387 +    }
   1.388 +
   1.389 +    // use defaultCenturyStart unless it's the flag value;
   1.390 +    // then use systemDefaultCenturyStart
   1.391 +
   1.392 +    return fgSystemDefaultCenturyStart;
   1.393 +}
   1.394 +
   1.395 +int32_t
   1.396 +IndianCalendar::internalGetDefaultCenturyStartYear() const
   1.397 +{
   1.398 +    // lazy-evaluate systemDefaultCenturyStartYear
   1.399 +    UBool needsUpdate;
   1.400 +    { 
   1.401 +        Mutex m;
   1.402 +
   1.403 +        needsUpdate = (fgSystemDefaultCenturyStart == fgSystemDefaultCentury);
   1.404 +    }
   1.405 +
   1.406 +    if (needsUpdate) {
   1.407 +        initializeSystemDefaultCentury();
   1.408 +    }
   1.409 +
   1.410 +    // use defaultCenturyStart unless it's the flag value;
   1.411 +    // then use systemDefaultCenturyStartYear
   1.412 +
   1.413 +    return    fgSystemDefaultCenturyStartYear;
   1.414 +}
   1.415 +
   1.416 +void
   1.417 +IndianCalendar::initializeSystemDefaultCentury()
   1.418 +{
   1.419 +    // initialize systemDefaultCentury and systemDefaultCenturyYear based
   1.420 +    // on the current time.  They'll be set to 80 years before
   1.421 +    // the current time.
   1.422 +    // No point in locking as it should be idempotent.
   1.423 +    if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury) {
   1.424 +        UErrorCode status = U_ZERO_ERROR;
   1.425 +
   1.426 +        IndianCalendar calendar(Locale("@calendar=Indian"),status);
   1.427 +        if (U_SUCCESS(status)) {
   1.428 +            calendar.setTime(Calendar::getNow(), status);
   1.429 +            calendar.add(UCAL_YEAR, -80, status);
   1.430 +
   1.431 +            UDate    newStart = calendar.getTime(status);
   1.432 +            int32_t  newYear  = calendar.get(UCAL_YEAR, status);
   1.433 +
   1.434 +            {
   1.435 +                Mutex m;
   1.436 +
   1.437 +                fgSystemDefaultCenturyStart = newStart;
   1.438 +                fgSystemDefaultCenturyStartYear = newYear;
   1.439 +            }
   1.440 +        }
   1.441 +
   1.442 +        // We have no recourse upon failure unless we want to propagate the failure
   1.443 +        // out.
   1.444 +    }
   1.445 +}
   1.446 +
   1.447 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IndianCalendar)
   1.448 +
   1.449 +U_NAMESPACE_END
   1.450 +
   1.451 +#endif
   1.452 +

mercurial