1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/hebrwcal.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,722 @@ 1.4 +/* 1.5 +****************************************************************************** 1.6 +* Copyright (C) 2003-2013, International Business Machines Corporation 1.7 +* and others. All Rights Reserved. 1.8 +****************************************************************************** 1.9 +* 1.10 +* File HEBRWCAL.CPP 1.11 +* 1.12 +* Modification History: 1.13 +* 1.14 +* Date Name Description 1.15 +* 12/03/2003 srl ported from java HebrewCalendar 1.16 +***************************************************************************** 1.17 +*/ 1.18 + 1.19 +#include "hebrwcal.h" 1.20 + 1.21 +#if !UCONFIG_NO_FORMATTING 1.22 + 1.23 +#include "umutex.h" 1.24 +#include <float.h> 1.25 +#include "gregoimp.h" // Math 1.26 +#include "astro.h" // CalendarAstronomer 1.27 +#include "uhash.h" 1.28 +#include "ucln_in.h" 1.29 + 1.30 +// Hebrew Calendar implementation 1.31 + 1.32 +/** 1.33 +* The absolute date, in milliseconds since 1/1/1970 AD, Gregorian, 1.34 +* of the start of the Hebrew calendar. In order to keep this calendar's 1.35 +* time of day in sync with that of the Gregorian calendar, we use 1.36 +* midnight, rather than sunset the day before. 1.37 +*/ 1.38 +//static const double EPOCH_MILLIS = -180799862400000.; // 1/1/1 HY 1.39 + 1.40 +static const int32_t LIMITS[UCAL_FIELD_COUNT][4] = { 1.41 + // Minimum Greatest Least Maximum 1.42 + // Minimum Maximum 1.43 + { 0, 0, 0, 0}, // ERA 1.44 + { -5000000, -5000000, 5000000, 5000000}, // YEAR 1.45 + { 0, 0, 12, 12}, // MONTH 1.46 + { 1, 1, 51, 56}, // WEEK_OF_YEAR 1.47 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH 1.48 + { 1, 1, 29, 30}, // DAY_OF_MONTH 1.49 + { 1, 1, 353, 385}, // DAY_OF_YEAR 1.50 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK 1.51 + { -1, -1, 5, 5}, // DAY_OF_WEEK_IN_MONTH 1.52 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM 1.53 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR 1.54 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY 1.55 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE 1.56 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND 1.57 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND 1.58 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET 1.59 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET 1.60 + { -5000000, -5000000, 5000000, 5000000}, // YEAR_WOY 1.61 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL 1.62 + { -5000000, -5000000, 5000000, 5000000}, // EXTENDED_YEAR 1.63 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY 1.64 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY 1.65 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH 1.66 +}; 1.67 + 1.68 +/** 1.69 +* The lengths of the Hebrew months. This is complicated, because there 1.70 +* are three different types of years, or six if you count leap years. 1.71 +* Due to the rules for postponing the start of the year to avoid having 1.72 +* certain holidays fall on the sabbath, the year can end up being three 1.73 +* different lengths, called "deficient", "normal", and "complete". 1.74 +*/ 1.75 +static const int8_t MONTH_LENGTH[][3] = { 1.76 + // Deficient Normal Complete 1.77 + { 30, 30, 30 }, //Tishri 1.78 + { 29, 29, 30 }, //Heshvan 1.79 + { 29, 30, 30 }, //Kislev 1.80 + { 29, 29, 29 }, //Tevet 1.81 + { 30, 30, 30 }, //Shevat 1.82 + { 30, 30, 30 }, //Adar I (leap years only) 1.83 + { 29, 29, 29 }, //Adar 1.84 + { 30, 30, 30 }, //Nisan 1.85 + { 29, 29, 29 }, //Iyar 1.86 + { 30, 30, 30 }, //Sivan 1.87 + { 29, 29, 29 }, //Tammuz 1.88 + { 30, 30, 30 }, //Av 1.89 + { 29, 29, 29 }, //Elul 1.90 +}; 1.91 + 1.92 +/** 1.93 +* The cumulative # of days to the end of each month in a non-leap year 1.94 +* Although this can be calculated from the MONTH_LENGTH table, 1.95 +* keeping it around separately makes some calculations a lot faster 1.96 +*/ 1.97 + 1.98 +static const int16_t MONTH_START[][3] = { 1.99 + // Deficient Normal Complete 1.100 + { 0, 0, 0 }, // (placeholder) 1.101 + { 30, 30, 30 }, // Tishri 1.102 + { 59, 59, 60 }, // Heshvan 1.103 + { 88, 89, 90 }, // Kislev 1.104 + { 117, 118, 119 }, // Tevet 1.105 + { 147, 148, 149 }, // Shevat 1.106 + { 147, 148, 149 }, // (Adar I placeholder) 1.107 + { 176, 177, 178 }, // Adar 1.108 + { 206, 207, 208 }, // Nisan 1.109 + { 235, 236, 237 }, // Iyar 1.110 + { 265, 266, 267 }, // Sivan 1.111 + { 294, 295, 296 }, // Tammuz 1.112 + { 324, 325, 326 }, // Av 1.113 + { 353, 354, 355 }, // Elul 1.114 +}; 1.115 + 1.116 +/** 1.117 +* The cumulative # of days to the end of each month in a leap year 1.118 +*/ 1.119 +static const int16_t LEAP_MONTH_START[][3] = { 1.120 + // Deficient Normal Complete 1.121 + { 0, 0, 0 }, // (placeholder) 1.122 + { 30, 30, 30 }, // Tishri 1.123 + { 59, 59, 60 }, // Heshvan 1.124 + { 88, 89, 90 }, // Kislev 1.125 + { 117, 118, 119 }, // Tevet 1.126 + { 147, 148, 149 }, // Shevat 1.127 + { 177, 178, 179 }, // Adar I 1.128 + { 206, 207, 208 }, // Adar II 1.129 + { 236, 237, 238 }, // Nisan 1.130 + { 265, 266, 267 }, // Iyar 1.131 + { 295, 296, 297 }, // Sivan 1.132 + { 324, 325, 326 }, // Tammuz 1.133 + { 354, 355, 356 }, // Av 1.134 + { 383, 384, 385 }, // Elul 1.135 +}; 1.136 + 1.137 +static icu::CalendarCache *gCache = NULL; 1.138 + 1.139 +U_CDECL_BEGIN 1.140 +static UBool calendar_hebrew_cleanup(void) { 1.141 + delete gCache; 1.142 + gCache = NULL; 1.143 + return TRUE; 1.144 +} 1.145 +U_CDECL_END 1.146 + 1.147 +U_NAMESPACE_BEGIN 1.148 +//------------------------------------------------------------------------- 1.149 +// Constructors... 1.150 +//------------------------------------------------------------------------- 1.151 + 1.152 +/** 1.153 +* Constructs a default <code>HebrewCalendar</code> using the current time 1.154 +* in the default time zone with the default locale. 1.155 +* @internal 1.156 +*/ 1.157 +HebrewCalendar::HebrewCalendar(const Locale& aLocale, UErrorCode& success) 1.158 +: Calendar(TimeZone::createDefault(), aLocale, success) 1.159 + 1.160 +{ 1.161 + setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. 1.162 +} 1.163 + 1.164 + 1.165 +HebrewCalendar::~HebrewCalendar() { 1.166 +} 1.167 + 1.168 +const char *HebrewCalendar::getType() const { 1.169 + return "hebrew"; 1.170 +} 1.171 + 1.172 +Calendar* HebrewCalendar::clone() const { 1.173 + return new HebrewCalendar(*this); 1.174 +} 1.175 + 1.176 +HebrewCalendar::HebrewCalendar(const HebrewCalendar& other) : Calendar(other) { 1.177 +} 1.178 + 1.179 + 1.180 +//------------------------------------------------------------------------- 1.181 +// Rolling and adding functions overridden from Calendar 1.182 +// 1.183 +// These methods call through to the default implementation in IBMCalendar 1.184 +// for most of the fields and only handle the unusual ones themselves. 1.185 +//------------------------------------------------------------------------- 1.186 + 1.187 +/** 1.188 +* Add a signed amount to a specified field, using this calendar's rules. 1.189 +* For example, to add three days to the current date, you can call 1.190 +* <code>add(Calendar.DATE, 3)</code>. 1.191 +* <p> 1.192 +* When adding to certain fields, the values of other fields may conflict and 1.193 +* need to be changed. For example, when adding one to the {@link #MONTH MONTH} field 1.194 +* for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field 1.195 +* must be adjusted so that the result is "29 Elul 5758" rather than the invalid 1.196 +* "30 Elul 5758". 1.197 +* <p> 1.198 +* This method is able to add to 1.199 +* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET}, 1.200 +* and {@link #ZONE_OFFSET ZONE_OFFSET}. 1.201 +* <p> 1.202 +* <b>Note:</b> You should always use {@link #roll roll} and add rather 1.203 +* than attempting to perform arithmetic operations directly on the fields 1.204 +* of a <tt>HebrewCalendar</tt>. Since the {@link #MONTH MONTH} field behaves 1.205 +* discontinuously in non-leap years, simple arithmetic can give invalid results. 1.206 +* <p> 1.207 +* @param field the time field. 1.208 +* @param amount the amount to add to the field. 1.209 +* 1.210 +* @exception IllegalArgumentException if the field is invalid or refers 1.211 +* to a field that cannot be handled by this method. 1.212 +* @internal 1.213 +*/ 1.214 +void HebrewCalendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) 1.215 +{ 1.216 + if(U_FAILURE(status)) { 1.217 + return; 1.218 + } 1.219 + switch (field) { 1.220 + case UCAL_MONTH: 1.221 + { 1.222 + // We can't just do a set(MONTH, get(MONTH) + amount). The 1.223 + // reason is ADAR_1. Suppose amount is +2 and we land in 1.224 + // ADAR_1 -- then we have to bump to ADAR_2 aka ADAR. But 1.225 + // if amount is -2 and we land in ADAR_1, then we have to 1.226 + // bump the other way -- down to SHEVAT. - Alan 11/00 1.227 + int32_t month = get(UCAL_MONTH, status); 1.228 + int32_t year = get(UCAL_YEAR, status); 1.229 + UBool acrossAdar1; 1.230 + if (amount > 0) { 1.231 + acrossAdar1 = (month < ADAR_1); // started before ADAR_1? 1.232 + month += amount; 1.233 + for (;;) { 1.234 + if (acrossAdar1 && month>=ADAR_1 && !isLeapYear(year)) { 1.235 + ++month; 1.236 + } 1.237 + if (month <= ELUL) { 1.238 + break; 1.239 + } 1.240 + month -= ELUL+1; 1.241 + ++year; 1.242 + acrossAdar1 = TRUE; 1.243 + } 1.244 + } else { 1.245 + acrossAdar1 = (month > ADAR_1); // started after ADAR_1? 1.246 + month += amount; 1.247 + for (;;) { 1.248 + if (acrossAdar1 && month<=ADAR_1 && !isLeapYear(year)) { 1.249 + --month; 1.250 + } 1.251 + if (month >= 0) { 1.252 + break; 1.253 + } 1.254 + month += ELUL+1; 1.255 + --year; 1.256 + acrossAdar1 = TRUE; 1.257 + } 1.258 + } 1.259 + set(UCAL_MONTH, month); 1.260 + set(UCAL_YEAR, year); 1.261 + pinField(UCAL_DAY_OF_MONTH, status); 1.262 + break; 1.263 + } 1.264 + 1.265 + default: 1.266 + Calendar::add(field, amount, status); 1.267 + break; 1.268 + } 1.269 +} 1.270 + 1.271 +/** 1.272 +* @deprecated ICU 2.6 use UCalendarDateFields instead of EDateFields 1.273 +*/ 1.274 +void HebrewCalendar::add(EDateFields field, int32_t amount, UErrorCode& status) 1.275 +{ 1.276 + add((UCalendarDateFields)field, amount, status); 1.277 +} 1.278 + 1.279 +/** 1.280 +* Rolls (up/down) a specified amount time on the given field. For 1.281 +* example, to roll the current date up by three days, you can call 1.282 +* <code>roll(Calendar.DATE, 3)</code>. If the 1.283 +* field is rolled past its maximum allowable value, it will "wrap" back 1.284 +* to its minimum and continue rolling. 1.285 +* For example, calling <code>roll(Calendar.DATE, 10)</code> 1.286 +* on a Hebrew calendar set to "25 Av 5758" will result in the date "5 Av 5758". 1.287 +* <p> 1.288 +* When rolling certain fields, the values of other fields may conflict and 1.289 +* need to be changed. For example, when rolling the {@link #MONTH MONTH} field 1.290 +* upward by one for the date "30 Av 5758", the {@link #DAY_OF_MONTH DAY_OF_MONTH} field 1.291 +* must be adjusted so that the result is "29 Elul 5758" rather than the invalid 1.292 +* "30 Elul". 1.293 +* <p> 1.294 +* This method is able to roll 1.295 +* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET}, 1.296 +* and {@link #ZONE_OFFSET ZONE_OFFSET}. Subclasses may, of course, add support for 1.297 +* additional fields in their overrides of <code>roll</code>. 1.298 +* <p> 1.299 +* <b>Note:</b> You should always use roll and {@link #add add} rather 1.300 +* than attempting to perform arithmetic operations directly on the fields 1.301 +* of a <tt>HebrewCalendar</tt>. Since the {@link #MONTH MONTH} field behaves 1.302 +* discontinuously in non-leap years, simple arithmetic can give invalid results. 1.303 +* <p> 1.304 +* @param field the time field. 1.305 +* @param amount the amount by which the field should be rolled. 1.306 +* 1.307 +* @exception IllegalArgumentException if the field is invalid or refers 1.308 +* to a field that cannot be handled by this method. 1.309 +* @internal 1.310 +*/ 1.311 +void HebrewCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) 1.312 +{ 1.313 + if(U_FAILURE(status)) { 1.314 + return; 1.315 + } 1.316 + switch (field) { 1.317 + case UCAL_MONTH: 1.318 + { 1.319 + int32_t month = get(UCAL_MONTH, status); 1.320 + int32_t year = get(UCAL_YEAR, status); 1.321 + 1.322 + UBool leapYear = isLeapYear(year); 1.323 + int32_t yearLength = monthsInYear(year); 1.324 + int32_t newMonth = month + (amount % yearLength); 1.325 + // 1.326 + // If it's not a leap year and we're rolling past the missing month 1.327 + // of ADAR_1, we need to roll an extra month to make up for it. 1.328 + // 1.329 + if (!leapYear) { 1.330 + if (amount > 0 && month < ADAR_1 && newMonth >= ADAR_1) { 1.331 + newMonth++; 1.332 + } else if (amount < 0 && month > ADAR_1 && newMonth <= ADAR_1) { 1.333 + newMonth--; 1.334 + } 1.335 + } 1.336 + set(UCAL_MONTH, (newMonth + 13) % 13); 1.337 + pinField(UCAL_DAY_OF_MONTH, status); 1.338 + return; 1.339 + } 1.340 + default: 1.341 + Calendar::roll(field, amount, status); 1.342 + } 1.343 +} 1.344 + 1.345 +void HebrewCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { 1.346 + roll((UCalendarDateFields)field, amount, status); 1.347 +} 1.348 + 1.349 +//------------------------------------------------------------------------- 1.350 +// Support methods 1.351 +//------------------------------------------------------------------------- 1.352 + 1.353 +// Hebrew date calculations are performed in terms of days, hours, and 1.354 +// "parts" (or halakim), which are 1/1080 of an hour, or 3 1/3 seconds. 1.355 +static const int32_t HOUR_PARTS = 1080; 1.356 +static const int32_t DAY_PARTS = 24*HOUR_PARTS; 1.357 + 1.358 +// An approximate value for the length of a lunar month. 1.359 +// It is used to calculate the approximate year and month of a given 1.360 +// absolute date. 1.361 +static const int32_t MONTH_DAYS = 29; 1.362 +static const int32_t MONTH_FRACT = 12*HOUR_PARTS + 793; 1.363 +static const int32_t MONTH_PARTS = MONTH_DAYS*DAY_PARTS + MONTH_FRACT; 1.364 + 1.365 +// The time of the new moon (in parts) on 1 Tishri, year 1 (the epoch) 1.366 +// counting from noon on the day before. BAHARAD is an abbreviation of 1.367 +// Bet (Monday), Hey (5 hours from sunset), Resh-Daled (204). 1.368 +static const int32_t BAHARAD = 11*HOUR_PARTS + 204; 1.369 + 1.370 +/** 1.371 +* Finds the day # of the first day in the given Hebrew year. 1.372 +* To do this, we want to calculate the time of the Tishri 1 new moon 1.373 +* in that year. 1.374 +* <p> 1.375 +* The algorithm here is similar to ones described in a number of 1.376 +* references, including: 1.377 +* <ul> 1.378 +* <li>"Calendrical Calculations", by Nachum Dershowitz & Edward Reingold, 1.379 +* Cambridge University Press, 1997, pages 85-91. 1.380 +* 1.381 +* <li>Hebrew Calendar Science and Myths, 1.382 +* <a href="http://www.geocities.com/Athens/1584/"> 1.383 +* http://www.geocities.com/Athens/1584/</a> 1.384 +* 1.385 +* <li>The Calendar FAQ, 1.386 +* <a href="http://www.faqs.org/faqs/calendars/faq/"> 1.387 +* http://www.faqs.org/faqs/calendars/faq/</a> 1.388 +* </ul> 1.389 +*/ 1.390 +int32_t HebrewCalendar::startOfYear(int32_t year, UErrorCode &status) 1.391 +{ 1.392 + ucln_i18n_registerCleanup(UCLN_I18N_HEBREW_CALENDAR, calendar_hebrew_cleanup); 1.393 + int32_t day = CalendarCache::get(&gCache, year, status); 1.394 + 1.395 + if (day == 0) { 1.396 + int32_t months = (235 * year - 234) / 19; // # of months before year 1.397 + 1.398 + int64_t frac = (int64_t)months * MONTH_FRACT + BAHARAD; // Fractional part of day # 1.399 + day = months * 29 + (int32_t)(frac / DAY_PARTS); // Whole # part of calculation 1.400 + frac = frac % DAY_PARTS; // Time of day 1.401 + 1.402 + int32_t wd = (day % 7); // Day of week (0 == Monday) 1.403 + 1.404 + if (wd == 2 || wd == 4 || wd == 6) { 1.405 + // If the 1st is on Sun, Wed, or Fri, postpone to the next day 1.406 + day += 1; 1.407 + wd = (day % 7); 1.408 + } 1.409 + if (wd == 1 && frac > 15*HOUR_PARTS+204 && !isLeapYear(year) ) { 1.410 + // If the new moon falls after 3:11:20am (15h204p from the previous noon) 1.411 + // on a Tuesday and it is not a leap year, postpone by 2 days. 1.412 + // This prevents 356-day years. 1.413 + day += 2; 1.414 + } 1.415 + else if (wd == 0 && frac > 21*HOUR_PARTS+589 && isLeapYear(year-1) ) { 1.416 + // If the new moon falls after 9:32:43 1/3am (21h589p from yesterday noon) 1.417 + // on a Monday and *last* year was a leap year, postpone by 1 day. 1.418 + // Prevents 382-day years. 1.419 + day += 1; 1.420 + } 1.421 + CalendarCache::put(&gCache, year, day, status); 1.422 + } 1.423 + return day; 1.424 +} 1.425 + 1.426 +/** 1.427 +* Find the day of the week for a given day 1.428 +* 1.429 +* @param day The # of days since the start of the Hebrew calendar, 1.430 +* 1-based (i.e. 1/1/1 AM is day 1). 1.431 +*/ 1.432 +int32_t HebrewCalendar::absoluteDayToDayOfWeek(int32_t day) 1.433 +{ 1.434 + // We know that 1/1/1 AM is a Monday, which makes the math easy... 1.435 + return (day % 7) + 1; 1.436 +} 1.437 + 1.438 +/** 1.439 +* Returns the the type of a given year. 1.440 +* 0 "Deficient" year with 353 or 383 days 1.441 +* 1 "Normal" year with 354 or 384 days 1.442 +* 2 "Complete" year with 355 or 385 days 1.443 +*/ 1.444 +int32_t HebrewCalendar::yearType(int32_t year) const 1.445 +{ 1.446 + int32_t yearLength = handleGetYearLength(year); 1.447 + 1.448 + if (yearLength > 380) { 1.449 + yearLength -= 30; // Subtract length of leap month. 1.450 + } 1.451 + 1.452 + int type = 0; 1.453 + 1.454 + switch (yearLength) { 1.455 + case 353: 1.456 + type = 0; break; 1.457 + case 354: 1.458 + type = 1; break; 1.459 + case 355: 1.460 + type = 2; break; 1.461 + default: 1.462 + //throw new RuntimeException("Illegal year length " + yearLength + " in year " + year); 1.463 + type = 1; 1.464 + } 1.465 + return type; 1.466 +} 1.467 + 1.468 +/** 1.469 +* Determine whether a given Hebrew year is a leap year 1.470 +* 1.471 +* The rule here is that if (year % 19) == 0, 3, 6, 8, 11, 14, or 17. 1.472 +* The formula below performs the same test, believe it or not. 1.473 +*/ 1.474 +UBool HebrewCalendar::isLeapYear(int32_t year) { 1.475 + //return (year * 12 + 17) % 19 >= 12; 1.476 + int32_t x = (year*12 + 17) % 19; 1.477 + return x >= ((x < 0) ? -7 : 12); 1.478 +} 1.479 + 1.480 +int32_t HebrewCalendar::monthsInYear(int32_t year) { 1.481 + return isLeapYear(year) ? 13 : 12; 1.482 +} 1.483 + 1.484 +//------------------------------------------------------------------------- 1.485 +// Calendar framework 1.486 +//------------------------------------------------------------------------- 1.487 + 1.488 +/** 1.489 +* @internal 1.490 +*/ 1.491 +int32_t HebrewCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { 1.492 + return LIMITS[field][limitType]; 1.493 +} 1.494 + 1.495 +/** 1.496 +* Returns the length of the given month in the given year 1.497 +* @internal 1.498 +*/ 1.499 +int32_t HebrewCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { 1.500 + // Resolve out-of-range months. This is necessary in order to 1.501 + // obtain the correct year. We correct to 1.502 + // a 12- or 13-month year (add/subtract 12 or 13, depending 1.503 + // on the year) but since we _always_ number from 0..12, and 1.504 + // the leap year determines whether or not month 5 (Adar 1) 1.505 + // is present, we allow 0..12 in any given year. 1.506 + while (month < 0) { 1.507 + month += monthsInYear(--extendedYear); 1.508 + } 1.509 + // Careful: allow 0..12 in all years 1.510 + while (month > 12) { 1.511 + month -= monthsInYear(extendedYear++); 1.512 + } 1.513 + 1.514 + switch (month) { 1.515 + case HESHVAN: 1.516 + case KISLEV: 1.517 + // These two month lengths can vary 1.518 + return MONTH_LENGTH[month][yearType(extendedYear)]; 1.519 + 1.520 + default: 1.521 + // The rest are a fixed length 1.522 + return MONTH_LENGTH[month][0]; 1.523 + } 1.524 +} 1.525 + 1.526 +/** 1.527 +* Returns the number of days in the given Hebrew year 1.528 +* @internal 1.529 +*/ 1.530 +int32_t HebrewCalendar::handleGetYearLength(int32_t eyear) const { 1.531 + UErrorCode status = U_ZERO_ERROR; 1.532 + return startOfYear(eyear+1, status) - startOfYear(eyear, status); 1.533 +} 1.534 + 1.535 +//------------------------------------------------------------------------- 1.536 +// Functions for converting from milliseconds to field values 1.537 +//------------------------------------------------------------------------- 1.538 + 1.539 +/** 1.540 +* Subclasses may override this method to compute several fields 1.541 +* specific to each calendar system. These are: 1.542 +* 1.543 +* <ul><li>ERA 1.544 +* <li>YEAR 1.545 +* <li>MONTH 1.546 +* <li>DAY_OF_MONTH 1.547 +* <li>DAY_OF_YEAR 1.548 +* <li>EXTENDED_YEAR</ul> 1.549 +* 1.550 +* Subclasses can refer to the DAY_OF_WEEK and DOW_LOCAL fields, 1.551 +* which will be set when this method is called. Subclasses can 1.552 +* also call the getGregorianXxx() methods to obtain Gregorian 1.553 +* calendar equivalents for the given Julian day. 1.554 +* 1.555 +* <p>In addition, subclasses should compute any subclass-specific 1.556 +* fields, that is, fields from BASE_FIELD_COUNT to 1.557 +* getFieldCount() - 1. 1.558 +* @internal 1.559 +*/ 1.560 +void HebrewCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { 1.561 + int32_t d = julianDay - 347997; 1.562 + double m = ((d * (double)DAY_PARTS)/ (double) MONTH_PARTS); // Months (approx) 1.563 + int32_t year = (int32_t)( ((19. * m + 234.) / 235.) + 1.); // Years (approx) 1.564 + int32_t ys = startOfYear(year, status); // 1st day of year 1.565 + int32_t dayOfYear = (d - ys); 1.566 + 1.567 + // Because of the postponement rules, it's possible to guess wrong. Fix it. 1.568 + while (dayOfYear < 1) { 1.569 + year--; 1.570 + ys = startOfYear(year, status); 1.571 + dayOfYear = (d - ys); 1.572 + } 1.573 + 1.574 + // Now figure out which month we're in, and the date within that month 1.575 + int32_t type = yearType(year); 1.576 + UBool isLeap = isLeapYear(year); 1.577 + 1.578 + int32_t month = 0; 1.579 + int32_t momax = sizeof(MONTH_START) / (3 * sizeof(MONTH_START[0][0])); 1.580 + while (month < momax && dayOfYear > ( isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type] ) ) { 1.581 + month++; 1.582 + } 1.583 + if (month >= momax || month<=0) { 1.584 + // TODO: I found dayOfYear could be out of range when 1.585 + // a large value is set to julianDay. I patched startOfYear 1.586 + // to reduce the chace, but it could be still reproduced either 1.587 + // by startOfYear or other places. For now, we check 1.588 + // the month is in valid range to avoid out of array index 1.589 + // access problem here. However, we need to carefully review 1.590 + // the calendar implementation to check the extreme limit of 1.591 + // each calendar field and the code works well for any values 1.592 + // in the valid value range. -yoshito 1.593 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.594 + return; 1.595 + } 1.596 + month--; 1.597 + int dayOfMonth = dayOfYear - (isLeap ? LEAP_MONTH_START[month][type] : MONTH_START[month][type]); 1.598 + 1.599 + internalSet(UCAL_ERA, 0); 1.600 + internalSet(UCAL_YEAR, year); 1.601 + internalSet(UCAL_EXTENDED_YEAR, year); 1.602 + internalSet(UCAL_MONTH, month); 1.603 + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); 1.604 + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); 1.605 +} 1.606 + 1.607 +//------------------------------------------------------------------------- 1.608 +// Functions for converting from field values to milliseconds 1.609 +//------------------------------------------------------------------------- 1.610 + 1.611 +/** 1.612 +* @internal 1.613 +*/ 1.614 +int32_t HebrewCalendar::handleGetExtendedYear() { 1.615 + int32_t year; 1.616 + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { 1.617 + year = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 1.618 + } else { 1.619 + year = internalGet(UCAL_YEAR, 1); // Default to year 1 1.620 + } 1.621 + return year; 1.622 +} 1.623 + 1.624 +/** 1.625 +* Return JD of start of given month/year. 1.626 +* @internal 1.627 +*/ 1.628 +int32_t HebrewCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UBool /*useMonth*/) const { 1.629 + UErrorCode status = U_ZERO_ERROR; 1.630 + // Resolve out-of-range months. This is necessary in order to 1.631 + // obtain the correct year. We correct to 1.632 + // a 12- or 13-month year (add/subtract 12 or 13, depending 1.633 + // on the year) but since we _always_ number from 0..12, and 1.634 + // the leap year determines whether or not month 5 (Adar 1) 1.635 + // is present, we allow 0..12 in any given year. 1.636 + while (month < 0) { 1.637 + month += monthsInYear(--eyear); 1.638 + } 1.639 + // Careful: allow 0..12 in all years 1.640 + while (month > 12) { 1.641 + month -= monthsInYear(eyear++); 1.642 + } 1.643 + 1.644 + int32_t day = startOfYear(eyear, status); 1.645 + 1.646 + if(U_FAILURE(status)) { 1.647 + return 0; 1.648 + } 1.649 + 1.650 + if (month != 0) { 1.651 + if (isLeapYear(eyear)) { 1.652 + day += LEAP_MONTH_START[month][yearType(eyear)]; 1.653 + } else { 1.654 + day += MONTH_START[month][yearType(eyear)]; 1.655 + } 1.656 + } 1.657 + 1.658 + return (int) (day + 347997); 1.659 +} 1.660 + 1.661 +UBool 1.662 +HebrewCalendar::inDaylightTime(UErrorCode& status) const 1.663 +{ 1.664 + // copied from GregorianCalendar 1.665 + if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) 1.666 + return FALSE; 1.667 + 1.668 + // Force an update of the state of the Calendar. 1.669 + ((HebrewCalendar*)this)->complete(status); // cast away const 1.670 + 1.671 + return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE); 1.672 +} 1.673 + 1.674 +/** 1.675 + * The system maintains a static default century start date and Year. They are 1.676 + * initialized the first time they are used. Once the system default century date 1.677 + * and year are set, they do not change. 1.678 + */ 1.679 +static UDate gSystemDefaultCenturyStart = DBL_MIN; 1.680 +static int32_t gSystemDefaultCenturyStartYear = -1; 1.681 +static icu::UInitOnce gSystemDefaultCenturyInit = U_INITONCE_INITIALIZER; 1.682 + 1.683 +UBool HebrewCalendar::haveDefaultCentury() const 1.684 +{ 1.685 + return TRUE; 1.686 +} 1.687 + 1.688 +static void U_CALLCONV initializeSystemDefaultCentury() 1.689 +{ 1.690 + // initialize systemDefaultCentury and systemDefaultCenturyYear based 1.691 + // on the current time. They'll be set to 80 years before 1.692 + // the current time. 1.693 + UErrorCode status = U_ZERO_ERROR; 1.694 + HebrewCalendar calendar(Locale("@calendar=hebrew"),status); 1.695 + if (U_SUCCESS(status)) { 1.696 + calendar.setTime(Calendar::getNow(), status); 1.697 + calendar.add(UCAL_YEAR, -80, status); 1.698 + 1.699 + gSystemDefaultCenturyStart = calendar.getTime(status); 1.700 + gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); 1.701 + } 1.702 + // We have no recourse upon failure unless we want to propagate the failure 1.703 + // out. 1.704 +} 1.705 + 1.706 + 1.707 +UDate HebrewCalendar::defaultCenturyStart() const { 1.708 + // lazy-evaluate systemDefaultCenturyStart 1.709 + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); 1.710 + return gSystemDefaultCenturyStart; 1.711 +} 1.712 + 1.713 +int32_t HebrewCalendar::defaultCenturyStartYear() const { 1.714 + // lazy-evaluate systemDefaultCenturyStartYear 1.715 + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); 1.716 + return gSystemDefaultCenturyStartYear; 1.717 +} 1.718 + 1.719 + 1.720 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(HebrewCalendar) 1.721 + 1.722 +U_NAMESPACE_END 1.723 + 1.724 +#endif // UCONFIG_NO_FORMATTING 1.725 +