intl/icu/source/i18n/persncal.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  ******************************************************************************
     3  * Copyright (C) 2003-2013, 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  *   10/1/2012   roozbeh     Fixed algorithm and heavily refactored and rewrote
    14  *                           based on the implementation of Gregorian
    15  *****************************************************************************
    16  */
    18 #include "persncal.h"
    20 #if !UCONFIG_NO_FORMATTING
    22 #include "umutex.h"
    23 #include "gregoimp.h" // Math
    24 #include <float.h>
    26 static const int16_t kPersianNumDays[]
    27 = {0,31,62,93,124,155,186,216,246,276,306,336}; // 0-based, for day-in-year
    28 static const int8_t kPersianMonthLength[]
    29 = {31,31,31,31,31,31,30,30,30,30,30,29}; // 0-based
    30 static const int8_t kPersianLeapMonthLength[]
    31 = {31,31,31,31,31,31,30,30,30,30,30,30}; // 0-based
    33 static const int32_t kPersianCalendarLimits[UCAL_FIELD_COUNT][4] = {
    34     // Minimum  Greatest     Least   Maximum
    35     //           Minimum   Maximum
    36     {        0,        0,        0,        0}, // ERA
    37     { -5000000, -5000000,  5000000,  5000000}, // YEAR
    38     {        0,        0,       11,       11}, // MONTH
    39     {        1,        1,       52,       53}, // WEEK_OF_YEAR
    40     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
    41     {        1,       1,        29,       31}, // DAY_OF_MONTH
    42     {        1,       1,       365,      366}, // DAY_OF_YEAR
    43     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
    44     {        1,       1,         5,        5}, // DAY_OF_WEEK_IN_MONTH
    45     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
    46     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
    47     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
    48     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
    49     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
    50     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
    51     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
    52     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
    53     { -5000000, -5000000,  5000000,  5000000}, // YEAR_WOY
    54     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
    55     { -5000000, -5000000,  5000000,  5000000}, // EXTENDED_YEAR
    56     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
    57     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
    58     {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
    59 };
    61 U_NAMESPACE_BEGIN
    63 static const int32_t PERSIAN_EPOCH = 1948320;
    65 // Implementation of the PersianCalendar class
    67 //-------------------------------------------------------------------------
    68 // Constructors...
    69 //-------------------------------------------------------------------------
    71 const char *PersianCalendar::getType() const { 
    72     return "persian";
    73 }
    75 Calendar* PersianCalendar::clone() const {
    76     return new PersianCalendar(*this);
    77 }
    79 PersianCalendar::PersianCalendar(const Locale& aLocale, UErrorCode& success)
    80   :   Calendar(TimeZone::createDefault(), aLocale, success)
    81 {
    82     setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly.
    83 }
    85 PersianCalendar::PersianCalendar(const PersianCalendar& other) : Calendar(other) {
    86 }
    88 PersianCalendar::~PersianCalendar()
    89 {
    90 }
    92 //-------------------------------------------------------------------------
    93 // Minimum / Maximum access functions
    94 //-------------------------------------------------------------------------
    97 int32_t PersianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
    98     return kPersianCalendarLimits[field][limitType];
    99 }
   101 //-------------------------------------------------------------------------
   102 // Assorted calculation utilities
   103 //
   105 /**
   106  * Determine whether a year is a leap year in the Persian calendar
   107  */
   108 UBool PersianCalendar::isLeapYear(int32_t year)
   109 {
   110     int32_t remainder;
   111     ClockMath::floorDivide(25 * year + 11, 33, remainder);
   112     return (remainder < 8);
   113 }
   115 /**
   116  * Return the day # on which the given year starts.  Days are counted
   117  * from the Persian epoch, origin 0.
   118  */
   119 int32_t PersianCalendar::yearStart(int32_t year) {
   120     return handleComputeMonthStart(year,0,FALSE);
   121 }
   123 /**
   124  * Return the day # on which the given month starts.  Days are counted
   125  * from the Persian epoch, origin 0.
   126  *
   127  * @param year  The Persian year
   128  * @param year  The Persian month, 0-based
   129  */
   130 int32_t PersianCalendar::monthStart(int32_t year, int32_t month) const {
   131     return handleComputeMonthStart(year,month,TRUE);
   132 }
   134 //----------------------------------------------------------------------
   135 // Calendar framework
   136 //----------------------------------------------------------------------
   138 /**
   139  * Return the length (in days) of the given month.
   140  *
   141  * @param year  The Persian year
   142  * @param year  The Persian month, 0-based
   143  */
   144 int32_t PersianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const {
   145     // If the month is out of range, adjust it into range, and
   146     // modify the extended year value accordingly.
   147     if (month < 0 || month > 11) {
   148         extendedYear += ClockMath::floorDivide(month, 12, month);
   149     }
   151     return isLeapYear(extendedYear) ? kPersianLeapMonthLength[month] : kPersianMonthLength[month];
   152 }
   154 /**
   155  * Return the number of days in the given Persian year
   156  */
   157 int32_t PersianCalendar::handleGetYearLength(int32_t extendedYear) const {
   158     return isLeapYear(extendedYear) ? 366 : 365;
   159 }
   161 //-------------------------------------------------------------------------
   162 // Functions for converting from field values to milliseconds....
   163 //-------------------------------------------------------------------------
   165 // Return JD of start of given month/year
   166 int32_t PersianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/) const {
   167     // If the month is out of range, adjust it into range, and
   168     // modify the extended year value accordingly.
   169     if (month < 0 || month > 11) {
   170         eyear += ClockMath::floorDivide(month, 12, month);
   171     }
   173     int32_t julianDay = PERSIAN_EPOCH - 1 + 365 * (eyear - 1) + ClockMath::floorDivide(8 * eyear + 21, 33);
   175     if (month != 0) {
   176         julianDay += kPersianNumDays[month];
   177     }
   179     return julianDay;
   180 }
   182 //-------------------------------------------------------------------------
   183 // Functions for converting from milliseconds to field values
   184 //-------------------------------------------------------------------------
   186 int32_t PersianCalendar::handleGetExtendedYear() {
   187     int32_t year;
   188     if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
   189         year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
   190     } else {
   191         year = internalGet(UCAL_YEAR, 1); // Default to year 1
   192     }
   193     return year;
   194 }
   196 /**
   197  * Override Calendar to compute several fields specific to the Persian
   198  * calendar system.  These are:
   199  *
   200  * <ul><li>ERA
   201  * <li>YEAR
   202  * <li>MONTH
   203  * <li>DAY_OF_MONTH
   204  * <li>DAY_OF_YEAR
   205  * <li>EXTENDED_YEAR</ul>
   206  * 
   207  * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this
   208  * method is called.
   209  */
   210 void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) {
   211     int32_t year, month, dayOfMonth, dayOfYear;
   213     int32_t daysSinceEpoch = julianDay - PERSIAN_EPOCH;
   214     year = 1 + ClockMath::floorDivide(33 * daysSinceEpoch + 3, 12053);
   216     int32_t farvardin1 = 365 * (year - 1) + ClockMath::floorDivide(8 * year + 21, 33);
   217     dayOfYear = (daysSinceEpoch - farvardin1); // 0-based
   218     if (dayOfYear < 216) { // Compute 0-based month
   219         month = dayOfYear / 31;
   220     } else {
   221         month = (dayOfYear - 6) / 30;
   222     }
   223     dayOfMonth = dayOfYear - kPersianNumDays[month] + 1;
   224     ++dayOfYear; // Make it 1-based now
   226     internalSet(UCAL_ERA, 0);
   227     internalSet(UCAL_YEAR, year);
   228     internalSet(UCAL_EXTENDED_YEAR, year);
   229     internalSet(UCAL_MONTH, month);
   230     internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
   231     internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
   232 }    
   234 UBool
   235 PersianCalendar::inDaylightTime(UErrorCode& status) const
   236 {
   237     // copied from GregorianCalendar
   238     if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) 
   239         return FALSE;
   241     // Force an update of the state of the Calendar.
   242     ((PersianCalendar*)this)->complete(status); // cast away const
   244     return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE);
   245 }
   247 // default century
   249 static UDate           gSystemDefaultCenturyStart       = DBL_MIN;
   250 static int32_t         gSystemDefaultCenturyStartYear   = -1;
   251 static icu::UInitOnce  gSystemDefaultCenturyInit        = U_INITONCE_INITIALIZER;
   253 UBool PersianCalendar::haveDefaultCentury() const
   254 {
   255     return TRUE;
   256 }
   258 static void U_CALLCONV initializeSystemDefaultCentury() {
   259     // initialize systemDefaultCentury and systemDefaultCenturyYear based
   260     // on the current time.  They'll be set to 80 years before
   261     // the current time.
   262     UErrorCode status = U_ZERO_ERROR;
   263     PersianCalendar calendar(Locale("@calendar=persian"),status);
   264     if (U_SUCCESS(status))
   265     {
   266         calendar.setTime(Calendar::getNow(), status);
   267         calendar.add(UCAL_YEAR, -80, status);
   269         gSystemDefaultCenturyStart = calendar.getTime(status);
   270         gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status);
   271     }
   272     // We have no recourse upon failure unless we want to propagate the failure
   273     // out.
   274 }
   276 UDate PersianCalendar::defaultCenturyStart() const {
   277     // lazy-evaluate systemDefaultCenturyStart
   278     umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
   279     return gSystemDefaultCenturyStart;
   280 }
   282 int32_t PersianCalendar::defaultCenturyStartYear() const {
   283     // lazy-evaluate systemDefaultCenturyStartYear
   284     umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury);
   285     return gSystemDefaultCenturyStartYear;
   286 }
   288 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PersianCalendar)
   290 U_NAMESPACE_END
   292 #endif

mercurial