Home | History | Annotate | Download | only in i18n
      1 /*
      2  ******************************************************************************
      3  * Copyright (C) 2003-2008, International Business Machines Corporation
      4  * and others. All Rights Reserved.
      5  ******************************************************************************
      6  *
      7  * File PERSNCAL.CPP
      8  *
      9  * Modification History:
     10  *
     11  *   Date        Name        Description
     12  *   9/23/2003 mehran        posted to icu-design
     13  *****************************************************************************
     14  */
     15 
     16 #include "persncal.h"
     17 
     18 #if !UCONFIG_NO_FORMATTING
     19 
     20 #include "umutex.h"
     21 #include <float.h>
     22 
     23 static const int8_t monthDays[] = { 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29 };
     24 
     25 static int32_t
     26 jalali_to_julian(int year, int month, int day)
     27 {
     28     int32_t daysNo=0;
     29     int i;
     30 
     31     year = year -475+2820;
     32     month -= 1;
     33 
     34     daysNo=(year/2820)*1029983;
     35     year=year % 2820;
     36 
     37     daysNo+=(year/128)* 46751;
     38     if((year/128)>21)
     39     {
     40         daysNo-=46751;
     41         year=(year%128)+128;
     42     }
     43     else
     44         year=year%128;
     45 
     46     if(year>=29)
     47     {
     48         year-=29;
     49         daysNo+=10592;
     50     }
     51 
     52     if(year>=66)
     53     {
     54         year-=66;
     55         daysNo+=24106;
     56     }
     57     else if( year>=33)
     58     {
     59         daysNo+=(year/33)* 12053;
     60         year=year%33;
     61     }
     62 
     63     if (year >= 5)
     64     {
     65         daysNo += 1826;
     66         year -=5;
     67     }
     68     else if (year == 4)
     69     {
     70         daysNo += 1460;
     71         year -=4;
     72     }
     73 
     74     daysNo += 1461 * (year/4);
     75     year %= 4;
     76     daysNo += 365 * year;
     77 
     78     for (i = 0; i < month; i++) {
     79         daysNo += monthDays[i];
     80     }
     81 
     82     daysNo += day;
     83 
     84     return daysNo-856493;
     85 }
     86 
     87 static void julian_to_jalali (int32_t daysNo, int *h_y, int *h_m, int *h_d)
     88 {
     89     int year=0, month=0, day=0,scalarDays=0;
     90     int i;
     91 
     92     daysNo+=856493;
     93     scalarDays=daysNo;
     94     year=(daysNo/1029983)*2820;
     95     daysNo=daysNo%1029983;
     96 
     97     if((daysNo/46751)<=21)
     98     {
     99         year+=(daysNo/46751)* 128;
    100         daysNo=daysNo%46751;
    101     }
    102     else
    103     {
    104         year+=(daysNo/46751)* 128;
    105         daysNo=daysNo%46751;
    106         year-=128;
    107         daysNo+=46751;
    108     }
    109 
    110     if (daysNo >= 10592)
    111     {
    112         year+= 29;
    113         daysNo -= 10592;
    114     }
    115 
    116     if(daysNo>=24106)
    117     {
    118         daysNo-=24106;
    119         year+=66;
    120     }
    121 
    122     if(daysNo>=12053)
    123     {
    124         daysNo-=12053;
    125         year+=33;
    126     }
    127 
    128 
    129     if (daysNo >= 1826)
    130     {
    131         year+= 5;
    132         daysNo -= 1826;
    133     }
    134     else if (daysNo > 1095)
    135     {
    136         year+= 3;
    137         daysNo -= 1095;
    138 
    139     }
    140 
    141     year +=(4 * (daysNo/1461));
    142     daysNo %= 1461;
    143 
    144     if (daysNo == 0)
    145     {
    146         year -= 1;
    147         daysNo = 366;
    148     }
    149     else
    150     {
    151         year += daysNo/365;
    152         daysNo = daysNo % 365;
    153         if (daysNo == 0)
    154         {
    155             year -= 1;
    156             daysNo = 365;
    157         }
    158 
    159     }
    160 
    161     for (i = 0; i < 11 && daysNo > monthDays[i]; ++i) {
    162         daysNo -= monthDays[i];
    163     }
    164 
    165     month = i + 1;
    166 
    167     day = daysNo;
    168 
    169     *h_d = day;
    170     *h_m = month;
    171     *h_y = year-2345;
    172 }
    173 
    174 U_NAMESPACE_BEGIN
    175 
    176 // Implementation of the PersianCalendar class
    177 
    178 //-------------------------------------------------------------------------
    179 // Constructors...
    180 //-------------------------------------------------------------------------
    181 
    182 const char *PersianCalendar::getType() const {
    183     return "persian";
    184 }
    185 
    186 Calendar* PersianCalendar::clone() const {
    187     return new PersianCalendar(*this);
    188 }
    189 
    190 PersianCalendar::PersianCalendar(const Locale& aLocale, UErrorCode& success)
    191   :   Calendar(TimeZone::createDefault(), aLocale, success)
    192 {
    193     setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
    194 }
    195 
    196 PersianCalendar::PersianCalendar(const PersianCalendar& other) : Calendar(other) {
    197 }
    198 
    199 PersianCalendar::~PersianCalendar()
    200 {
    201 }
    202 
    203 //-------------------------------------------------------------------------
    204 // Minimum / Maximum access functions
    205 //-------------------------------------------------------------------------
    206 
    207 static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = {
    208     // Minimum  Greatest     Least   Maximum
    209     //           Minimum   Maximum
    210     {        0,        0,        0,        0}, // ERA
    211     { -5000000, -5000000,  5000000,  5000000}, // YEAR
    212     {        0,        0,       11,       11}, // MONTH
    213     {        1,        1,       52,       53}, // WEEK_OF_YEAR
    214     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
    215     {        1,       1,        29,       31}, // DAY_OF_MONTH
    216     {        1,       1,       365,      366}, // DAY_OF_YEAR
    217     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
    218     {        1,       1,         5,        5}, // DAY_OF_WEEK_IN_MONTH
    219     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
    220     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
    221     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
    222     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
    223     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
    224     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
    225     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
    226     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
    227     { -5000000, -5000000,  5000000,  5000000}, // YEAR_WOY
    228     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
    229     { -5000000, -5000000,  5000000,  5000000}, // EXTENDED_YEAR
    230     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
    231     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
    232     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
    233 };
    234 static const int32_t MONTH_COUNT[12][4]  = {
    235     //len len2   st  st2
    236     {  31,  31,   0,   0 }, // Farvardin
    237     {  31,  31,  31,  31 }, // Ordibehesht
    238     {  31,  31,  62,  62 }, // Khordad
    239     {  31,  31,  93,  93 }, // Tir
    240     {  31,  31, 124, 124 }, // Mordad
    241     {  31,  31, 155, 155 }, // Shahrivar
    242     {  30,  30, 186, 186 }, // Mehr
    243     {  30,  30, 216, 216 }, // Aban
    244     {  30,  30, 246, 246 }, // Azar
    245     {  30,  30, 276, 276 }, // Dey
    246     {  30,  30, 306, 306 }, // Bahman
    247     {  29,  30, 336, 336 }  // Esfand
    248     // len  length of month
    249     // len2 length of month in a leap year
    250     // st   days in year before start of month
    251     // st2  days in year before month in leap year
    252 };
    253 
    254 int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
    255     return LIMITS[field][limitType];
    256 }
    257 
    258 //-------------------------------------------------------------------------
    259 // Assorted calculation utilities
    260 //
    261 
    262 /**
    263  * Determine whether a year is a leap year in the Persian calendar
    264  */
    265 UBool PersianCalendar::isLeapYear(int32_t year)
    266 {
    267     return jalali_to_julian(year+1,1,1)-jalali_to_julian(year,1,1) == 366;
    268 }
    269 
    270 /**
    271  * Return the day # on which the given year starts.  Days are counted
    272  * from the Hijri epoch, origin 0.
    273  */
    274 int32_t PersianCalendar::yearStart(int32_t year) {
    275     return handleComputeMonthStart(year,1,FALSE);
    276 }
    277 
    278 /**
    279  * Return the day # on which the given month starts.  Days are counted
    280  * from the Hijri epoch, origin 0.
    281  *
    282  * @param year  The hijri shamsi year
    283  * @param year  The hijri shamsi month, 0-based
    284  */
    285 int32_t PersianCalendar::monthStart(int32_t year, int32_t month) const {
    286     return handleComputeMonthStart(year,month,FALSE);
    287 }
    288 
    289 //----------------------------------------------------------------------
    290 // Calendar framework
    291 //----------------------------------------------------------------------
    292 
    293 /**
    294  * Return the length (in days) of the given month.
    295  *
    296  * @param year  The hijri shamsi year
    297  * @param year  The hijri shamsi month, 0-based
    298  */
    299 int32_t PersianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
    300     return MONTH_COUNT[month][PersianCalendar::isLeapYear(extendedYear)?1:0];
    301 }
    302 
    303 /**
    304  * Return the number of days in the given Persian year
    305  */
    306 int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear) const {
    307     return 365 + (PersianCalendar::isLeapYear(extendedYear) ? 1 : 0);
    308 }
    309 
    310 //-------------------------------------------------------------------------
    311 // Functions for converting from field values to milliseconds....
    312 //-------------------------------------------------------------------------
    313 
    314 // Return JD of start of given month/year
    315 int32_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool useMonth) const {
    316     // If the month is out of range, adjust it into range, and
    317     // modify the extended year value accordingly.
    318     if (month < 0 || month > 11) {
    319         eyear += month / 12;
    320         month = month % 12;
    321     }
    322     return jalali_to_julian(eyear,(useMonth?month+1:1),1)-1+1947955;
    323 }
    324 
    325 //-------------------------------------------------------------------------
    326 // Functions for converting from milliseconds to field values
    327 //-------------------------------------------------------------------------
    328 
    329 int32_t PersianCalendar::handleGetExtendedYear() {
    330     int32_t year;
    331     if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
    332         year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
    333     } else {
    334         year = internalGet(UCAL_YEAR, 1); // Default to year 1
    335     }
    336     return year;
    337 }
    338 
    339 /**
    340  * Override Calendar to compute several fields specific to the Persian
    341  * calendar system.  These are:
    342  *
    343  * <ul><li>ERA
    344  * <li>YEAR
    345  * <li>MONTH
    346  * <li>DAY_OF_MONTH
    347  * <li>DAY_OF_YEAR
    348  * <li>EXTENDED_YEAR</ul>
    349  *
    350  * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
    351  * method is called. The getGregorianXxx() methods return Gregorian
    352  * calendar equivalents for the given Julian day.
    353  */
    354 void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) {
    355     int jy,jm,jd;
    356     julian_to_jalali(julianDay-1947955,&jy,&jm,&jd);
    357     internalSet(UCAL_ERA, 0);
    358     internalSet(UCAL_YEAR, jy);
    359     internalSet(UCAL_EXTENDED_YEAR, jy);
    360     internalSet(UCAL_MONTH, jm-1);
    361     internalSet(UCAL_DAY_OF_MONTH, jd);
    362     internalSet(UCAL_DAY_OF_YEAR, jd + MONTH_COUNT[jm-1][2]);
    363 }
    364 
    365 UBool
    366 PersianCalendar::inDaylightTime(UErrorCode& status) const
    367 {
    368     // copied from GregorianCalendar
    369     if (U_FAILURE(status) || !getTimeZone().useDaylightTime())
    370         return FALSE;
    371 
    372     // Force an update of the state of the Calendar.
    373     ((PersianCalendar*)this)->complete(status); // cast away const
    374 
    375     return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE);
    376 }
    377 
    378 // default century
    379 const UDate     PersianCalendar::fgSystemDefaultCentury        = DBL_MIN;
    380 const int32_t   PersianCalendar::fgSystemDefaultCenturyYear    = -1;
    381 
    382 UDate           PersianCalendar::fgSystemDefaultCenturyStart       = DBL_MIN;
    383 int32_t         PersianCalendar::fgSystemDefaultCenturyStartYear   = -1;
    384 
    385 UBool PersianCalendar::haveDefaultCentury() const
    386 {
    387     return TRUE;
    388 }
    389 
    390 UDate PersianCalendar::defaultCenturyStart() const
    391 {
    392     return internalGetDefaultCenturyStart();
    393 }
    394 
    395 int32_t PersianCalendar::defaultCenturyStartYear() const
    396 {
    397     return internalGetDefaultCenturyStartYear();
    398 }
    399 
    400 UDate
    401 PersianCalendar::internalGetDefaultCenturyStart() const
    402 {
    403     // lazy-evaluate systemDefaultCenturyStart
    404     UBool needsUpdate;
    405     UMTX_CHECK(NULL, (fgSystemDefaultCenturyStart == fgSystemDefaultCentury), needsUpdate);
    406 
    407     if (needsUpdate) {
    408         initializeSystemDefaultCentury();
    409     }
    410 
    411     // use defaultCenturyStart unless it's the flag value;
    412     // then use systemDefaultCenturyStart
    413 
    414     return fgSystemDefaultCenturyStart;
    415 }
    416 
    417 int32_t
    418 PersianCalendar::internalGetDefaultCenturyStartYear() const
    419 {
    420     // lazy-evaluate systemDefaultCenturyStartYear
    421     UBool needsUpdate;
    422     UMTX_CHECK(NULL, (fgSystemDefaultCenturyStart == fgSystemDefaultCentury), needsUpdate);
    423 
    424     if (needsUpdate) {
    425         initializeSystemDefaultCentury();
    426     }
    427 
    428     // use defaultCenturyStart unless it's the flag value;
    429     // then use systemDefaultCenturyStartYear
    430 
    431     return    fgSystemDefaultCenturyStartYear;
    432 }
    433 
    434 void
    435 PersianCalendar::initializeSystemDefaultCentury()
    436 {
    437     // initialize systemDefaultCentury and systemDefaultCenturyYear based
    438     // on the current time.  They'll be set to 80 years before
    439     // the current time.
    440     UErrorCode status = U_ZERO_ERROR;
    441     PersianCalendar calendar(Locale("@calendar=persian"),status);
    442     if (U_SUCCESS(status))
    443     {
    444         calendar.setTime(Calendar::getNow(), status);
    445         calendar.add(UCAL_YEAR, -80, status);
    446         UDate    newStart =  calendar.getTime(status);
    447         int32_t  newYear  =  calendar.get(UCAL_YEAR, status);
    448         umtx_lock(NULL);
    449         if (fgSystemDefaultCenturyStart == fgSystemDefaultCentury)
    450         {
    451             fgSystemDefaultCenturyStartYear = newYear;
    452             fgSystemDefaultCenturyStart = newStart;
    453         }
    454         umtx_unlock(NULL);
    455     }
    456     // We have no recourse upon failure unless we want to propagate the failure
    457     // out.
    458 }
    459 
    460 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PersianCalendar)
    461 
    462 U_NAMESPACE_END
    463 
    464 #endif
    465 
    466