OLD | NEW |
(Empty) | |
| 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 i
s 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 li
mitType) 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 mont
h) 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, U
Bool 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 &/*statu
s*/) { |
| 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) : FAL
SE); |
| 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), ne
edsUpdate); |
| 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), ne
edsUpdate); |
| 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 |
OLD | NEW |