intl/icu/source/i18n/gregoimp.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-2008, International Business Machines
     4  * Corporation and others.  All Rights Reserved.
     5  **********************************************************************
     6  * Author: Alan Liu
     7  * Created: September 2 2003
     8  * Since: ICU 2.8
     9  **********************************************************************
    10  */
    12 #include "gregoimp.h"
    14 #if !UCONFIG_NO_FORMATTING
    16 #include "unicode/ucal.h"
    17 #include "uresimp.h"
    18 #include "cstring.h"
    19 #include "uassert.h"
    21 #if defined(U_DEBUG_CALDATA)
    22 #include <stdio.h>
    23 #endif
    25 U_NAMESPACE_BEGIN
    27 int32_t ClockMath::floorDivide(int32_t numerator, int32_t denominator) {
    28     return (numerator >= 0) ?
    29         numerator / denominator : ((numerator + 1) / denominator) - 1;
    30 }
    32 int32_t ClockMath::floorDivide(double numerator, int32_t denominator,
    33                           int32_t& remainder) {
    34     double quotient;
    35     quotient = uprv_floor(numerator / denominator);
    36     remainder = (int32_t) (numerator - (quotient * denominator));
    37     return (int32_t) quotient;
    38 }
    40 double ClockMath::floorDivide(double dividend, double divisor,
    41                          double& remainder) {
    42     // Only designed to work for positive divisors
    43     U_ASSERT(divisor > 0);
    44     double quotient = floorDivide(dividend, divisor);
    45     remainder = dividend - (quotient * divisor);
    46     // N.B. For certain large dividends, on certain platforms, there
    47     // is a bug such that the quotient is off by one.  If you doubt
    48     // this to be true, set a breakpoint below and run cintltst.
    49     if (remainder < 0 || remainder >= divisor) {
    50         // E.g. 6.7317038241449352e+022 / 86400000.0 is wrong on my
    51         // machine (too high by one).  4.1792057231752762e+024 /
    52         // 86400000.0 is wrong the other way (too low).
    53         double q = quotient;
    54         quotient += (remainder < 0) ? -1 : +1;
    55         if (q == quotient) {
    56             // For quotients > ~2^53, we won't be able to add or
    57             // subtract one, since the LSB of the mantissa will be >
    58             // 2^0; that is, the exponent (base 2) will be larger than
    59             // the length, in bits, of the mantissa.  In that case, we
    60             // can't give a correct answer, so we set the remainder to
    61             // zero.  This has the desired effect of making extreme
    62             // values give back an approximate answer rather than
    63             // crashing.  For example, UDate values above a ~10^25
    64             // might all have a time of midnight.
    65             remainder = 0;
    66         } else {
    67             remainder = dividend - (quotient * divisor);
    68         }
    69     }
    70     U_ASSERT(0 <= remainder && remainder < divisor);
    71     return quotient;
    72 }
    74 const int32_t JULIAN_1_CE    = 1721426; // January 1, 1 CE Gregorian
    75 const int32_t JULIAN_1970_CE = 2440588; // January 1, 1970 CE Gregorian
    77 const int16_t Grego::DAYS_BEFORE[24] =
    78     {0,31,59,90,120,151,181,212,243,273,304,334,
    79      0,31,60,91,121,152,182,213,244,274,305,335};
    81 const int8_t Grego::MONTH_LENGTH[24] =
    82     {31,28,31,30,31,30,31,31,30,31,30,31,
    83      31,29,31,30,31,30,31,31,30,31,30,31};
    85 double Grego::fieldsToDay(int32_t year, int32_t month, int32_t dom) {
    87     int32_t y = year - 1;
    89     double julian = 365 * y + ClockMath::floorDivide(y, 4) + (JULIAN_1_CE - 3) + // Julian cal
    90         ClockMath::floorDivide(y, 400) - ClockMath::floorDivide(y, 100) + 2 + // => Gregorian cal
    91         DAYS_BEFORE[month + (isLeapYear(year) ? 12 : 0)] + dom; // => month/dom
    93     return julian - JULIAN_1970_CE; // JD => epoch day
    94 }
    96 void Grego::dayToFields(double day, int32_t& year, int32_t& month,
    97                         int32_t& dom, int32_t& dow, int32_t& doy) {
    99     // Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
   100     day += JULIAN_1970_CE - JULIAN_1_CE;
   102     // Convert from the day number to the multiple radix
   103     // representation.  We use 400-year, 100-year, and 4-year cycles.
   104     // For example, the 4-year cycle has 4 years + 1 leap day; giving
   105     // 1461 == 365*4 + 1 days.
   106     int32_t n400 = ClockMath::floorDivide(day, 146097, doy); // 400-year cycle length
   107     int32_t n100 = ClockMath::floorDivide(doy, 36524, doy); // 100-year cycle length
   108     int32_t n4   = ClockMath::floorDivide(doy, 1461, doy); // 4-year cycle length
   109     int32_t n1   = ClockMath::floorDivide(doy, 365, doy);
   110     year = 400*n400 + 100*n100 + 4*n4 + n1;
   111     if (n100 == 4 || n1 == 4) {
   112         doy = 365; // Dec 31 at end of 4- or 400-year cycle
   113     } else {
   114         ++year;
   115     }
   117     UBool isLeap = isLeapYear(year);
   119     // Gregorian day zero is a Monday.
   120     dow = (int32_t) uprv_fmod(day + 1, 7);
   121     dow += (dow < 0) ? (UCAL_SUNDAY + 7) : UCAL_SUNDAY;
   123     // Common Julian/Gregorian calculation
   124     int32_t correction = 0;
   125     int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
   126     if (doy >= march1) {
   127         correction = isLeap ? 1 : 2;
   128     }
   129     month = (12 * (doy + correction) + 6) / 367; // zero-based month
   130     dom = doy - DAYS_BEFORE[month + (isLeap ? 12 : 0)] + 1; // one-based DOM
   131     doy++; // one-based doy
   132 }
   134 void Grego::timeToFields(UDate time, int32_t& year, int32_t& month,
   135                         int32_t& dom, int32_t& dow, int32_t& doy, int32_t& mid) {
   136     double millisInDay;
   137     double day = ClockMath::floorDivide((double)time, (double)U_MILLIS_PER_DAY, millisInDay);
   138     mid = (int32_t)millisInDay;
   139     dayToFields(day, year, month, dom, dow, doy);
   140 }
   142 int32_t Grego::dayOfWeek(double day) {
   143     int32_t dow;
   144     ClockMath::floorDivide(day + UCAL_THURSDAY, 7, dow);
   145     return (dow == 0) ? UCAL_SATURDAY : dow;
   146 }
   148 int32_t Grego::dayOfWeekInMonth(int32_t year, int32_t month, int32_t dom) {
   149     int32_t weekInMonth = (dom + 6)/7;
   150     if (weekInMonth == 4) {
   151         if (dom + 7 > monthLength(year, month)) {
   152             weekInMonth = -1;
   153         }
   154     } else if (weekInMonth == 5) {
   155         weekInMonth = -1;
   156     }
   157     return weekInMonth;
   158 }
   160 /* ---- CalendarData ------ */
   162 #define U_CALENDAR_KEY "calendar"
   163 #define U_GREGORIAN_KEY "gregorian"
   164 #define U_FORMAT_KEY "format"
   165 #define U_DEFAULT_KEY "default"
   166 #define U_CALENDAR_DATA ((char*)0)
   169 // CalendarData::CalendarData(const Locale& loc, UErrorCode& status) 
   170 //   : fFillin(NULL), fBundle(NULL), fFallback(NULL) {
   171 //   initData(loc.getBaseName(), (char*) "???", status);
   172 // }
   174 CalendarData::CalendarData(const Locale& loc, const char *type, UErrorCode& status)
   175   : fFillin(NULL), fOtherFillin(NULL), fBundle(NULL), fFallback(NULL) {
   176   initData(loc.getBaseName(), type, status);
   177 }
   179 void CalendarData::initData(const char *locale, const char *type, UErrorCode& status) {
   180   fOtherFillin = ures_open(U_CALENDAR_DATA, locale, &status);
   181   fFillin = ures_getByKey(fOtherFillin, U_CALENDAR_KEY, fFillin, &status);
   183   if((type != NULL) && 
   184      (*type != '\0') && 
   185      (uprv_strcmp(type, U_GREGORIAN_KEY)))
   186   {
   187     fBundle = ures_getByKeyWithFallback(fFillin, type, NULL, &status);
   188     fFallback = ures_getByKeyWithFallback(fFillin, U_GREGORIAN_KEY, NULL, &status);
   190 #if defined (U_DEBUG_CALDATA)
   191     fprintf(stderr, "%p: CalendarData(%s, %s, %s) -> main(%p, %s)=%s, fallback(%p, %s)=%s\n", 
   192             this, locale, type, u_errorName(status), fBundle, type, fBundle?ures_getLocale(fBundle, &status):"", 
   193             fFallback, U_GREGORIAN_KEY, fFallback?ures_getLocale(fFallback, &status):"");
   194 #endif
   196   } else {
   197     fBundle = ures_getByKeyWithFallback(fFillin, U_GREGORIAN_KEY, NULL, &status);
   198 #if defined (U_DEBUG_CALDATA)
   199     fprintf(stderr, "%p: CalendarData(%s, %s, %s) -> main(%p, %s)=%s, fallback = NULL\n",
   200             this, locale, type, u_errorName(status), fBundle, U_GREGORIAN_KEY, fBundle?ures_getLocale(fBundle, &status):"" );
   201 #endif
   202   }
   203 }
   205 CalendarData::~CalendarData() {
   206     ures_close(fFillin);
   207     ures_close(fBundle);
   208     ures_close(fFallback);
   209     ures_close(fOtherFillin);
   210 }
   212 UResourceBundle*
   213 CalendarData::getByKey(const char *key, UErrorCode& status) {
   214     if(U_FAILURE(status)) {
   215         return NULL;
   216     }
   218     if(fBundle) {
   219         fFillin = ures_getByKeyWithFallback(fBundle, key, fFillin, &status);
   220 #if defined (U_DEBUG_CALDATA)
   221         fprintf(stderr, "%p: get %s -> %s - from MAIN %s\n",this, key, u_errorName(status), ures_getLocale(fFillin, &status));
   222 #endif
   223     }
   224     if(fFallback && (status == U_MISSING_RESOURCE_ERROR)) {
   225         status = U_ZERO_ERROR; // retry with fallback (gregorian)
   226         fFillin = ures_getByKeyWithFallback(fFallback, key, fFillin, &status);
   227 #if defined (U_DEBUG_CALDATA)
   228         fprintf(stderr, "%p: get %s -> %s - from FALLBACK %s\n",this, key, u_errorName(status), ures_getLocale(fFillin, &status));
   229 #endif
   230     }
   231     return fFillin;
   232 }
   234 UResourceBundle* CalendarData::getByKey2(const char *key, const char *subKey, UErrorCode& status) {
   235     if(U_FAILURE(status)) {
   236         return NULL;
   237     }
   239     if(fBundle) {
   240 #if defined (U_DEBUG_CALDATA)
   241         fprintf(stderr, "%p: //\n");
   242 #endif
   243         fFillin = ures_getByKeyWithFallback(fBundle, key, fFillin, &status);
   244         fOtherFillin = ures_getByKeyWithFallback(fFillin, U_FORMAT_KEY, fOtherFillin, &status);
   245         fFillin = ures_getByKeyWithFallback(fOtherFillin, subKey, fFillin, &status);
   246 #if defined (U_DEBUG_CALDATA)
   247         fprintf(stderr, "%p: get %s/format/%s -> %s - from MAIN %s\n", this, key, subKey, u_errorName(status), ures_getLocale(fFillin, &status));
   248 #endif
   249     }
   250     if(fFallback && (status == U_MISSING_RESOURCE_ERROR)) {
   251         status = U_ZERO_ERROR; // retry with fallback (gregorian)
   252         fFillin = ures_getByKeyWithFallback(fFallback, key, fFillin, &status);
   253         fOtherFillin = ures_getByKeyWithFallback(fFillin, U_FORMAT_KEY, fOtherFillin, &status);
   254         fFillin = ures_getByKeyWithFallback(fOtherFillin, subKey, fFillin, &status);
   255 #if defined (U_DEBUG_CALDATA)
   256         fprintf(stderr, "%p: get %s/format/%s -> %s - from FALLBACK %s\n",this, key, subKey, u_errorName(status), ures_getLocale(fFillin,&status));
   257 #endif
   258     }
   260 //// handling of 'default' keyword on failure: Commented out for 3.0.
   261 //   if((status == U_MISSING_RESOURCE_ERROR) && 
   262 //      uprv_strcmp(subKey,U_DEFAULT_KEY)) { // avoid recursion
   263 // #if defined (U_DEBUG_CALDATA)
   264 //     fprintf(stderr, "%p: - attempting fallback -\n", this);
   265 //     fflush(stderr);
   266 // #endif
   267 //     UErrorCode subStatus = U_ZERO_ERROR;
   268 //     int32_t len;
   269 //     char kwBuf[128] = "";
   270 //     const UChar *kw;
   271 //     /* fFillin = */ getByKey2(key, U_DEFAULT_KEY, subStatus);
   272 //     kw = ures_getString(fFillin, &len, &subStatus);
   273 //     if(len>126) { // too big
   274 //       len = 0;
   275 //     }
   276 //     if(U_SUCCESS(subStatus) && (len>0)) {
   277 //       u_UCharsToChars(kw, kwBuf, len+1);
   278 //       if(*kwBuf && uprv_strcmp(kwBuf,subKey)) {
   279 // #if defined (U_DEBUG_CALDATA)
   280 //         fprintf(stderr, "%p: trying  %s/format/default -> \"%s\"\n",this, key, kwBuf);
   281 // #endif
   282 //         // now try again with the default
   283 //         status = U_ZERO_ERROR;
   284 //         /* fFillin = */ getByKey2(key, kwBuf, status);
   285 //       }
   286 // #if defined (U_DEBUG_CALDATA)
   287 //     } else {
   288 //       fprintf(stderr, "%p: could not load  %s/format/default  - fail out (%s)\n",this, key, kwBuf, u_errorName(status));
   289 // #endif
   290 //     }
   291 //   }
   293     return fFillin;
   294 }
   296 UResourceBundle* CalendarData::getByKey3(const char *key, const char *contextKey, const char *subKey, UErrorCode& status) {
   297     if(U_FAILURE(status)) {
   298         return NULL;
   299     }
   301     if(fBundle) {
   302 #if defined (U_DEBUG_CALDATA)
   303         fprintf(stderr, "%p: //\n");
   304 #endif
   305         fFillin = ures_getByKeyWithFallback(fBundle, key, fFillin, &status);
   306         fOtherFillin = ures_getByKeyWithFallback(fFillin, contextKey, fOtherFillin, &status);
   307         fFillin = ures_getByKeyWithFallback(fOtherFillin, subKey, fFillin, &status);
   308 #if defined (U_DEBUG_CALDATA)
   309         fprintf(stderr, "%p: get %s/%s/%s -> %s - from MAIN %s\n", this, key, contextKey, subKey, u_errorName(status), ures_getLocale(fFillin, &status));
   310 #endif
   311     }
   312     if(fFallback && (status == U_MISSING_RESOURCE_ERROR)) {
   313         status = U_ZERO_ERROR; // retry with fallback (gregorian)
   314         fFillin = ures_getByKeyWithFallback(fFallback, key, fFillin, &status);
   315         fOtherFillin = ures_getByKeyWithFallback(fFillin, contextKey, fOtherFillin, &status);
   316         fFillin = ures_getByKeyWithFallback(fOtherFillin, subKey, fFillin, &status);
   317 #if defined (U_DEBUG_CALDATA)
   318         fprintf(stderr, "%p: get %s/%s/%s -> %s - from FALLBACK %s\n",this, key, contextKey, subKey, u_errorName(status), ures_getLocale(fFillin,&status));
   319 #endif
   320     }
   322     return fFillin;
   323 }
   325 U_NAMESPACE_END
   327 #endif
   328 //eof

mercurial