1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/gregocal.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1317 @@ 1.4 +/* 1.5 +******************************************************************************* 1.6 +* Copyright (C) 1997-2013, International Business Machines Corporation and 1.7 +* others. All Rights Reserved. 1.8 +******************************************************************************* 1.9 +* 1.10 +* File GREGOCAL.CPP 1.11 +* 1.12 +* Modification History: 1.13 +* 1.14 +* Date Name Description 1.15 +* 02/05/97 clhuang Creation. 1.16 +* 03/28/97 aliu Made highly questionable fix to computeFields to 1.17 +* handle DST correctly. 1.18 +* 04/22/97 aliu Cleaned up code drastically. Added monthLength(). 1.19 +* Finished unimplemented parts of computeTime() for 1.20 +* week-based date determination. Removed quetionable 1.21 +* fix and wrote correct fix for computeFields() and 1.22 +* daylight time handling. Rewrote inDaylightTime() 1.23 +* and computeFields() to handle sensitive Daylight to 1.24 +* Standard time transitions correctly. 1.25 +* 05/08/97 aliu Added code review changes. Fixed isLeapYear() to 1.26 +* not cutover. 1.27 +* 08/12/97 aliu Added equivalentTo. Misc other fixes. Updated 1.28 +* add() from Java source. 1.29 +* 07/28/98 stephen Sync up with JDK 1.2 1.30 +* 09/14/98 stephen Changed type of kOneDay, kOneWeek to double. 1.31 +* Fixed bug in roll() 1.32 +* 10/15/99 aliu Fixed j31, incorrect WEEK_OF_YEAR computation. 1.33 +* 10/15/99 aliu Fixed j32, cannot set date to Feb 29 2000 AD. 1.34 +* {JDK bug 4210209 4209272} 1.35 +* 11/15/99 weiv Added YEAR_WOY and DOW_LOCAL computation 1.36 +* to timeToFields method, updated kMinValues, kMaxValues & kLeastMaxValues 1.37 +* 12/09/99 aliu Fixed j81, calculation errors and roll bugs 1.38 +* in year of cutover. 1.39 +* 01/24/2000 aliu Revised computeJulianDay for YEAR YEAR_WOY WOY. 1.40 +******************************************************************************** 1.41 +*/ 1.42 + 1.43 +#include "unicode/utypes.h" 1.44 +#include <float.h> 1.45 + 1.46 +#if !UCONFIG_NO_FORMATTING 1.47 + 1.48 +#include "unicode/gregocal.h" 1.49 +#include "gregoimp.h" 1.50 +#include "umutex.h" 1.51 +#include "uassert.h" 1.52 + 1.53 +// ***************************************************************************** 1.54 +// class GregorianCalendar 1.55 +// ***************************************************************************** 1.56 + 1.57 +/** 1.58 +* Note that the Julian date used here is not a true Julian date, since 1.59 +* it is measured from midnight, not noon. This value is the Julian 1.60 +* day number of January 1, 1970 (Gregorian calendar) at noon UTC. [LIU] 1.61 +*/ 1.62 + 1.63 +static const int16_t kNumDays[] 1.64 += {0,31,59,90,120,151,181,212,243,273,304,334}; // 0-based, for day-in-year 1.65 +static const int16_t kLeapNumDays[] 1.66 += {0,31,60,91,121,152,182,213,244,274,305,335}; // 0-based, for day-in-year 1.67 +static const int8_t kMonthLength[] 1.68 += {31,28,31,30,31,30,31,31,30,31,30,31}; // 0-based 1.69 +static const int8_t kLeapMonthLength[] 1.70 += {31,29,31,30,31,30,31,31,30,31,30,31}; // 0-based 1.71 + 1.72 +// setTimeInMillis() limits the Julian day range to +/-7F000000. 1.73 +// This would seem to limit the year range to: 1.74 +// ms=+183882168921600000 jd=7f000000 December 20, 5828963 AD 1.75 +// ms=-184303902528000000 jd=81000000 September 20, 5838270 BC 1.76 +// HOWEVER, CalendarRegressionTest/Test4167060 shows that the actual 1.77 +// range limit on the year field is smaller (~ +/-140000). [alan 3.0] 1.78 + 1.79 +static const int32_t kGregorianCalendarLimits[UCAL_FIELD_COUNT][4] = { 1.80 + // Minimum Greatest Least Maximum 1.81 + // Minimum Maximum 1.82 + { 0, 0, 1, 1}, // ERA 1.83 + { 1, 1, 140742, 144683}, // YEAR 1.84 + { 0, 0, 11, 11}, // MONTH 1.85 + { 1, 1, 52, 53}, // WEEK_OF_YEAR 1.86 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH 1.87 + { 1, 1, 28, 31}, // DAY_OF_MONTH 1.88 + { 1, 1, 365, 366}, // DAY_OF_YEAR 1.89 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK 1.90 + { -1, -1, 4, 5}, // DAY_OF_WEEK_IN_MONTH 1.91 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM 1.92 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR 1.93 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY 1.94 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE 1.95 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND 1.96 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND 1.97 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET 1.98 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET 1.99 + { -140742, -140742, 140742, 144683}, // YEAR_WOY 1.100 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL 1.101 + { -140742, -140742, 140742, 144683}, // EXTENDED_YEAR 1.102 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY 1.103 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY 1.104 + {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH 1.105 +}; 1.106 + 1.107 +/* 1.108 +* <pre> 1.109 +* Greatest Least 1.110 +* Field name Minimum Minimum Maximum Maximum 1.111 +* ---------- ------- ------- ------- ------- 1.112 +* ERA 0 0 1 1 1.113 +* YEAR 1 1 140742 144683 1.114 +* MONTH 0 0 11 11 1.115 +* WEEK_OF_YEAR 1 1 52 53 1.116 +* WEEK_OF_MONTH 0 0 4 6 1.117 +* DAY_OF_MONTH 1 1 28 31 1.118 +* DAY_OF_YEAR 1 1 365 366 1.119 +* DAY_OF_WEEK 1 1 7 7 1.120 +* DAY_OF_WEEK_IN_MONTH -1 -1 4 5 1.121 +* AM_PM 0 0 1 1 1.122 +* HOUR 0 0 11 11 1.123 +* HOUR_OF_DAY 0 0 23 23 1.124 +* MINUTE 0 0 59 59 1.125 +* SECOND 0 0 59 59 1.126 +* MILLISECOND 0 0 999 999 1.127 +* ZONE_OFFSET -12* -12* 12* 12* 1.128 +* DST_OFFSET 0 0 1* 1* 1.129 +* YEAR_WOY 1 1 140742 144683 1.130 +* DOW_LOCAL 1 1 7 7 1.131 +* </pre> 1.132 +* (*) In units of one-hour 1.133 +*/ 1.134 + 1.135 +#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) 1.136 +#include <stdio.h> 1.137 +#endif 1.138 + 1.139 +U_NAMESPACE_BEGIN 1.140 + 1.141 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(GregorianCalendar) 1.142 + 1.143 +// 00:00:00 UTC, October 15, 1582, expressed in ms from the epoch. 1.144 +// Note that only Italy and other Catholic countries actually 1.145 +// observed this cutover. Most other countries followed in 1.146 +// the next few centuries, some as late as 1928. [LIU] 1.147 +// in Java, -12219292800000L 1.148 +//const UDate GregorianCalendar::kPapalCutover = -12219292800000L; 1.149 +static const uint32_t kCutoverJulianDay = 2299161; 1.150 +static const UDate kPapalCutover = (2299161.0 - kEpochStartAsJulianDay) * U_MILLIS_PER_DAY; 1.151 +//static const UDate kPapalCutoverJulian = (2299161.0 - kEpochStartAsJulianDay); 1.152 + 1.153 +// ------------------------------------- 1.154 + 1.155 +GregorianCalendar::GregorianCalendar(UErrorCode& status) 1.156 +: Calendar(status), 1.157 +fGregorianCutover(kPapalCutover), 1.158 +fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 1.159 +fIsGregorian(TRUE), fInvertGregorian(FALSE) 1.160 +{ 1.161 + setTimeInMillis(getNow(), status); 1.162 +} 1.163 + 1.164 +// ------------------------------------- 1.165 + 1.166 +GregorianCalendar::GregorianCalendar(TimeZone* zone, UErrorCode& status) 1.167 +: Calendar(zone, Locale::getDefault(), status), 1.168 +fGregorianCutover(kPapalCutover), 1.169 +fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 1.170 +fIsGregorian(TRUE), fInvertGregorian(FALSE) 1.171 +{ 1.172 + setTimeInMillis(getNow(), status); 1.173 +} 1.174 + 1.175 +// ------------------------------------- 1.176 + 1.177 +GregorianCalendar::GregorianCalendar(const TimeZone& zone, UErrorCode& status) 1.178 +: Calendar(zone, Locale::getDefault(), status), 1.179 +fGregorianCutover(kPapalCutover), 1.180 +fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 1.181 +fIsGregorian(TRUE), fInvertGregorian(FALSE) 1.182 +{ 1.183 + setTimeInMillis(getNow(), status); 1.184 +} 1.185 + 1.186 +// ------------------------------------- 1.187 + 1.188 +GregorianCalendar::GregorianCalendar(const Locale& aLocale, UErrorCode& status) 1.189 +: Calendar(TimeZone::createDefault(), aLocale, status), 1.190 +fGregorianCutover(kPapalCutover), 1.191 +fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 1.192 +fIsGregorian(TRUE), fInvertGregorian(FALSE) 1.193 +{ 1.194 + setTimeInMillis(getNow(), status); 1.195 +} 1.196 + 1.197 +// ------------------------------------- 1.198 + 1.199 +GregorianCalendar::GregorianCalendar(TimeZone* zone, const Locale& aLocale, 1.200 + UErrorCode& status) 1.201 + : Calendar(zone, aLocale, status), 1.202 + fGregorianCutover(kPapalCutover), 1.203 + fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 1.204 + fIsGregorian(TRUE), fInvertGregorian(FALSE) 1.205 +{ 1.206 + setTimeInMillis(getNow(), status); 1.207 +} 1.208 + 1.209 +// ------------------------------------- 1.210 + 1.211 +GregorianCalendar::GregorianCalendar(const TimeZone& zone, const Locale& aLocale, 1.212 + UErrorCode& status) 1.213 + : Calendar(zone, aLocale, status), 1.214 + fGregorianCutover(kPapalCutover), 1.215 + fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 1.216 + fIsGregorian(TRUE), fInvertGregorian(FALSE) 1.217 +{ 1.218 + setTimeInMillis(getNow(), status); 1.219 +} 1.220 + 1.221 +// ------------------------------------- 1.222 + 1.223 +GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date, 1.224 + UErrorCode& status) 1.225 + : Calendar(TimeZone::createDefault(), Locale::getDefault(), status), 1.226 + fGregorianCutover(kPapalCutover), 1.227 + fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 1.228 + fIsGregorian(TRUE), fInvertGregorian(FALSE) 1.229 +{ 1.230 + set(UCAL_ERA, AD); 1.231 + set(UCAL_YEAR, year); 1.232 + set(UCAL_MONTH, month); 1.233 + set(UCAL_DATE, date); 1.234 +} 1.235 + 1.236 +// ------------------------------------- 1.237 + 1.238 +GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date, 1.239 + int32_t hour, int32_t minute, UErrorCode& status) 1.240 + : Calendar(TimeZone::createDefault(), Locale::getDefault(), status), 1.241 + fGregorianCutover(kPapalCutover), 1.242 + fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 1.243 + fIsGregorian(TRUE), fInvertGregorian(FALSE) 1.244 +{ 1.245 + set(UCAL_ERA, AD); 1.246 + set(UCAL_YEAR, year); 1.247 + set(UCAL_MONTH, month); 1.248 + set(UCAL_DATE, date); 1.249 + set(UCAL_HOUR_OF_DAY, hour); 1.250 + set(UCAL_MINUTE, minute); 1.251 +} 1.252 + 1.253 +// ------------------------------------- 1.254 + 1.255 +GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date, 1.256 + int32_t hour, int32_t minute, int32_t second, 1.257 + UErrorCode& status) 1.258 + : Calendar(TimeZone::createDefault(), Locale::getDefault(), status), 1.259 + fGregorianCutover(kPapalCutover), 1.260 + fCutoverJulianDay(kCutoverJulianDay), fNormalizedGregorianCutover(fGregorianCutover), fGregorianCutoverYear(1582), 1.261 + fIsGregorian(TRUE), fInvertGregorian(FALSE) 1.262 +{ 1.263 + set(UCAL_ERA, AD); 1.264 + set(UCAL_YEAR, year); 1.265 + set(UCAL_MONTH, month); 1.266 + set(UCAL_DATE, date); 1.267 + set(UCAL_HOUR_OF_DAY, hour); 1.268 + set(UCAL_MINUTE, minute); 1.269 + set(UCAL_SECOND, second); 1.270 +} 1.271 + 1.272 +// ------------------------------------- 1.273 + 1.274 +GregorianCalendar::~GregorianCalendar() 1.275 +{ 1.276 +} 1.277 + 1.278 +// ------------------------------------- 1.279 + 1.280 +GregorianCalendar::GregorianCalendar(const GregorianCalendar &source) 1.281 +: Calendar(source), 1.282 +fGregorianCutover(source.fGregorianCutover), 1.283 +fCutoverJulianDay(source.fCutoverJulianDay), fNormalizedGregorianCutover(source.fNormalizedGregorianCutover), fGregorianCutoverYear(source.fGregorianCutoverYear), 1.284 +fIsGregorian(source.fIsGregorian), fInvertGregorian(source.fInvertGregorian) 1.285 +{ 1.286 +} 1.287 + 1.288 +// ------------------------------------- 1.289 + 1.290 +Calendar* GregorianCalendar::clone() const 1.291 +{ 1.292 + return new GregorianCalendar(*this); 1.293 +} 1.294 + 1.295 +// ------------------------------------- 1.296 + 1.297 +GregorianCalendar & 1.298 +GregorianCalendar::operator=(const GregorianCalendar &right) 1.299 +{ 1.300 + if (this != &right) 1.301 + { 1.302 + Calendar::operator=(right); 1.303 + fGregorianCutover = right.fGregorianCutover; 1.304 + fNormalizedGregorianCutover = right.fNormalizedGregorianCutover; 1.305 + fGregorianCutoverYear = right.fGregorianCutoverYear; 1.306 + fCutoverJulianDay = right.fCutoverJulianDay; 1.307 + } 1.308 + return *this; 1.309 +} 1.310 + 1.311 +// ------------------------------------- 1.312 + 1.313 +UBool GregorianCalendar::isEquivalentTo(const Calendar& other) const 1.314 +{ 1.315 + // Calendar override. 1.316 + return Calendar::isEquivalentTo(other) && 1.317 + fGregorianCutover == ((GregorianCalendar*)&other)->fGregorianCutover; 1.318 +} 1.319 + 1.320 +// ------------------------------------- 1.321 + 1.322 +void 1.323 +GregorianCalendar::setGregorianChange(UDate date, UErrorCode& status) 1.324 +{ 1.325 + if (U_FAILURE(status)) 1.326 + return; 1.327 + 1.328 + fGregorianCutover = date; 1.329 + 1.330 + // Precompute two internal variables which we use to do the actual 1.331 + // cutover computations. These are the normalized cutover, which is the 1.332 + // midnight at or before the cutover, and the cutover year. The 1.333 + // normalized cutover is in pure date milliseconds; it contains no time 1.334 + // of day or timezone component, and it used to compare against other 1.335 + // pure date values. 1.336 + int32_t cutoverDay = (int32_t)ClockMath::floorDivide(fGregorianCutover, (double)kOneDay); 1.337 + fNormalizedGregorianCutover = cutoverDay * kOneDay; 1.338 + 1.339 + // Handle the rare case of numeric overflow. If the user specifies a 1.340 + // change of UDate(Long.MIN_VALUE), in order to get a pure Gregorian 1.341 + // calendar, then the epoch day is -106751991168, which when multiplied 1.342 + // by ONE_DAY gives 9223372036794351616 -- the negative value is too 1.343 + // large for 64 bits, and overflows into a positive value. We correct 1.344 + // this by using the next day, which for all intents is semantically 1.345 + // equivalent. 1.346 + if (cutoverDay < 0 && fNormalizedGregorianCutover > 0) { 1.347 + fNormalizedGregorianCutover = (cutoverDay + 1) * kOneDay; 1.348 + } 1.349 + 1.350 + // Normalize the year so BC values are represented as 0 and negative 1.351 + // values. 1.352 + GregorianCalendar *cal = new GregorianCalendar(getTimeZone(), status); 1.353 + /* test for NULL */ 1.354 + if (cal == 0) { 1.355 + status = U_MEMORY_ALLOCATION_ERROR; 1.356 + return; 1.357 + } 1.358 + if(U_FAILURE(status)) 1.359 + return; 1.360 + cal->setTime(date, status); 1.361 + fGregorianCutoverYear = cal->get(UCAL_YEAR, status); 1.362 + if (cal->get(UCAL_ERA, status) == BC) 1.363 + fGregorianCutoverYear = 1 - fGregorianCutoverYear; 1.364 + fCutoverJulianDay = cutoverDay; 1.365 + delete cal; 1.366 +} 1.367 + 1.368 + 1.369 +void GregorianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) { 1.370 + int32_t eyear, month, dayOfMonth, dayOfYear, unusedRemainder; 1.371 + 1.372 + 1.373 + if(U_FAILURE(status)) { 1.374 + return; 1.375 + } 1.376 + 1.377 +#if defined (U_DEBUG_CAL) 1.378 + fprintf(stderr, "%s:%d: jd%d- (greg's %d)- [cut=%d]\n", 1.379 + __FILE__, __LINE__, julianDay, getGregorianDayOfYear(), fCutoverJulianDay); 1.380 +#endif 1.381 + 1.382 + 1.383 + if (julianDay >= fCutoverJulianDay) { 1.384 + month = getGregorianMonth(); 1.385 + dayOfMonth = getGregorianDayOfMonth(); 1.386 + dayOfYear = getGregorianDayOfYear(); 1.387 + eyear = getGregorianYear(); 1.388 + } else { 1.389 + // The Julian epoch day (not the same as Julian Day) 1.390 + // is zero on Saturday December 30, 0 (Gregorian). 1.391 + int32_t julianEpochDay = julianDay - (kJan1_1JulianDay - 2); 1.392 + eyear = (int32_t) ClockMath::floorDivide((4.0*julianEpochDay) + 1464.0, (int32_t) 1461, unusedRemainder); 1.393 + 1.394 + // Compute the Julian calendar day number for January 1, eyear 1.395 + int32_t january1 = 365*(eyear-1) + ClockMath::floorDivide(eyear-1, (int32_t)4); 1.396 + dayOfYear = (julianEpochDay - january1); // 0-based 1.397 + 1.398 + // Julian leap years occurred historically every 4 years starting 1.399 + // with 8 AD. Before 8 AD the spacing is irregular; every 3 years 1.400 + // from 45 BC to 9 BC, and then none until 8 AD. However, we don't 1.401 + // implement this historical detail; instead, we implement the 1.402 + // computatinally cleaner proleptic calendar, which assumes 1.403 + // consistent 4-year cycles throughout time. 1.404 + UBool isLeap = ((eyear&0x3) == 0); // equiv. to (eyear%4 == 0) 1.405 + 1.406 + // Common Julian/Gregorian calculation 1.407 + int32_t correction = 0; 1.408 + int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1 1.409 + if (dayOfYear >= march1) { 1.410 + correction = isLeap ? 1 : 2; 1.411 + } 1.412 + month = (12 * (dayOfYear + correction) + 6) / 367; // zero-based month 1.413 + dayOfMonth = dayOfYear - (isLeap?kLeapNumDays[month]:kNumDays[month]) + 1; // one-based DOM 1.414 + ++dayOfYear; 1.415 +#if defined (U_DEBUG_CAL) 1.416 + // fprintf(stderr, "%d - %d[%d] + 1\n", dayOfYear, isLeap?kLeapNumDays[month]:kNumDays[month], month ); 1.417 + // fprintf(stderr, "%s:%d: greg's HCF %d -> %d/%d/%d not %d/%d/%d\n", 1.418 + // __FILE__, __LINE__,julianDay, 1.419 + // eyear,month,dayOfMonth, 1.420 + // getGregorianYear(), getGregorianMonth(), getGregorianDayOfMonth() ); 1.421 + fprintf(stderr, "%s:%d: doy %d (greg's %d)- [cut=%d]\n", 1.422 + __FILE__, __LINE__, dayOfYear, getGregorianDayOfYear(), fCutoverJulianDay); 1.423 +#endif 1.424 + 1.425 + } 1.426 + 1.427 + // [j81] if we are after the cutover in its year, shift the day of the year 1.428 + if((eyear == fGregorianCutoverYear) && (julianDay >= fCutoverJulianDay)) { 1.429 + //from handleComputeMonthStart 1.430 + int32_t gregShift = Grego::gregorianShift(eyear); 1.431 +#if defined (U_DEBUG_CAL) 1.432 + fprintf(stderr, "%s:%d: gregorian shift %d ::: doy%d => %d [cut=%d]\n", 1.433 + __FILE__, __LINE__,gregShift, dayOfYear, dayOfYear+gregShift, fCutoverJulianDay); 1.434 +#endif 1.435 + dayOfYear += gregShift; 1.436 + } 1.437 + 1.438 + internalSet(UCAL_MONTH, month); 1.439 + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); 1.440 + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); 1.441 + internalSet(UCAL_EXTENDED_YEAR, eyear); 1.442 + int32_t era = AD; 1.443 + if (eyear < 1) { 1.444 + era = BC; 1.445 + eyear = 1 - eyear; 1.446 + } 1.447 + internalSet(UCAL_ERA, era); 1.448 + internalSet(UCAL_YEAR, eyear); 1.449 +} 1.450 + 1.451 + 1.452 +// ------------------------------------- 1.453 + 1.454 +UDate 1.455 +GregorianCalendar::getGregorianChange() const 1.456 +{ 1.457 + return fGregorianCutover; 1.458 +} 1.459 + 1.460 +// ------------------------------------- 1.461 + 1.462 +UBool 1.463 +GregorianCalendar::isLeapYear(int32_t year) const 1.464 +{ 1.465 + // MSVC complains bitterly if we try to use Grego::isLeapYear here 1.466 + // NOTE: year&0x3 == year%4 1.467 + return (year >= fGregorianCutoverYear ? 1.468 + (((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0))) : // Gregorian 1.469 + ((year&0x3) == 0)); // Julian 1.470 +} 1.471 + 1.472 +// ------------------------------------- 1.473 + 1.474 +int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField) 1.475 +{ 1.476 + fInvertGregorian = FALSE; 1.477 + 1.478 + int32_t jd = Calendar::handleComputeJulianDay(bestField); 1.479 + 1.480 + if((bestField == UCAL_WEEK_OF_YEAR) && // if we are doing WOY calculations, we are counting relative to Jan 1 *julian* 1.481 + (internalGet(UCAL_EXTENDED_YEAR)==fGregorianCutoverYear) && 1.482 + jd >= fCutoverJulianDay) { 1.483 + fInvertGregorian = TRUE; // So that the Julian Jan 1 will be used in handleComputeMonthStart 1.484 + return Calendar::handleComputeJulianDay(bestField); 1.485 + } 1.486 + 1.487 + 1.488 + // The following check handles portions of the cutover year BEFORE the 1.489 + // cutover itself happens. 1.490 + //if ((fIsGregorian==TRUE) != (jd >= fCutoverJulianDay)) { /* cutoverJulianDay)) { */ 1.491 + if ((fIsGregorian==TRUE) != (jd >= fCutoverJulianDay)) { /* cutoverJulianDay)) { */ 1.492 +#if defined (U_DEBUG_CAL) 1.493 + fprintf(stderr, "%s:%d: jd [invert] %d\n", 1.494 + __FILE__, __LINE__, jd); 1.495 +#endif 1.496 + fInvertGregorian = TRUE; 1.497 + jd = Calendar::handleComputeJulianDay(bestField); 1.498 +#if defined (U_DEBUG_CAL) 1.499 + fprintf(stderr, "%s:%d: fIsGregorian %s, fInvertGregorian %s - ", 1.500 + __FILE__, __LINE__,fIsGregorian?"T":"F", fInvertGregorian?"T":"F"); 1.501 + fprintf(stderr, " jd NOW %d\n", 1.502 + jd); 1.503 +#endif 1.504 + } else { 1.505 +#if defined (U_DEBUG_CAL) 1.506 + fprintf(stderr, "%s:%d: jd [==] %d - %sfIsGregorian %sfInvertGregorian, %d\n", 1.507 + __FILE__, __LINE__, jd, fIsGregorian?"T":"F", fInvertGregorian?"T":"F", bestField); 1.508 +#endif 1.509 + } 1.510 + 1.511 + if(fIsGregorian && (internalGet(UCAL_EXTENDED_YEAR) == fGregorianCutoverYear)) { 1.512 + int32_t gregShift = Grego::gregorianShift(internalGet(UCAL_EXTENDED_YEAR)); 1.513 + if (bestField == UCAL_DAY_OF_YEAR) { 1.514 +#if defined (U_DEBUG_CAL) 1.515 + fprintf(stderr, "%s:%d: [DOY%d] gregorian shift of JD %d += %d\n", 1.516 + __FILE__, __LINE__, fFields[bestField],jd, gregShift); 1.517 +#endif 1.518 + jd -= gregShift; 1.519 + } else if ( bestField == UCAL_WEEK_OF_MONTH ) { 1.520 + int32_t weekShift = 14; 1.521 +#if defined (U_DEBUG_CAL) 1.522 + fprintf(stderr, "%s:%d: [WOY/WOM] gregorian week shift of %d += %d\n", 1.523 + __FILE__, __LINE__, jd, weekShift); 1.524 +#endif 1.525 + jd += weekShift; // shift by weeks for week based fields. 1.526 + } 1.527 + } 1.528 + 1.529 + return jd; 1.530 +} 1.531 + 1.532 +int32_t GregorianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, 1.533 + 1.534 + UBool /* useMonth */) const 1.535 +{ 1.536 + GregorianCalendar *nonConstThis = (GregorianCalendar*)this; // cast away const 1.537 + 1.538 + // If the month is out of range, adjust it into range, and 1.539 + // modify the extended year value accordingly. 1.540 + if (month < 0 || month > 11) { 1.541 + eyear += ClockMath::floorDivide(month, 12, month); 1.542 + } 1.543 + 1.544 + UBool isLeap = eyear%4 == 0; 1.545 + int32_t y = eyear-1; 1.546 + int32_t julianDay = 365*y + ClockMath::floorDivide(y, 4) + (kJan1_1JulianDay - 3); 1.547 + 1.548 + nonConstThis->fIsGregorian = (eyear >= fGregorianCutoverYear); 1.549 +#if defined (U_DEBUG_CAL) 1.550 + fprintf(stderr, "%s:%d: (hcms%d/%d) fIsGregorian %s, fInvertGregorian %s\n", 1.551 + __FILE__, __LINE__, eyear,month, fIsGregorian?"T":"F", fInvertGregorian?"T":"F"); 1.552 +#endif 1.553 + if (fInvertGregorian) { 1.554 + nonConstThis->fIsGregorian = !fIsGregorian; 1.555 + } 1.556 + if (fIsGregorian) { 1.557 + isLeap = isLeap && ((eyear%100 != 0) || (eyear%400 == 0)); 1.558 + // Add 2 because Gregorian calendar starts 2 days after 1.559 + // Julian calendar 1.560 + int32_t gregShift = Grego::gregorianShift(eyear); 1.561 +#if defined (U_DEBUG_CAL) 1.562 + fprintf(stderr, "%s:%d: (hcms%d/%d) gregorian shift of %d += %d\n", 1.563 + __FILE__, __LINE__, eyear, month, julianDay, gregShift); 1.564 +#endif 1.565 + julianDay += gregShift; 1.566 + } 1.567 + 1.568 + // At this point julianDay indicates the day BEFORE the first 1.569 + // day of January 1, <eyear> of either the Julian or Gregorian 1.570 + // calendar. 1.571 + 1.572 + if (month != 0) { 1.573 + julianDay += isLeap?kLeapNumDays[month]:kNumDays[month]; 1.574 + } 1.575 + 1.576 + return julianDay; 1.577 +} 1.578 + 1.579 +int32_t GregorianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const 1.580 +{ 1.581 + // If the month is out of range, adjust it into range, and 1.582 + // modify the extended year value accordingly. 1.583 + if (month < 0 || month > 11) { 1.584 + extendedYear += ClockMath::floorDivide(month, 12, month); 1.585 + } 1.586 + 1.587 + return isLeapYear(extendedYear) ? kLeapMonthLength[month] : kMonthLength[month]; 1.588 +} 1.589 + 1.590 +int32_t GregorianCalendar::handleGetYearLength(int32_t eyear) const { 1.591 + return isLeapYear(eyear) ? 366 : 365; 1.592 +} 1.593 + 1.594 + 1.595 +int32_t 1.596 +GregorianCalendar::monthLength(int32_t month) const 1.597 +{ 1.598 + int32_t year = internalGet(UCAL_EXTENDED_YEAR); 1.599 + return handleGetMonthLength(year, month); 1.600 +} 1.601 + 1.602 +// ------------------------------------- 1.603 + 1.604 +int32_t 1.605 +GregorianCalendar::monthLength(int32_t month, int32_t year) const 1.606 +{ 1.607 + return isLeapYear(year) ? kLeapMonthLength[month] : kMonthLength[month]; 1.608 +} 1.609 + 1.610 +// ------------------------------------- 1.611 + 1.612 +int32_t 1.613 +GregorianCalendar::yearLength(int32_t year) const 1.614 +{ 1.615 + return isLeapYear(year) ? 366 : 365; 1.616 +} 1.617 + 1.618 +// ------------------------------------- 1.619 + 1.620 +int32_t 1.621 +GregorianCalendar::yearLength() const 1.622 +{ 1.623 + return isLeapYear(internalGet(UCAL_YEAR)) ? 366 : 365; 1.624 +} 1.625 + 1.626 +// ------------------------------------- 1.627 + 1.628 +/** 1.629 +* After adjustments such as add(MONTH), add(YEAR), we don't want the 1.630 +* month to jump around. E.g., we don't want Jan 31 + 1 month to go to Mar 1.631 +* 3, we want it to go to Feb 28. Adjustments which might run into this 1.632 +* problem call this method to retain the proper month. 1.633 +*/ 1.634 +void 1.635 +GregorianCalendar::pinDayOfMonth() 1.636 +{ 1.637 + int32_t monthLen = monthLength(internalGet(UCAL_MONTH)); 1.638 + int32_t dom = internalGet(UCAL_DATE); 1.639 + if(dom > monthLen) 1.640 + set(UCAL_DATE, monthLen); 1.641 +} 1.642 + 1.643 +// ------------------------------------- 1.644 + 1.645 + 1.646 +UBool 1.647 +GregorianCalendar::validateFields() const 1.648 +{ 1.649 + for (int32_t field = 0; field < UCAL_FIELD_COUNT; field++) { 1.650 + // Ignore DATE and DAY_OF_YEAR which are handled below 1.651 + if (field != UCAL_DATE && 1.652 + field != UCAL_DAY_OF_YEAR && 1.653 + isSet((UCalendarDateFields)field) && 1.654 + ! boundsCheck(internalGet((UCalendarDateFields)field), (UCalendarDateFields)field)) 1.655 + return FALSE; 1.656 + } 1.657 + 1.658 + // Values differ in Least-Maximum and Maximum should be handled 1.659 + // specially. 1.660 + if (isSet(UCAL_DATE)) { 1.661 + int32_t date = internalGet(UCAL_DATE); 1.662 + if (date < getMinimum(UCAL_DATE) || 1.663 + date > monthLength(internalGet(UCAL_MONTH))) { 1.664 + return FALSE; 1.665 + } 1.666 + } 1.667 + 1.668 + if (isSet(UCAL_DAY_OF_YEAR)) { 1.669 + int32_t days = internalGet(UCAL_DAY_OF_YEAR); 1.670 + if (days < 1 || days > yearLength()) { 1.671 + return FALSE; 1.672 + } 1.673 + } 1.674 + 1.675 + // Handle DAY_OF_WEEK_IN_MONTH, which must not have the value zero. 1.676 + // We've checked against minimum and maximum above already. 1.677 + if (isSet(UCAL_DAY_OF_WEEK_IN_MONTH) && 1.678 + 0 == internalGet(UCAL_DAY_OF_WEEK_IN_MONTH)) { 1.679 + return FALSE; 1.680 + } 1.681 + 1.682 + return TRUE; 1.683 +} 1.684 + 1.685 +// ------------------------------------- 1.686 + 1.687 +UBool 1.688 +GregorianCalendar::boundsCheck(int32_t value, UCalendarDateFields field) const 1.689 +{ 1.690 + return value >= getMinimum(field) && value <= getMaximum(field); 1.691 +} 1.692 + 1.693 +// ------------------------------------- 1.694 + 1.695 +UDate 1.696 +GregorianCalendar::getEpochDay(UErrorCode& status) 1.697 +{ 1.698 + complete(status); 1.699 + // Divide by 1000 (convert to seconds) in order to prevent overflow when 1.700 + // dealing with UDate(Long.MIN_VALUE) and UDate(Long.MAX_VALUE). 1.701 + double wallSec = internalGetTime()/1000 + (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET))/1000; 1.702 + 1.703 + return ClockMath::floorDivide(wallSec, kOneDay/1000.0); 1.704 +} 1.705 + 1.706 +// ------------------------------------- 1.707 + 1.708 + 1.709 +// ------------------------------------- 1.710 + 1.711 +/** 1.712 +* Compute the julian day number of the day BEFORE the first day of 1.713 +* January 1, year 1 of the given calendar. If julianDay == 0, it 1.714 +* specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian 1.715 +* or Gregorian). 1.716 +*/ 1.717 +double GregorianCalendar::computeJulianDayOfYear(UBool isGregorian, 1.718 + int32_t year, UBool& isLeap) 1.719 +{ 1.720 + isLeap = year%4 == 0; 1.721 + int32_t y = year - 1; 1.722 + double julianDay = 365.0*y + ClockMath::floorDivide(y, 4) + (kJan1_1JulianDay - 3); 1.723 + 1.724 + if (isGregorian) { 1.725 + isLeap = isLeap && ((year%100 != 0) || (year%400 == 0)); 1.726 + // Add 2 because Gregorian calendar starts 2 days after Julian calendar 1.727 + julianDay += Grego::gregorianShift(year); 1.728 + } 1.729 + 1.730 + return julianDay; 1.731 +} 1.732 + 1.733 +// /** 1.734 +// * Compute the day of week, relative to the first day of week, from 1.735 +// * 0..6, of the current DOW_LOCAL or DAY_OF_WEEK fields. This is 1.736 +// * equivalent to get(DOW_LOCAL) - 1. 1.737 +// */ 1.738 +// int32_t GregorianCalendar::computeRelativeDOW() const { 1.739 +// int32_t relDow = 0; 1.740 +// if (fStamp[UCAL_DOW_LOCAL] > fStamp[UCAL_DAY_OF_WEEK]) { 1.741 +// relDow = internalGet(UCAL_DOW_LOCAL) - 1; // 1-based 1.742 +// } else if (fStamp[UCAL_DAY_OF_WEEK] != kUnset) { 1.743 +// relDow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); 1.744 +// if (relDow < 0) relDow += 7; 1.745 +// } 1.746 +// return relDow; 1.747 +// } 1.748 + 1.749 +// /** 1.750 +// * Compute the day of week, relative to the first day of week, 1.751 +// * from 0..6 of the given julian day. 1.752 +// */ 1.753 +// int32_t GregorianCalendar::computeRelativeDOW(double julianDay) const { 1.754 +// int32_t relDow = julianDayToDayOfWeek(julianDay) - getFirstDayOfWeek(); 1.755 +// if (relDow < 0) { 1.756 +// relDow += 7; 1.757 +// } 1.758 +// return relDow; 1.759 +// } 1.760 + 1.761 +// /** 1.762 +// * Compute the DOY using the WEEK_OF_YEAR field and the julian day 1.763 +// * of the day BEFORE January 1 of a year (a return value from 1.764 +// * computeJulianDayOfYear). 1.765 +// */ 1.766 +// int32_t GregorianCalendar::computeDOYfromWOY(double julianDayOfYear) const { 1.767 +// // Compute DOY from day of week plus week of year 1.768 + 1.769 +// // Find the day of the week for the first of this year. This 1.770 +// // is zero-based, with 0 being the locale-specific first day of 1.771 +// // the week. Add 1 to get first day of year. 1.772 +// int32_t fdy = computeRelativeDOW(julianDayOfYear + 1); 1.773 + 1.774 +// return 1.775 +// // Compute doy of first (relative) DOW of WOY 1 1.776 +// (((7 - fdy) < getMinimalDaysInFirstWeek()) 1.777 +// ? (8 - fdy) : (1 - fdy)) 1.778 + 1.779 +// // Adjust for the week number. 1.780 +// + (7 * (internalGet(UCAL_WEEK_OF_YEAR) - 1)) 1.781 + 1.782 +// // Adjust for the DOW 1.783 +// + computeRelativeDOW(); 1.784 +// } 1.785 + 1.786 +// ------------------------------------- 1.787 + 1.788 +double 1.789 +GregorianCalendar::millisToJulianDay(UDate millis) 1.790 +{ 1.791 + return (double)kEpochStartAsJulianDay + ClockMath::floorDivide(millis, (double)kOneDay); 1.792 +} 1.793 + 1.794 +// ------------------------------------- 1.795 + 1.796 +UDate 1.797 +GregorianCalendar::julianDayToMillis(double julian) 1.798 +{ 1.799 + return (UDate) ((julian - kEpochStartAsJulianDay) * (double) kOneDay); 1.800 +} 1.801 + 1.802 +// ------------------------------------- 1.803 + 1.804 +int32_t 1.805 +GregorianCalendar::aggregateStamp(int32_t stamp_a, int32_t stamp_b) 1.806 +{ 1.807 + return (((stamp_a != kUnset && stamp_b != kUnset) 1.808 + ? uprv_max(stamp_a, stamp_b) 1.809 + : (int32_t)kUnset)); 1.810 +} 1.811 + 1.812 +// ------------------------------------- 1.813 + 1.814 +/** 1.815 +* Roll a field by a signed amount. 1.816 +* Note: This will be made public later. [LIU] 1.817 +*/ 1.818 + 1.819 +void 1.820 +GregorianCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { 1.821 + roll((UCalendarDateFields) field, amount, status); 1.822 +} 1.823 + 1.824 +void 1.825 +GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) 1.826 +{ 1.827 + if((amount == 0) || U_FAILURE(status)) { 1.828 + return; 1.829 + } 1.830 + 1.831 + // J81 processing. (gregorian cutover) 1.832 + UBool inCutoverMonth = FALSE; 1.833 + int32_t cMonthLen=0; // 'c' for cutover; in days 1.834 + int32_t cDayOfMonth=0; // no discontinuity: [0, cMonthLen) 1.835 + double cMonthStart=0.0; // in ms 1.836 + 1.837 + // Common code - see if we're in the cutover month of the cutover year 1.838 + if(get(UCAL_EXTENDED_YEAR, status) == fGregorianCutoverYear) { 1.839 + switch (field) { 1.840 + case UCAL_DAY_OF_MONTH: 1.841 + case UCAL_WEEK_OF_MONTH: 1.842 + { 1.843 + int32_t max = monthLength(internalGet(UCAL_MONTH)); 1.844 + UDate t = internalGetTime(); 1.845 + // We subtract 1 from the DAY_OF_MONTH to make it zero-based, and an 1.846 + // additional 10 if we are after the cutover. Thus the monthStart 1.847 + // value will be correct iff we actually are in the cutover month. 1.848 + cDayOfMonth = internalGet(UCAL_DAY_OF_MONTH) - ((t >= fGregorianCutover) ? 10 : 0); 1.849 + cMonthStart = t - ((cDayOfMonth - 1) * kOneDay); 1.850 + // A month containing the cutover is 10 days shorter. 1.851 + if ((cMonthStart < fGregorianCutover) && 1.852 + (cMonthStart + (cMonthLen=(max-10))*kOneDay >= fGregorianCutover)) { 1.853 + inCutoverMonth = TRUE; 1.854 + } 1.855 + } 1.856 + default: 1.857 + ; 1.858 + } 1.859 + } 1.860 + 1.861 + switch (field) { 1.862 + case UCAL_WEEK_OF_YEAR: { 1.863 + // Unlike WEEK_OF_MONTH, WEEK_OF_YEAR never shifts the day of the 1.864 + // week. Also, rolling the week of the year can have seemingly 1.865 + // strange effects simply because the year of the week of year 1.866 + // may be different from the calendar year. For example, the 1.867 + // date Dec 28, 1997 is the first day of week 1 of 1998 (if 1.868 + // weeks start on Sunday and the minimal days in first week is 1.869 + // <= 3). 1.870 + int32_t woy = get(UCAL_WEEK_OF_YEAR, status); 1.871 + // Get the ISO year, which matches the week of year. This 1.872 + // may be one year before or after the calendar year. 1.873 + int32_t isoYear = get(UCAL_YEAR_WOY, status); 1.874 + int32_t isoDoy = internalGet(UCAL_DAY_OF_YEAR); 1.875 + if (internalGet(UCAL_MONTH) == UCAL_JANUARY) { 1.876 + if (woy >= 52) { 1.877 + isoDoy += handleGetYearLength(isoYear); 1.878 + } 1.879 + } else { 1.880 + if (woy == 1) { 1.881 + isoDoy -= handleGetYearLength(isoYear - 1); 1.882 + } 1.883 + } 1.884 + woy += amount; 1.885 + // Do fast checks to avoid unnecessary computation: 1.886 + if (woy < 1 || woy > 52) { 1.887 + // Determine the last week of the ISO year. 1.888 + // We do this using the standard formula we use 1.889 + // everywhere in this file. If we can see that the 1.890 + // days at the end of the year are going to fall into 1.891 + // week 1 of the next year, we drop the last week by 1.892 + // subtracting 7 from the last day of the year. 1.893 + int32_t lastDoy = handleGetYearLength(isoYear); 1.894 + int32_t lastRelDow = (lastDoy - isoDoy + internalGet(UCAL_DAY_OF_WEEK) - 1.895 + getFirstDayOfWeek()) % 7; 1.896 + if (lastRelDow < 0) lastRelDow += 7; 1.897 + if ((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) lastDoy -= 7; 1.898 + int32_t lastWoy = weekNumber(lastDoy, lastRelDow + 1); 1.899 + woy = ((woy + lastWoy - 1) % lastWoy) + 1; 1.900 + } 1.901 + set(UCAL_WEEK_OF_YEAR, woy); 1.902 + set(UCAL_YEAR_WOY,isoYear); 1.903 + return; 1.904 + } 1.905 + 1.906 + case UCAL_DAY_OF_MONTH: 1.907 + if( !inCutoverMonth ) { 1.908 + Calendar::roll(field, amount, status); 1.909 + return; 1.910 + } else { 1.911 + // [j81] 1582 special case for DOM 1.912 + // The default computation works except when the current month 1.913 + // contains the Gregorian cutover. We handle this special case 1.914 + // here. [j81 - aliu] 1.915 + double monthLen = cMonthLen * kOneDay; 1.916 + double msIntoMonth = uprv_fmod(internalGetTime() - cMonthStart + 1.917 + amount * kOneDay, monthLen); 1.918 + if (msIntoMonth < 0) { 1.919 + msIntoMonth += monthLen; 1.920 + } 1.921 +#if defined (U_DEBUG_CAL) 1.922 + fprintf(stderr, "%s:%d: roll DOM %d -> %.0lf ms \n", 1.923 + __FILE__, __LINE__,amount, cMonthLen, cMonthStart+msIntoMonth); 1.924 +#endif 1.925 + setTimeInMillis(cMonthStart + msIntoMonth, status); 1.926 + return; 1.927 + } 1.928 + 1.929 + case UCAL_WEEK_OF_MONTH: 1.930 + if( !inCutoverMonth ) { 1.931 + Calendar::roll(field, amount, status); 1.932 + return; 1.933 + } else { 1.934 +#if defined (U_DEBUG_CAL) 1.935 + fprintf(stderr, "%s:%d: roll WOM %d ??????????????????? \n", 1.936 + __FILE__, __LINE__,amount); 1.937 +#endif 1.938 + // NOTE: following copied from the old 1.939 + // GregorianCalendar::roll( WEEK_OF_MONTH ) code 1.940 + 1.941 + // This is tricky, because during the roll we may have to shift 1.942 + // to a different day of the week. For example: 1.943 + 1.944 + // s m t w r f s 1.945 + // 1 2 3 4 5 1.946 + // 6 7 8 9 10 11 12 1.947 + 1.948 + // When rolling from the 6th or 7th back one week, we go to the 1.949 + // 1st (assuming that the first partial week counts). The same 1.950 + // thing happens at the end of the month. 1.951 + 1.952 + // The other tricky thing is that we have to figure out whether 1.953 + // the first partial week actually counts or not, based on the 1.954 + // minimal first days in the week. And we have to use the 1.955 + // correct first day of the week to delineate the week 1.956 + // boundaries. 1.957 + 1.958 + // Here's our algorithm. First, we find the real boundaries of 1.959 + // the month. Then we discard the first partial week if it 1.960 + // doesn't count in this locale. Then we fill in the ends with 1.961 + // phantom days, so that the first partial week and the last 1.962 + // partial week are full weeks. We then have a nice square 1.963 + // block of weeks. We do the usual rolling within this block, 1.964 + // as is done elsewhere in this method. If we wind up on one of 1.965 + // the phantom days that we added, we recognize this and pin to 1.966 + // the first or the last day of the month. Easy, eh? 1.967 + 1.968 + // Another wrinkle: To fix jitterbug 81, we have to make all this 1.969 + // work in the oddball month containing the Gregorian cutover. 1.970 + // This month is 10 days shorter than usual, and also contains 1.971 + // a discontinuity in the days; e.g., the default cutover month 1.972 + // is Oct 1582, and goes from day of month 4 to day of month 15. 1.973 + 1.974 + // Normalize the DAY_OF_WEEK so that 0 is the first day of the week 1.975 + // in this locale. We have dow in 0..6. 1.976 + int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek(); 1.977 + if (dow < 0) 1.978 + dow += 7; 1.979 + 1.980 + // Find the day of month, compensating for cutover discontinuity. 1.981 + int32_t dom = cDayOfMonth; 1.982 + 1.983 + // Find the day of the week (normalized for locale) for the first 1.984 + // of the month. 1.985 + int32_t fdm = (dow - dom + 1) % 7; 1.986 + if (fdm < 0) 1.987 + fdm += 7; 1.988 + 1.989 + // Get the first day of the first full week of the month, 1.990 + // including phantom days, if any. Figure out if the first week 1.991 + // counts or not; if it counts, then fill in phantom days. If 1.992 + // not, advance to the first real full week (skip the partial week). 1.993 + int32_t start; 1.994 + if ((7 - fdm) < getMinimalDaysInFirstWeek()) 1.995 + start = 8 - fdm; // Skip the first partial week 1.996 + else 1.997 + start = 1 - fdm; // This may be zero or negative 1.998 + 1.999 + // Get the day of the week (normalized for locale) for the last 1.1000 + // day of the month. 1.1001 + int32_t monthLen = cMonthLen; 1.1002 + int32_t ldm = (monthLen - dom + dow) % 7; 1.1003 + // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here. 1.1004 + 1.1005 + // Get the limit day for the blocked-off rectangular month; that 1.1006 + // is, the day which is one past the last day of the month, 1.1007 + // after the month has already been filled in with phantom days 1.1008 + // to fill out the last week. This day has a normalized DOW of 0. 1.1009 + int32_t limit = monthLen + 7 - ldm; 1.1010 + 1.1011 + // Now roll between start and (limit - 1). 1.1012 + int32_t gap = limit - start; 1.1013 + int32_t newDom = (dom + amount*7 - start) % gap; 1.1014 + if (newDom < 0) 1.1015 + newDom += gap; 1.1016 + newDom += start; 1.1017 + 1.1018 + // Finally, pin to the real start and end of the month. 1.1019 + if (newDom < 1) 1.1020 + newDom = 1; 1.1021 + if (newDom > monthLen) 1.1022 + newDom = monthLen; 1.1023 + 1.1024 + // Set the DAY_OF_MONTH. We rely on the fact that this field 1.1025 + // takes precedence over everything else (since all other fields 1.1026 + // are also set at this point). If this fact changes (if the 1.1027 + // disambiguation algorithm changes) then we will have to unset 1.1028 + // the appropriate fields here so that DAY_OF_MONTH is attended 1.1029 + // to. 1.1030 + 1.1031 + // If we are in the cutover month, manipulate ms directly. Don't do 1.1032 + // this in general because it doesn't work across DST boundaries 1.1033 + // (details, details). This takes care of the discontinuity. 1.1034 + setTimeInMillis(cMonthStart + (newDom-1)*kOneDay, status); 1.1035 + return; 1.1036 + } 1.1037 + 1.1038 + default: 1.1039 + Calendar::roll(field, amount, status); 1.1040 + return; 1.1041 + } 1.1042 +} 1.1043 + 1.1044 +// ------------------------------------- 1.1045 + 1.1046 + 1.1047 +/** 1.1048 +* Return the minimum value that this field could have, given the current date. 1.1049 +* For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum(). 1.1050 +* @param field the time field. 1.1051 +* @return the minimum value that this field could have, given the current date. 1.1052 +* @deprecated ICU 2.6. Use getActualMinimum(UCalendarDateFields field) instead. 1.1053 +*/ 1.1054 +int32_t GregorianCalendar::getActualMinimum(EDateFields field) const 1.1055 +{ 1.1056 + return getMinimum((UCalendarDateFields)field); 1.1057 +} 1.1058 + 1.1059 +int32_t GregorianCalendar::getActualMinimum(EDateFields field, UErrorCode& /* status */) const 1.1060 +{ 1.1061 + return getMinimum((UCalendarDateFields)field); 1.1062 +} 1.1063 + 1.1064 +/** 1.1065 +* Return the minimum value that this field could have, given the current date. 1.1066 +* For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum(). 1.1067 +* @param field the time field. 1.1068 +* @return the minimum value that this field could have, given the current date. 1.1069 +* @draft ICU 2.6. 1.1070 +*/ 1.1071 +int32_t GregorianCalendar::getActualMinimum(UCalendarDateFields field, UErrorCode& /* status */) const 1.1072 +{ 1.1073 + return getMinimum(field); 1.1074 +} 1.1075 + 1.1076 + 1.1077 +// ------------------------------------ 1.1078 + 1.1079 +/** 1.1080 +* Old year limits were least max 292269054, max 292278994. 1.1081 +*/ 1.1082 + 1.1083 +/** 1.1084 +* @stable ICU 2.0 1.1085 +*/ 1.1086 +int32_t GregorianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const { 1.1087 + return kGregorianCalendarLimits[field][limitType]; 1.1088 +} 1.1089 + 1.1090 +/** 1.1091 +* Return the maximum value that this field could have, given the current date. 1.1092 +* For example, with the date "Feb 3, 1997" and the DAY_OF_MONTH field, the actual 1.1093 +* maximum would be 28; for "Feb 3, 1996" it s 29. Similarly for a Hebrew calendar, 1.1094 +* for some years the actual maximum for MONTH is 12, and for others 13. 1.1095 +* @stable ICU 2.0 1.1096 +*/ 1.1097 +int32_t GregorianCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const 1.1098 +{ 1.1099 + /* It is a known limitation that the code here (and in getActualMinimum) 1.1100 + * won't behave properly at the extreme limits of GregorianCalendar's 1.1101 + * representable range (except for the code that handles the YEAR 1.1102 + * field). That's because the ends of the representable range are at 1.1103 + * odd spots in the year. For calendars with the default Gregorian 1.1104 + * cutover, these limits are Sun Dec 02 16:47:04 GMT 292269055 BC to Sun 1.1105 + * Aug 17 07:12:55 GMT 292278994 AD, somewhat different for non-GMT 1.1106 + * zones. As a result, if the calendar is set to Aug 1 292278994 AD, 1.1107 + * the actual maximum of DAY_OF_MONTH is 17, not 30. If the date is Mar 1.1108 + * 31 in that year, the actual maximum month might be Jul, whereas is 1.1109 + * the date is Mar 15, the actual maximum might be Aug -- depending on 1.1110 + * the precise semantics that are desired. Similar considerations 1.1111 + * affect all fields. Nonetheless, this effect is sufficiently arcane 1.1112 + * that we permit it, rather than complicating the code to handle such 1.1113 + * intricacies. - liu 8/20/98 1.1114 + 1.1115 + * UPDATE: No longer true, since we have pulled in the limit values on 1.1116 + * the year. - Liu 11/6/00 */ 1.1117 + 1.1118 + switch (field) { 1.1119 + 1.1120 + case UCAL_YEAR: 1.1121 + /* The year computation is no different, in principle, from the 1.1122 + * others, however, the range of possible maxima is large. In 1.1123 + * addition, the way we know we've exceeded the range is different. 1.1124 + * For these reasons, we use the special case code below to handle 1.1125 + * this field. 1.1126 + * 1.1127 + * The actual maxima for YEAR depend on the type of calendar: 1.1128 + * 1.1129 + * Gregorian = May 17, 292275056 BC - Aug 17, 292278994 AD 1.1130 + * Julian = Dec 2, 292269055 BC - Jan 3, 292272993 AD 1.1131 + * Hybrid = Dec 2, 292269055 BC - Aug 17, 292278994 AD 1.1132 + * 1.1133 + * We know we've exceeded the maximum when either the month, date, 1.1134 + * time, or era changes in response to setting the year. We don't 1.1135 + * check for month, date, and time here because the year and era are 1.1136 + * sufficient to detect an invalid year setting. NOTE: If code is 1.1137 + * added to check the month and date in the future for some reason, 1.1138 + * Feb 29 must be allowed to shift to Mar 1 when setting the year. 1.1139 + */ 1.1140 + { 1.1141 + if(U_FAILURE(status)) return 0; 1.1142 + Calendar *cal = clone(); 1.1143 + if(!cal) { 1.1144 + status = U_MEMORY_ALLOCATION_ERROR; 1.1145 + return 0; 1.1146 + } 1.1147 + 1.1148 + cal->setLenient(TRUE); 1.1149 + 1.1150 + int32_t era = cal->get(UCAL_ERA, status); 1.1151 + UDate d = cal->getTime(status); 1.1152 + 1.1153 + /* Perform a binary search, with the invariant that lowGood is a 1.1154 + * valid year, and highBad is an out of range year. 1.1155 + */ 1.1156 + int32_t lowGood = kGregorianCalendarLimits[UCAL_YEAR][1]; 1.1157 + int32_t highBad = kGregorianCalendarLimits[UCAL_YEAR][2]+1; 1.1158 + while ((lowGood + 1) < highBad) { 1.1159 + int32_t y = (lowGood + highBad) / 2; 1.1160 + cal->set(UCAL_YEAR, y); 1.1161 + if (cal->get(UCAL_YEAR, status) == y && cal->get(UCAL_ERA, status) == era) { 1.1162 + lowGood = y; 1.1163 + } else { 1.1164 + highBad = y; 1.1165 + cal->setTime(d, status); // Restore original fields 1.1166 + } 1.1167 + } 1.1168 + 1.1169 + delete cal; 1.1170 + return lowGood; 1.1171 + } 1.1172 + 1.1173 + default: 1.1174 + return Calendar::getActualMaximum(field,status); 1.1175 + } 1.1176 +} 1.1177 + 1.1178 + 1.1179 +int32_t GregorianCalendar::handleGetExtendedYear() { 1.1180 + // the year to return 1.1181 + int32_t year = kEpochYear; 1.1182 + 1.1183 + // year field to use 1.1184 + int32_t yearField = UCAL_EXTENDED_YEAR; 1.1185 + 1.1186 + // There are three separate fields which could be used to 1.1187 + // derive the proper year. Use the one most recently set. 1.1188 + if (fStamp[yearField] < fStamp[UCAL_YEAR]) 1.1189 + yearField = UCAL_YEAR; 1.1190 + if (fStamp[yearField] < fStamp[UCAL_YEAR_WOY]) 1.1191 + yearField = UCAL_YEAR_WOY; 1.1192 + 1.1193 + // based on the "best" year field, get the year 1.1194 + switch(yearField) { 1.1195 + case UCAL_EXTENDED_YEAR: 1.1196 + year = internalGet(UCAL_EXTENDED_YEAR, kEpochYear); 1.1197 + break; 1.1198 + 1.1199 + case UCAL_YEAR: 1.1200 + { 1.1201 + // The year defaults to the epoch start, the era to AD 1.1202 + int32_t era = internalGet(UCAL_ERA, AD); 1.1203 + if (era == BC) { 1.1204 + year = 1 - internalGet(UCAL_YEAR, 1); // Convert to extended year 1.1205 + } else { 1.1206 + year = internalGet(UCAL_YEAR, kEpochYear); 1.1207 + } 1.1208 + } 1.1209 + break; 1.1210 + 1.1211 + case UCAL_YEAR_WOY: 1.1212 + year = handleGetExtendedYearFromWeekFields(internalGet(UCAL_YEAR_WOY), internalGet(UCAL_WEEK_OF_YEAR)); 1.1213 +#if defined (U_DEBUG_CAL) 1.1214 + // if(internalGet(UCAL_YEAR_WOY) != year) { 1.1215 + fprintf(stderr, "%s:%d: hGEYFWF[%d,%d] -> %d\n", 1.1216 + __FILE__, __LINE__,internalGet(UCAL_YEAR_WOY),internalGet(UCAL_WEEK_OF_YEAR),year); 1.1217 + //} 1.1218 +#endif 1.1219 + break; 1.1220 + 1.1221 + default: 1.1222 + year = kEpochYear; 1.1223 + } 1.1224 + return year; 1.1225 +} 1.1226 + 1.1227 +int32_t GregorianCalendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy) 1.1228 +{ 1.1229 + // convert year to extended form 1.1230 + int32_t era = internalGet(UCAL_ERA, AD); 1.1231 + if(era == BC) { 1.1232 + yearWoy = 1 - yearWoy; 1.1233 + } 1.1234 + return Calendar::handleGetExtendedYearFromWeekFields(yearWoy, woy); 1.1235 +} 1.1236 + 1.1237 + 1.1238 +// ------------------------------------- 1.1239 + 1.1240 +UBool 1.1241 +GregorianCalendar::inDaylightTime(UErrorCode& status) const 1.1242 +{ 1.1243 + if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) 1.1244 + return FALSE; 1.1245 + 1.1246 + // Force an update of the state of the Calendar. 1.1247 + ((GregorianCalendar*)this)->complete(status); // cast away const 1.1248 + 1.1249 + return (UBool)(U_SUCCESS(status) ? (internalGet(UCAL_DST_OFFSET) != 0) : FALSE); 1.1250 +} 1.1251 + 1.1252 +// ------------------------------------- 1.1253 + 1.1254 +/** 1.1255 +* Return the ERA. We need a special method for this because the 1.1256 +* default ERA is AD, but a zero (unset) ERA is BC. 1.1257 +*/ 1.1258 +int32_t 1.1259 +GregorianCalendar::internalGetEra() const { 1.1260 + return isSet(UCAL_ERA) ? internalGet(UCAL_ERA) : (int32_t)AD; 1.1261 +} 1.1262 + 1.1263 +const char * 1.1264 +GregorianCalendar::getType() const { 1.1265 + //static const char kGregorianType = "gregorian"; 1.1266 + 1.1267 + return "gregorian"; 1.1268 +} 1.1269 + 1.1270 +/** 1.1271 + * The system maintains a static default century start date and Year. They are 1.1272 + * initialized the first time they are used. Once the system default century date 1.1273 + * and year are set, they do not change. 1.1274 + */ 1.1275 +static UDate gSystemDefaultCenturyStart = DBL_MIN; 1.1276 +static int32_t gSystemDefaultCenturyStartYear = -1; 1.1277 +static icu::UInitOnce gSystemDefaultCenturyInit = U_INITONCE_INITIALIZER; 1.1278 + 1.1279 + 1.1280 +UBool GregorianCalendar::haveDefaultCentury() const 1.1281 +{ 1.1282 + return TRUE; 1.1283 +} 1.1284 + 1.1285 +static void U_CALLCONV 1.1286 +initializeSystemDefaultCentury() 1.1287 +{ 1.1288 + // initialize systemDefaultCentury and systemDefaultCenturyYear based 1.1289 + // on the current time. They'll be set to 80 years before 1.1290 + // the current time. 1.1291 + UErrorCode status = U_ZERO_ERROR; 1.1292 + GregorianCalendar calendar(status); 1.1293 + if (U_SUCCESS(status)) { 1.1294 + calendar.setTime(Calendar::getNow(), status); 1.1295 + calendar.add(UCAL_YEAR, -80, status); 1.1296 + 1.1297 + gSystemDefaultCenturyStart = calendar.getTime(status); 1.1298 + gSystemDefaultCenturyStartYear = calendar.get(UCAL_YEAR, status); 1.1299 + } 1.1300 + // We have no recourse upon failure unless we want to propagate the failure 1.1301 + // out. 1.1302 +} 1.1303 + 1.1304 +UDate GregorianCalendar::defaultCenturyStart() const { 1.1305 + // lazy-evaluate systemDefaultCenturyStart 1.1306 + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); 1.1307 + return gSystemDefaultCenturyStart; 1.1308 +} 1.1309 + 1.1310 +int32_t GregorianCalendar::defaultCenturyStartYear() const { 1.1311 + // lazy-evaluate systemDefaultCenturyStartYear 1.1312 + umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); 1.1313 + return gSystemDefaultCenturyStartYear; 1.1314 +} 1.1315 + 1.1316 +U_NAMESPACE_END 1.1317 + 1.1318 +#endif /* #if !UCONFIG_NO_FORMATTING */ 1.1319 + 1.1320 +//eof