intl/icu/source/i18n/gregocal.cpp

changeset 0
6474c204b198
     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

mercurial