OLD | NEW |
(Empty) | |
| 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 */ |
| 11 |
| 12 #include "gregoimp.h" |
| 13 |
| 14 #if !UCONFIG_NO_FORMATTING |
| 15 |
| 16 #include "unicode/ucal.h" |
| 17 #include "uresimp.h" |
| 18 #include "cstring.h" |
| 19 #include "uassert.h" |
| 20 |
| 21 #if defined(U_DEBUG_CALDATA) |
| 22 #include <stdio.h> |
| 23 #endif |
| 24 |
| 25 U_NAMESPACE_BEGIN |
| 26 |
| 27 int32_t ClockMath::floorDivide(int32_t numerator, int32_t denominator) { |
| 28 return (numerator >= 0) ? |
| 29 numerator / denominator : ((numerator + 1) / denominator) - 1; |
| 30 } |
| 31 |
| 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 } |
| 39 |
| 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 } |
| 73 |
| 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 |
| 76 |
| 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}; |
| 80 |
| 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}; |
| 84 |
| 85 double Grego::fieldsToDay(int32_t year, int32_t month, int32_t dom) { |
| 86 |
| 87 int32_t y = year - 1; |
| 88 |
| 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 |
| 92 |
| 93 return julian - JULIAN_1970_CE; // JD => epoch day |
| 94 } |
| 95 |
| 96 void Grego::dayToFields(double day, int32_t& year, int32_t& month, |
| 97 int32_t& dom, int32_t& dow, int32_t& doy) { |
| 98 |
| 99 // Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar) |
| 100 day += JULIAN_1970_CE - JULIAN_1_CE; |
| 101 |
| 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 l
ength |
| 107 int32_t n100 = ClockMath::floorDivide(doy, 36524, doy); // 100-year cycle le
ngth |
| 108 int32_t n4 = ClockMath::floorDivide(doy, 1461, doy); // 4-year cycle lengt
h |
| 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 } |
| 116 |
| 117 UBool isLeap = isLeapYear(year); |
| 118 |
| 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; |
| 122 |
| 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 } |
| 133 |
| 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 } |
| 141 |
| 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 } |
| 147 |
| 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 } |
| 159 |
| 160 /* ---- CalendarData ------ */ |
| 161 |
| 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) |
| 167 |
| 168 |
| 169 // CalendarData::CalendarData(const Locale& loc, UErrorCode& status) |
| 170 // : fFillin(NULL), fBundle(NULL), fFallback(NULL) { |
| 171 // initData(loc.getBaseName(), (char*) "???", status); |
| 172 // } |
| 173 |
| 174 CalendarData::CalendarData(const Locale& loc, const char *type, UErrorCode& stat
us) |
| 175 : fFillin(NULL), fOtherFillin(NULL), fBundle(NULL), fFallback(NULL) { |
| 176 initData(loc.getBaseName(), type, status); |
| 177 } |
| 178 |
| 179 void CalendarData::initData(const char *locale, const char *type, UErrorCode& st
atus) { |
| 180 fOtherFillin = ures_open(U_CALENDAR_DATA, locale, &status); |
| 181 fFillin = ures_getByKey(fOtherFillin, U_CALENDAR_KEY, fFillin, &status); |
| 182 |
| 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, &statu
s); |
| 189 |
| 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, &sta
tus):""); |
| 194 #endif |
| 195 |
| 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, f
Bundle?ures_getLocale(fBundle, &status):"" ); |
| 201 #endif |
| 202 } |
| 203 } |
| 204 |
| 205 CalendarData::~CalendarData() { |
| 206 ures_close(fFillin); |
| 207 ures_close(fBundle); |
| 208 ures_close(fFallback); |
| 209 ures_close(fOtherFillin); |
| 210 } |
| 211 |
| 212 UResourceBundle* |
| 213 CalendarData::getByKey(const char *key, UErrorCode& status) { |
| 214 if(U_FAILURE(status)) { |
| 215 return NULL; |
| 216 } |
| 217 |
| 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_errorNa
me(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_err
orName(status), ures_getLocale(fFillin, &status)); |
| 229 #endif |
| 230 } |
| 231 return fFillin; |
| 232 } |
| 233 |
| 234 UResourceBundle* CalendarData::getByKey2(const char *key, const char *subKey, UE
rrorCode& status) { |
| 235 if(U_FAILURE(status)) { |
| 236 return NULL; |
| 237 } |
| 238 |
| 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, fOtherFi
llin, &status); |
| 245 fFillin = ures_getByKeyWithFallback(fOtherFillin, subKey, fFillin, &stat
us); |
| 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, fOtherFi
llin, &status); |
| 254 fFillin = ures_getByKeyWithFallback(fOtherFillin, subKey, fFillin, &stat
us); |
| 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 } |
| 259 |
| 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 // } |
| 292 |
| 293 return fFillin; |
| 294 } |
| 295 |
| 296 UResourceBundle* CalendarData::getByKey3(const char *key, const char *contextKey
, const char *subKey, UErrorCode& status) { |
| 297 if(U_FAILURE(status)) { |
| 298 return NULL; |
| 299 } |
| 300 |
| 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, fOtherFill
in, &status); |
| 307 fFillin = ures_getByKeyWithFallback(fOtherFillin, subKey, fFillin, &stat
us); |
| 308 #if defined (U_DEBUG_CALDATA) |
| 309 fprintf(stderr, "%p: get %s/%s/%s -> %s - from MAIN %s\n", this, key, co
ntextKey, 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, fOtherFill
in, &status); |
| 316 fFillin = ures_getByKeyWithFallback(fOtherFillin, subKey, fFillin, &stat
us); |
| 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 } |
| 321 |
| 322 return fFillin; |
| 323 } |
| 324 |
| 325 U_NAMESPACE_END |
| 326 |
| 327 #endif |
| 328 //eof |
OLD | NEW |