Coverage Report

Created: 2025-09-05 07:16

/src/icu/icu4c/source/i18n/gregocal.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2016 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
/*
4
*******************************************************************************
5
* Copyright (C) 1997-2016, International Business Machines Corporation and
6
* others. All Rights Reserved.
7
*******************************************************************************
8
*
9
* File GREGOCAL.CPP
10
*
11
* Modification History:
12
*
13
*   Date        Name        Description
14
*   02/05/97    clhuang     Creation.
15
*   03/28/97    aliu        Made highly questionable fix to computeFields to
16
*                           handle DST correctly.
17
*   04/22/97    aliu        Cleaned up code drastically.  Added monthLength().
18
*                           Finished unimplemented parts of computeTime() for
19
*                           week-based date determination.  Removed quetionable
20
*                           fix and wrote correct fix for computeFields() and
21
*                           daylight time handling.  Rewrote inDaylightTime()
22
*                           and computeFields() to handle sensitive Daylight to
23
*                           Standard time transitions correctly.
24
*   05/08/97    aliu        Added code review changes.  Fixed isLeapYear() to
25
*                           not cutover.
26
*   08/12/97    aliu        Added equivalentTo.  Misc other fixes.  Updated
27
*                           add() from Java source.
28
*    07/28/98    stephen        Sync up with JDK 1.2
29
*    09/14/98    stephen        Changed type of kOneDay, kOneWeek to double.
30
*                            Fixed bug in roll() 
31
*   10/15/99    aliu        Fixed j31, incorrect WEEK_OF_YEAR computation.
32
*   10/15/99    aliu        Fixed j32, cannot set date to Feb 29 2000 AD.
33
*                           {JDK bug 4210209 4209272}
34
*   11/15/99    weiv        Added YEAR_WOY and DOW_LOCAL computation
35
*                           to timeToFields method, updated kMinValues, kMaxValues & kLeastMaxValues
36
*   12/09/99    aliu        Fixed j81, calculation errors and roll bugs
37
*                           in year of cutover.
38
*   01/24/2000  aliu        Revised computeJulianDay for YEAR YEAR_WOY WOY.
39
********************************************************************************
40
*/
41
42
#include "unicode/utypes.h"
43
#include <float.h>
44
45
#if !UCONFIG_NO_FORMATTING
46
47
#include "unicode/gregocal.h"
48
#include "gregoimp.h"
49
#include "umutex.h"
50
#include "uassert.h"
51
52
// *****************************************************************************
53
// class GregorianCalendar
54
// *****************************************************************************
55
56
/**
57
* Note that the Julian date used here is not a true Julian date, since
58
* it is measured from midnight, not noon.  This value is the Julian
59
* day number of January 1, 1970 (Gregorian calendar) at noon UTC. [LIU]
60
*/
61
62
static const int16_t kNumDays[]
63
= {0,31,59,90,120,151,181,212,243,273,304,334}; // 0-based, for day-in-year
64
static const int16_t kLeapNumDays[]
65
= {0,31,60,91,121,152,182,213,244,274,305,335}; // 0-based, for day-in-year
66
static const int8_t kMonthLength[]
67
= {31,28,31,30,31,30,31,31,30,31,30,31}; // 0-based
68
static const int8_t kLeapMonthLength[]
69
= {31,29,31,30,31,30,31,31,30,31,30,31}; // 0-based
70
71
// setTimeInMillis() limits the Julian day range to +/-7F000000.
72
// This would seem to limit the year range to:
73
//  ms=+183882168921600000  jd=7f000000  December 20, 5828963 AD
74
//  ms=-184303902528000000  jd=81000000  September 20, 5838270 BC
75
// HOWEVER, CalendarRegressionTest/Test4167060 shows that the actual
76
// range limit on the year field is smaller (~ +/-140000). [alan 3.0]
77
78
static const int32_t kGregorianCalendarLimits[UCAL_FIELD_COUNT][4] = {
79
    // Minimum  Greatest    Least  Maximum
80
    //           Minimum  Maximum
81
    {        0,        0,        1,        1}, // ERA
82
    {        1,        1,   140742,   144683}, // YEAR
83
    {        0,        0,       11,       11}, // MONTH
84
    {        1,        1,       52,       53}, // WEEK_OF_YEAR
85
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // WEEK_OF_MONTH
86
    {        1,        1,       28,       31}, // DAY_OF_MONTH
87
    {        1,        1,      365,      366}, // DAY_OF_YEAR
88
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DAY_OF_WEEK
89
    {       -1,       -1,        4,        5}, // DAY_OF_WEEK_IN_MONTH
90
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // AM_PM
91
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR
92
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // HOUR_OF_DAY
93
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MINUTE
94
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // SECOND
95
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECOND
96
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // ZONE_OFFSET
97
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DST_OFFSET
98
    {  -140742,  -140742,   140742,   144683}, // YEAR_WOY
99
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // DOW_LOCAL
100
    {  -140742,  -140742,   140742,   144683}, // EXTENDED_YEAR
101
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // JULIAN_DAY
102
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // MILLISECONDS_IN_DAY
103
    {/*N/A*/-1,/*N/A*/-1,/*N/A*/-1,/*N/A*/-1}, // IS_LEAP_MONTH
104
    {        0,        0,       11,       11}, // ORDINAL_MONTH
105
};
106
107
/*
108
* <pre>
109
*                            Greatest       Least 
110
* Field name        Minimum   Minimum     Maximum     Maximum
111
* ----------        -------   -------     -------     -------
112
* ERA                     0         0           1           1
113
* YEAR                    1         1      140742      144683
114
* MONTH                   0         0          11          11
115
* WEEK_OF_YEAR            1         1          52          53
116
* WEEK_OF_MONTH           0         0           4           6
117
* DAY_OF_MONTH            1         1          28          31
118
* DAY_OF_YEAR             1         1         365         366
119
* DAY_OF_WEEK             1         1           7           7
120
* DAY_OF_WEEK_IN_MONTH   -1        -1           4           5
121
* AM_PM                   0         0           1           1
122
* HOUR                    0         0          11          11
123
* HOUR_OF_DAY             0         0          23          23
124
* MINUTE                  0         0          59          59
125
* SECOND                  0         0          59          59
126
* MILLISECOND             0         0         999         999
127
* ZONE_OFFSET           -12*      -12*         12*         12*
128
* DST_OFFSET              0         0           1*          1*
129
* YEAR_WOY                1         1      140742      144683
130
* DOW_LOCAL               1         1           7           7
131
* </pre>
132
* (*) In units of one-hour
133
*/
134
135
#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
136
#include <stdio.h>
137
#endif
138
139
U_NAMESPACE_BEGIN
140
141
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(GregorianCalendar)
142
143
// 00:00:00 UTC, October 15, 1582, expressed in ms from the epoch.
144
// Note that only Italy and other Catholic countries actually
145
// observed this cutover.  Most other countries followed in
146
// the next few centuries, some as late as 1928. [LIU]
147
// in Java, -12219292800000L
148
//const UDate GregorianCalendar::kPapalCutover = -12219292800000L;
149
static const uint32_t kCutoverJulianDay = 2299161;
150
static const int32_t kDefaultCutoverYear = 1582;
151
static const UDate kPapalCutover = (2299161.0 - kEpochStartAsJulianDay) * U_MILLIS_PER_DAY;
152
//static const UDate kPapalCutoverJulian = (2299161.0 - kEpochStartAsJulianDay);
153
154
// -------------------------------------
155
156
GregorianCalendar::GregorianCalendar(UErrorCode& status)
157
0
:   Calendar(status),
158
0
fGregorianCutover(kPapalCutover),
159
0
fCutoverJulianDay(kCutoverJulianDay), fGregorianCutoverYear(kDefaultCutoverYear),
160
0
fIsGregorian(true), fInvertGregorian(false)
161
0
{
162
0
    setTimeInMillis(getNow(), status);
163
0
}
164
165
// -------------------------------------
166
167
GregorianCalendar::GregorianCalendar(TimeZone* zone, UErrorCode& status)
168
0
:   GregorianCalendar(zone, Locale::getDefault(), status)
169
0
{
170
0
}
171
172
// -------------------------------------
173
174
GregorianCalendar::GregorianCalendar(const TimeZone& zone, UErrorCode& status)
175
0
:   GregorianCalendar(zone, Locale::getDefault(), status)
176
0
{
177
0
}
178
179
// -------------------------------------
180
181
GregorianCalendar::GregorianCalendar(const Locale& aLocale, UErrorCode& status)
182
17.0k
:   GregorianCalendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, status)
183
17.0k
{
184
17.0k
}
185
186
// -------------------------------------
187
188
GregorianCalendar::GregorianCalendar(TimeZone* zone, const Locale& aLocale,
189
                                     UErrorCode& status)
190
17.0k
                                     :   Calendar(zone, aLocale, status),
191
17.0k
                                     fGregorianCutover(kPapalCutover),
192
17.0k
                                     fCutoverJulianDay(kCutoverJulianDay), fGregorianCutoverYear(kDefaultCutoverYear),
193
17.0k
                                     fIsGregorian(true), fInvertGregorian(false)
194
17.0k
{
195
17.0k
    setTimeInMillis(getNow(), status);
196
17.0k
}
197
198
// -------------------------------------
199
200
GregorianCalendar::GregorianCalendar(const TimeZone& zone, const Locale& aLocale,
201
                                     UErrorCode& status)
202
0
                                     :   Calendar(zone, aLocale, status),
203
0
                                     fGregorianCutover(kPapalCutover),
204
0
                                     fCutoverJulianDay(kCutoverJulianDay), fGregorianCutoverYear(kDefaultCutoverYear),
205
0
                                     fIsGregorian(true), fInvertGregorian(false)
206
0
{
207
0
    setTimeInMillis(getNow(), status);
208
0
}
209
210
// -------------------------------------
211
212
GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date,
213
                                     UErrorCode& status)
214
0
                                     :   Calendar(TimeZone::createDefault(), Locale::getDefault(), status),
215
0
                                     fGregorianCutover(kPapalCutover),
216
0
                                     fCutoverJulianDay(kCutoverJulianDay), fGregorianCutoverYear(kDefaultCutoverYear),
217
0
                                     fIsGregorian(true), fInvertGregorian(false)
218
0
{
219
0
    set(UCAL_ERA, AD);
220
0
    set(UCAL_YEAR, year);
221
0
    set(UCAL_MONTH, month);
222
0
    set(UCAL_DATE, date);
223
0
}
224
225
// -------------------------------------
226
227
GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date,
228
                                     int32_t hour, int32_t minute, UErrorCode& status)
229
0
                                     :   GregorianCalendar(year, month, date, status)
230
0
{
231
0
    set(UCAL_HOUR_OF_DAY, hour);
232
0
    set(UCAL_MINUTE, minute);
233
0
}
234
235
// -------------------------------------
236
237
GregorianCalendar::GregorianCalendar(int32_t year, int32_t month, int32_t date,
238
                                     int32_t hour, int32_t minute, int32_t second,
239
                                     UErrorCode& status)
240
0
                                     :   GregorianCalendar(year, month, date, hour, minute, status)
241
0
{
242
0
    set(UCAL_SECOND, second);
243
0
}
244
245
// -------------------------------------
246
247
GregorianCalendar::~GregorianCalendar()
248
578k
{
249
578k
}
250
251
// -------------------------------------
252
253
GregorianCalendar::GregorianCalendar(const GregorianCalendar &source)
254
562k
:   Calendar(source),
255
562k
fGregorianCutover(source.fGregorianCutover),
256
562k
fCutoverJulianDay(source.fCutoverJulianDay), fGregorianCutoverYear(source.fGregorianCutoverYear),
257
562k
fIsGregorian(source.fIsGregorian), fInvertGregorian(source.fInvertGregorian)
258
562k
{
259
562k
}
260
261
// -------------------------------------
262
263
GregorianCalendar* GregorianCalendar::clone() const
264
315k
{
265
315k
    return new GregorianCalendar(*this);
266
315k
}
267
268
// -------------------------------------
269
270
GregorianCalendar &
271
GregorianCalendar::operator=(const GregorianCalendar &right)
272
0
{
273
0
    if (this != &right)
274
0
    {
275
0
        Calendar::operator=(right);
276
0
        fGregorianCutover = right.fGregorianCutover;
277
0
        fGregorianCutoverYear = right.fGregorianCutoverYear;
278
0
        fCutoverJulianDay = right.fCutoverJulianDay;
279
0
    }
280
0
    return *this;
281
0
}
282
283
// -------------------------------------
284
285
UBool GregorianCalendar::isEquivalentTo(const Calendar& other) const
286
0
{
287
    // Calendar override.
288
0
    return Calendar::isEquivalentTo(other) &&
289
0
        fGregorianCutover == ((GregorianCalendar*)&other)->fGregorianCutover;
290
0
}
291
292
// -------------------------------------
293
294
void
295
GregorianCalendar::setGregorianChange(UDate date, UErrorCode& status)
296
0
{
297
0
    if (U_FAILURE(status)) 
298
0
        return;
299
300
    // Precompute two internal variables which we use to do the actual
301
    // cutover computations.  These are the normalized cutover, which is the
302
    // midnight at or before the cutover, and the cutover year.  The
303
    // normalized cutover is in pure date milliseconds; it contains no time
304
    // of day or timezone component, and it used to compare against other
305
    // pure date values.
306
0
    double cutoverDay = ClockMath::floorDivide(date, kOneDay);
307
308
    // Handle the rare case of numeric overflow where the user specifies a time
309
    // outside of INT32_MIN .. INT32_MAX number of days.
310
    
311
0
    if (cutoverDay <= INT32_MIN) {
312
0
        cutoverDay = INT32_MIN;
313
0
        fGregorianCutover = cutoverDay * kOneDay;
314
0
    } else if (cutoverDay >= INT32_MAX) {
315
0
        cutoverDay = INT32_MAX;
316
0
        fGregorianCutover = cutoverDay * kOneDay;
317
0
    } else {
318
0
        fGregorianCutover = date;
319
0
    }
320
321
    // Normalize the year so BC values are represented as 0 and negative
322
    // values.
323
0
    GregorianCalendar *cal = new GregorianCalendar(getTimeZone(), status);
324
    /* test for nullptr */
325
0
    if (cal == nullptr) {
326
0
        status = U_MEMORY_ALLOCATION_ERROR;
327
0
        return;
328
0
    }
329
0
    if(U_FAILURE(status)) {
330
0
        return;
331
0
    }
332
0
    cal->setTime(date, status);
333
0
    fGregorianCutoverYear = cal->get(UCAL_YEAR, status);
334
0
    if (cal->get(UCAL_ERA, status) == BC) {
335
0
        fGregorianCutoverYear = 1 - fGregorianCutoverYear;
336
0
    }
337
0
    fCutoverJulianDay = static_cast<int32_t>(cutoverDay);
338
0
    delete cal;
339
0
}
340
341
877k
void GregorianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& status) {
342
877k
    int32_t eyear, month, dayOfMonth, dayOfYear, unusedRemainder;
343
344
877k
    if(U_FAILURE(status)) {
345
74
        return;
346
74
    }
347
348
#if defined (U_DEBUG_CAL)
349
    fprintf(stderr, "%s:%d: jd%d- (greg's %d)- [cut=%d]\n", 
350
        __FILE__, __LINE__, julianDay, getGregorianDayOfYear(), fCutoverJulianDay);
351
#endif
352
353
877k
    if (julianDay >= fCutoverJulianDay) {
354
860k
        month = getGregorianMonth();
355
860k
        dayOfMonth = getGregorianDayOfMonth();
356
860k
        dayOfYear = getGregorianDayOfYear();
357
860k
        eyear = getGregorianYear();
358
860k
    } else {
359
        // The Julian epoch day (not the same as Julian Day)
360
        // is zero on Saturday December 30, 0 (Gregorian).
361
17.0k
        int32_t julianEpochDay = julianDay - (kJan1_1JulianDay - 2);
362
17.0k
    eyear = static_cast<int32_t>(ClockMath::floorDivide((4.0 * julianEpochDay) + 1464.0, static_cast<int32_t>(1461), &unusedRemainder));
363
364
        // Compute the Julian calendar day number for January 1, eyear
365
17.0k
        int32_t january1 = 365 * (eyear - 1) + ClockMath::floorDivide(eyear - 1, static_cast<int32_t>(4));
366
17.0k
        dayOfYear = (julianEpochDay - january1); // 0-based
367
368
        // Julian leap years occurred historically every 4 years starting
369
        // with 8 AD.  Before 8 AD the spacing is irregular; every 3 years
370
        // from 45 BC to 9 BC, and then none until 8 AD.  However, we don't
371
        // implement this historical detail; instead, we implement the
372
        // computationally cleaner proleptic calendar, which assumes
373
        // consistent 4-year cycles throughout time.
374
17.0k
        UBool isLeap = ((eyear&0x3) == 0); // equiv. to (eyear%4 == 0)
375
376
        // Common Julian/Gregorian calculation
377
17.0k
        int32_t correction = 0;
378
17.0k
        int32_t march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
379
17.0k
        if (dayOfYear >= march1) {
380
13.0k
            correction = isLeap ? 1 : 2;
381
13.0k
        }
382
17.0k
        month = (12 * (dayOfYear + correction) + 6) / 367; // zero-based month
383
17.0k
        dayOfMonth = dayOfYear - (isLeap?kLeapNumDays[month]:kNumDays[month]) + 1; // one-based DOM
384
17.0k
        ++dayOfYear;
385
#if defined (U_DEBUG_CAL)
386
        //     fprintf(stderr, "%d - %d[%d] + 1\n", dayOfYear, isLeap?kLeapNumDays[month]:kNumDays[month], month );
387
        //           fprintf(stderr, "%s:%d:  greg's HCF %d -> %d/%d/%d not %d/%d/%d\n", 
388
        //                   __FILE__, __LINE__,julianDay,
389
        //          eyear,month,dayOfMonth,
390
        //          getGregorianYear(), getGregorianMonth(), getGregorianDayOfMonth()  );
391
        fprintf(stderr, "%s:%d: doy %d (greg's %d)- [cut=%d]\n", 
392
            __FILE__, __LINE__, dayOfYear, getGregorianDayOfYear(), fCutoverJulianDay);
393
#endif
394
395
17.0k
    }
396
397
    // [j81] if we are after the cutover in its year, shift the day of the year
398
877k
    if((eyear == fGregorianCutoverYear) && (julianDay >= fCutoverJulianDay)) {
399
        //from handleComputeMonthStart
400
682
        int32_t gregShift = Grego::gregorianShift(eyear);
401
#if defined (U_DEBUG_CAL)
402
        fprintf(stderr, "%s:%d:  gregorian shift %d :::  doy%d => %d [cut=%d]\n",
403
            __FILE__, __LINE__,gregShift, dayOfYear, dayOfYear+gregShift, fCutoverJulianDay);
404
#endif
405
682
        dayOfYear += gregShift;
406
682
    }
407
408
877k
    internalSet(UCAL_MONTH, month);
409
877k
    internalSet(UCAL_ORDINAL_MONTH, month);
410
877k
    internalSet(UCAL_DAY_OF_MONTH, dayOfMonth);
411
877k
    internalSet(UCAL_DAY_OF_YEAR, dayOfYear);
412
877k
    internalSet(UCAL_EXTENDED_YEAR, eyear);
413
877k
    int32_t era = AD;
414
877k
    if (eyear < 1) {
415
14.3k
        era = BC;
416
14.3k
        eyear = 1 - eyear;
417
14.3k
    }
418
877k
    internalSet(UCAL_ERA, era);
419
877k
    internalSet(UCAL_YEAR, eyear);
420
877k
}
421
422
423
// -------------------------------------
424
425
UDate
426
GregorianCalendar::getGregorianChange() const
427
0
{
428
0
    return fGregorianCutover;
429
0
}
430
431
// -------------------------------------
432
433
UBool 
434
GregorianCalendar::isLeapYear(int32_t year) const
435
1.29M
{
436
    // MSVC complains bitterly if we try to use Grego::isLeapYear here
437
    // NOTE: year&0x3 == year%4
438
1.29M
    return (year >= fGregorianCutoverYear ?
439
1.27M
        (((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0))) : // Gregorian
440
1.29M
    ((year&0x3) == 0)); // Julian
441
1.29M
}
442
443
// -------------------------------------
444
445
int32_t GregorianCalendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCode& status)
446
828k
{
447
828k
    fInvertGregorian = false;
448
449
828k
    int32_t jd = Calendar::handleComputeJulianDay(bestField, status);
450
828k
    if (U_FAILURE(status)) {
451
713
        return 0;
452
713
    }
453
454
827k
    if((bestField == UCAL_WEEK_OF_YEAR) &&  // if we are doing WOY calculations, we are counting relative to Jan 1 *julian*
455
827k
        (internalGet(UCAL_EXTENDED_YEAR)==fGregorianCutoverYear) && 
456
827k
        jd >= fCutoverJulianDay) { 
457
32
            fInvertGregorian = true;  // So that the Julian Jan 1 will be used in handleComputeMonthStart
458
32
            return Calendar::handleComputeJulianDay(bestField, status);
459
32
        }
460
461
462
        // The following check handles portions of the cutover year BEFORE the
463
        // cutover itself happens.
464
        //if ((fIsGregorian==true) != (jd >= fCutoverJulianDay)) {  /*  cutoverJulianDay)) { */
465
827k
        if ((fIsGregorian) != (jd >= fCutoverJulianDay)) {  /*  cutoverJulianDay)) { */
466
#if defined (U_DEBUG_CAL)
467
            fprintf(stderr, "%s:%d: jd [invert] %d\n", 
468
                __FILE__, __LINE__, jd);
469
#endif
470
1.92k
            fInvertGregorian = true;
471
1.92k
            jd = Calendar::handleComputeJulianDay(bestField, status);
472
1.92k
            if (U_FAILURE(status)) {
473
93
                return 0;
474
93
            }
475
#if defined (U_DEBUG_CAL)
476
            fprintf(stderr, "%s:%d:  fIsGregorian %s, fInvertGregorian %s - ", 
477
                __FILE__, __LINE__,fIsGregorian?"T":"F", fInvertGregorian?"T":"F");
478
            fprintf(stderr, " jd NOW %d\n", 
479
                jd);
480
#endif
481
825k
        } else {
482
#if defined (U_DEBUG_CAL)
483
            fprintf(stderr, "%s:%d: jd [==] %d - %sfIsGregorian %sfInvertGregorian, %d\n", 
484
                __FILE__, __LINE__, jd, fIsGregorian?"T":"F", fInvertGregorian?"T":"F", bestField);
485
#endif
486
825k
        }
487
488
827k
        if(fIsGregorian && (internalGet(UCAL_EXTENDED_YEAR) == fGregorianCutoverYear)) {
489
291
            int32_t gregShift = Grego::gregorianShift(internalGet(UCAL_EXTENDED_YEAR));
490
291
            if (bestField == UCAL_DAY_OF_YEAR) {
491
#if defined (U_DEBUG_CAL)
492
                fprintf(stderr, "%s:%d: [DOY%d] gregorian shift of JD %d += %d\n", 
493
                    __FILE__, __LINE__, fFields[bestField],jd, gregShift);
494
#endif
495
10
                jd -= gregShift;
496
281
            } else if ( bestField == UCAL_WEEK_OF_MONTH ) {
497
10
                int32_t weekShift = 14;
498
#if defined (U_DEBUG_CAL)
499
                fprintf(stderr, "%s:%d: [WOY/WOM] gregorian week shift of %d += %d\n", 
500
                    __FILE__, __LINE__, jd, weekShift);
501
#endif
502
10
                jd += weekShift; // shift by weeks for week based fields.
503
10
            }
504
291
        }
505
506
827k
        return jd;
507
827k
}
508
509
int64_t GregorianCalendar::handleComputeMonthStart(int32_t eyear, int32_t month,
510
511
                                                   UBool /* useMonth */, UErrorCode& status) const
512
1.45M
{
513
1.45M
    if (U_FAILURE(status)) {
514
0
        return 0;
515
0
    }
516
1.45M
    GregorianCalendar* nonConstThis = const_cast<GregorianCalendar*>(this); // cast away const
517
518
    // If the month is out of range, adjust it into range, and
519
    // modify the extended year value accordingly.
520
1.45M
    if (month < 0 || month > 11) {
521
4.32k
        if (uprv_add32_overflow(ClockMath::floorDivide(month, 12, &month),
522
4.32k
                                eyear, &eyear)) {
523
13
            status = U_ILLEGAL_ARGUMENT_ERROR;
524
13
            return 0;
525
13
        }
526
4.32k
    }
527
528
1.45M
    UBool isLeap = eyear%4 == 0;
529
1.45M
    int64_t y = static_cast<int64_t>(eyear) - 1;
530
1.45M
    int64_t julianDay = 365LL * y +
531
1.45M
        ClockMath::floorDivideInt64(y, 4LL) + kJan1_1JulianDay - 3LL;
532
533
1.45M
    nonConstThis->fIsGregorian = (eyear >= fGregorianCutoverYear);
534
#if defined (U_DEBUG_CAL)
535
    fprintf(stderr, "%s:%d: (hcms%d/%d) fIsGregorian %s, fInvertGregorian %s\n", 
536
        __FILE__, __LINE__, eyear,month, fIsGregorian?"T":"F", fInvertGregorian?"T":"F");
537
#endif
538
1.45M
    if (fInvertGregorian) {
539
2.38k
        nonConstThis->fIsGregorian = !fIsGregorian;
540
2.38k
    }
541
1.45M
    if (fIsGregorian) {
542
1.43M
        isLeap = isLeap && ((eyear%100 != 0) || (eyear%400 == 0));
543
        // Add 2 because Gregorian calendar starts 2 days after
544
        // Julian calendar
545
1.43M
        int32_t gregShift = Grego::gregorianShift(eyear);
546
#if defined (U_DEBUG_CAL)
547
        fprintf(stderr, "%s:%d: (hcms%d/%d) gregorian shift of %d += %d\n", 
548
            __FILE__, __LINE__, eyear, month, julianDay, gregShift);
549
#endif
550
1.43M
        julianDay += gregShift;
551
1.43M
    }
552
553
    // At this point julianDay indicates the day BEFORE the first
554
    // day of January 1, <eyear> of either the Julian or Gregorian
555
    // calendar.
556
557
1.45M
    if (month != 0) {
558
112k
        julianDay += isLeap?kLeapNumDays[month]:kNumDays[month];
559
112k
    }
560
561
1.45M
    return julianDay;
562
1.45M
}
563
564
int32_t GregorianCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& /* status */)  const
565
412k
{
566
    // If the month is out of range, adjust it into range, and
567
    // modify the extended year value accordingly.
568
412k
    if (month < 0 || month > 11) {
569
94
        extendedYear += ClockMath::floorDivide(month, 12, &month);
570
94
    }
571
572
412k
    return isLeapYear(extendedYear) ? kLeapMonthLength[month] : kMonthLength[month];
573
412k
}
574
575
878k
int32_t GregorianCalendar::handleGetYearLength(int32_t eyear, UErrorCode& status) const {
576
878k
    if (U_FAILURE(status)) return 0;
577
878k
    return isLeapYear(eyear) ? 366 : 365;
578
878k
}
579
580
581
int32_t
582
GregorianCalendar::monthLength(int32_t month, UErrorCode& status) const
583
142
{
584
142
    int32_t year = internalGet(UCAL_EXTENDED_YEAR);
585
142
    return handleGetMonthLength(year, month, status);
586
142
}
587
588
// -------------------------------------
589
590
int32_t
591
GregorianCalendar::monthLength(int32_t month, int32_t year) const
592
0
{
593
0
    return isLeapYear(year) ? kLeapMonthLength[month] : kMonthLength[month];
594
0
}
595
596
// -------------------------------------
597
598
int32_t
599
GregorianCalendar::yearLength() const
600
0
{
601
0
    return isLeapYear(internalGet(UCAL_YEAR)) ? 366 : 365;
602
0
}
603
604
// -------------------------------------
605
606
UBool
607
GregorianCalendar::validateFields() const
608
0
{
609
0
    for (int32_t field = 0; field < UCAL_FIELD_COUNT; field++) {
610
        // Ignore DATE and DAY_OF_YEAR which are handled below
611
0
        if (field != UCAL_DATE &&
612
0
            field != UCAL_DAY_OF_YEAR &&
613
0
            isSet(static_cast<UCalendarDateFields>(field)) &&
614
0
            !boundsCheck(internalGet(static_cast<UCalendarDateFields>(field)), static_cast<UCalendarDateFields>(field)))
615
0
            return false;
616
0
    }
617
618
    // Values differ in Least-Maximum and Maximum should be handled
619
    // specially.
620
0
    if (isSet(UCAL_DATE)) {
621
0
        int32_t date = internalGet(UCAL_DATE);
622
0
        UErrorCode internalStatus = U_ZERO_ERROR;
623
0
        if (date < getMinimum(UCAL_DATE) ||
624
0
            date > monthLength(internalGetMonth(internalStatus), internalStatus) ||
625
0
            U_FAILURE(internalStatus)) {
626
0
                return false;
627
0
        }
628
0
    }
629
630
0
    if (isSet(UCAL_DAY_OF_YEAR)) {
631
0
        int32_t days = internalGet(UCAL_DAY_OF_YEAR);
632
0
        if (days < 1 || days > yearLength()) {
633
0
            return false;
634
0
        }
635
0
    }
636
637
    // Handle DAY_OF_WEEK_IN_MONTH, which must not have the value zero.
638
    // We've checked against minimum and maximum above already.
639
0
    if (isSet(UCAL_DAY_OF_WEEK_IN_MONTH) &&
640
0
        0 == internalGet(UCAL_DAY_OF_WEEK_IN_MONTH)) {
641
0
            return false;
642
0
        }
643
644
0
        return true;
645
0
}
646
647
// -------------------------------------
648
649
UBool
650
GregorianCalendar::boundsCheck(int32_t value, UCalendarDateFields field) const
651
0
{
652
0
    return value >= getMinimum(field) && value <= getMaximum(field);
653
0
}
654
655
// -------------------------------------
656
657
UDate 
658
GregorianCalendar::getEpochDay(UErrorCode& status) 
659
0
{
660
0
    complete(status);
661
    // Divide by 1000 (convert to seconds) in order to prevent overflow when
662
    // dealing with UDate(Long.MIN_VALUE) and UDate(Long.MAX_VALUE).
663
0
    double wallSec = internalGetTime()/1000 + (internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET))/1000;
664
665
0
    return ClockMath::floorDivide(wallSec, kOneDay/1000.0);
666
0
}
667
668
// -------------------------------------
669
670
671
// -------------------------------------
672
673
/**
674
* Compute the julian day number of the day BEFORE the first day of
675
* January 1, year 1 of the given calendar.  If julianDay == 0, it
676
* specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
677
* or Gregorian).
678
*/
679
double GregorianCalendar::computeJulianDayOfYear(UBool isGregorian,
680
                                                 int32_t year, UBool& isLeap)
681
0
{
682
0
    isLeap = year%4 == 0;
683
0
    int32_t y = year - 1;
684
0
    double julianDay = 365.0*y + ClockMath::floorDivide(y, 4) + (kJan1_1JulianDay - 3);
685
686
0
    if (isGregorian) {
687
0
        isLeap = isLeap && ((year%100 != 0) || (year%400 == 0));
688
        // Add 2 because Gregorian calendar starts 2 days after Julian calendar
689
0
        julianDay += Grego::gregorianShift(year);
690
0
    }
691
692
0
    return julianDay;
693
0
}
694
695
// /**
696
//  * Compute the day of week, relative to the first day of week, from
697
//  * 0..6, of the current DOW_LOCAL or DAY_OF_WEEK fields.  This is
698
//  * equivalent to get(DOW_LOCAL) - 1.
699
//  */
700
// int32_t GregorianCalendar::computeRelativeDOW() const {
701
//     int32_t relDow = 0;
702
//     if (fStamp[UCAL_DOW_LOCAL] > fStamp[UCAL_DAY_OF_WEEK]) {
703
//         relDow = internalGet(UCAL_DOW_LOCAL) - 1; // 1-based
704
//     } else if (fStamp[UCAL_DAY_OF_WEEK] != kUnset) {
705
//         relDow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
706
//         if (relDow < 0) relDow += 7;
707
//     }
708
//     return relDow;
709
// }
710
711
// /**
712
//  * Compute the day of week, relative to the first day of week,
713
//  * from 0..6 of the given julian day.
714
//  */
715
// int32_t GregorianCalendar::computeRelativeDOW(double julianDay) const {
716
//   int32_t relDow = julianDayToDayOfWeek(julianDay) - getFirstDayOfWeek();
717
//     if (relDow < 0) {
718
//         relDow += 7;
719
//     }
720
//     return relDow;
721
// }
722
723
// /**
724
//  * Compute the DOY using the WEEK_OF_YEAR field and the julian day
725
//  * of the day BEFORE January 1 of a year (a return value from
726
//  * computeJulianDayOfYear).
727
//  */
728
// int32_t GregorianCalendar::computeDOYfromWOY(double julianDayOfYear) const {
729
//     // Compute DOY from day of week plus week of year
730
731
//     // Find the day of the week for the first of this year.  This
732
//     // is zero-based, with 0 being the locale-specific first day of
733
//     // the week.  Add 1 to get first day of year.
734
//     int32_t fdy = computeRelativeDOW(julianDayOfYear + 1);
735
736
//     return
737
//         // Compute doy of first (relative) DOW of WOY 1
738
//         (((7 - fdy) < getMinimalDaysInFirstWeek())
739
//          ? (8 - fdy) : (1 - fdy))
740
741
//         // Adjust for the week number.
742
//         + (7 * (internalGet(UCAL_WEEK_OF_YEAR) - 1))
743
744
//         // Adjust for the DOW
745
//         + computeRelativeDOW();
746
// }
747
748
// -------------------------------------
749
750
double
751
GregorianCalendar::millisToJulianDay(UDate millis)
752
0
{
753
0
    return static_cast<double>(kEpochStartAsJulianDay) + ClockMath::floorDivide(millis, kOneDay);
754
0
}
755
756
// -------------------------------------
757
758
UDate
759
GregorianCalendar::julianDayToMillis(double julian)
760
0
{
761
0
    return static_cast<UDate>((julian - kEpochStartAsJulianDay) * kOneDay);
762
0
}
763
764
// -------------------------------------
765
766
int32_t
767
GregorianCalendar::aggregateStamp(int32_t stamp_a, int32_t stamp_b)
768
0
{
769
0
    return (((stamp_a != kUnset && stamp_b != kUnset)
770
0
        ? uprv_max(stamp_a, stamp_b)
771
0
        : static_cast<int32_t>(kUnset)));
772
0
}
773
774
// -------------------------------------
775
776
/**
777
* Roll a field by a signed amount.
778
* Note: This will be made public later. [LIU]
779
*/
780
781
void
782
0
GregorianCalendar::roll(EDateFields field, int32_t amount, UErrorCode& status) {
783
0
    roll(static_cast<UCalendarDateFields>(field), amount, status);
784
0
}
785
786
void
787
1.35k
GregorianCalendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) UPRV_NO_SANITIZE_UNDEFINED {
788
1.35k
    if((amount == 0) || U_FAILURE(status)) {
789
18
        return;
790
18
    }
791
792
    // J81 processing. (gregorian cutover)
793
1.33k
    UBool inCutoverMonth = false;
794
1.33k
    int32_t cMonthLen=0; // 'c' for cutover; in days
795
1.33k
    int32_t cDayOfMonth=0; // no discontinuity: [0, cMonthLen)
796
1.33k
    double cMonthStart=0.0; // in ms
797
798
    // Common code - see if we're in the cutover month of the cutover year
799
1.33k
    if(get(UCAL_EXTENDED_YEAR, status) == fGregorianCutoverYear) {
800
175
        switch (field) {
801
58
        case UCAL_DAY_OF_MONTH:
802
142
        case UCAL_WEEK_OF_MONTH:
803
142
            {
804
142
                int32_t max = monthLength(internalGetMonth(status), status);
805
142
                if (U_FAILURE(status)) {
806
0
                    return;
807
0
                }
808
142
                UDate t = internalGetTime();
809
                // We subtract 1 from the DAY_OF_MONTH to make it zero-based, and an
810
                // additional 10 if we are after the cutover. Thus the monthStart
811
                // value will be correct iff we actually are in the cutover month.
812
142
                cDayOfMonth = internalGet(UCAL_DAY_OF_MONTH) - ((t >= fGregorianCutover) ? 10 : 0);
813
142
                cMonthStart = t - ((cDayOfMonth - 1) * kOneDay);
814
                // A month containing the cutover is 10 days shorter.
815
142
                if ((cMonthStart < fGregorianCutover) &&
816
142
                    (cMonthStart + (cMonthLen=(max-10))*kOneDay >= fGregorianCutover)) {
817
106
                        inCutoverMonth = true;
818
106
                }
819
142
            }
820
0
            break;
821
33
        default:
822
33
            ;
823
175
        }
824
175
    }
825
826
1.33k
    switch (field) {
827
291
    case UCAL_WEEK_OF_YEAR: {
828
        // Unlike WEEK_OF_MONTH, WEEK_OF_YEAR never shifts the day of the
829
        // week.  Also, rolling the week of the year can have seemingly
830
        // strange effects simply because the year of the week of year
831
        // may be different from the calendar year.  For example, the
832
        // date Dec 28, 1997 is the first day of week 1 of 1998 (if
833
        // weeks start on Sunday and the minimal days in first week is
834
        // <= 3).
835
291
        int32_t woy = get(UCAL_WEEK_OF_YEAR, status);
836
        // Get the ISO year, which matches the week of year.  This
837
        // may be one year before or after the calendar year.
838
291
        int32_t isoYear = get(UCAL_YEAR_WOY, status);
839
291
        int32_t isoDoy = internalGet(UCAL_DAY_OF_YEAR);
840
291
        int32_t month = internalGetMonth(status);
841
291
        if (U_FAILURE(status)) {
842
40
            return;
843
40
        }
844
251
        if (month == UCAL_JANUARY) {
845
139
            if (woy >= 52) {
846
30
                isoDoy += handleGetYearLength(isoYear, status);
847
30
            }
848
139
        } else {
849
112
            if (woy == 1) {
850
20
                isoDoy -= handleGetYearLength(isoYear - 1, status);
851
20
            }
852
112
        }
853
251
        if (U_FAILURE(status)) return;
854
251
        if (uprv_add32_overflow(woy, amount, &woy)) {
855
34
            status = U_ILLEGAL_ARGUMENT_ERROR;
856
34
            return;
857
34
        }
858
        // Do fast checks to avoid unnecessary computation:
859
217
        if (woy < 1 || woy > 52) {
860
            // Determine the last week of the ISO year.
861
            // We do this using the standard formula we use
862
            // everywhere in this file.  If we can see that the
863
            // days at the end of the year are going to fall into
864
            // week 1 of the next year, we drop the last week by
865
            // subtracting 7 from the last day of the year.
866
194
            int32_t lastDoy = handleGetYearLength(isoYear, status);
867
194
            if (U_FAILURE(status)) return;
868
194
            int32_t lastRelDow = (lastDoy - isoDoy + internalGet(UCAL_DAY_OF_WEEK) -
869
194
                getFirstDayOfWeek()) % 7;
870
194
            if (lastRelDow < 0) lastRelDow += 7;
871
194
            if ((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) lastDoy -= 7;
872
194
            int32_t lastWoy = weekNumber(lastDoy, lastRelDow + 1);
873
194
            woy = ((woy + lastWoy - 1) % lastWoy) + 1;
874
194
        }
875
217
        set(UCAL_WEEK_OF_YEAR, woy);
876
217
        set(UCAL_YEAR_WOY,isoYear);
877
217
        return;
878
217
                            }
879
880
87
    case UCAL_DAY_OF_MONTH:
881
87
        if( !inCutoverMonth ) { 
882
51
            Calendar::roll(field, amount, status);
883
51
            return;
884
51
        }
885
36
        {
886
            // [j81] 1582 special case for DOM
887
            // The default computation works except when the current month
888
            // contains the Gregorian cutover.  We handle this special case
889
            // here.  [j81 - aliu]
890
36
            double monthLen = cMonthLen * kOneDay;
891
36
            double msIntoMonth = uprv_fmod(internalGetTime() - cMonthStart +
892
36
                amount * kOneDay, monthLen);
893
36
            if (msIntoMonth < 0) {
894
18
                msIntoMonth += monthLen;
895
18
            }
896
#if defined (U_DEBUG_CAL)
897
            fprintf(stderr, "%s:%d: roll DOM %d  -> %.0lf ms  \n", 
898
                __FILE__, __LINE__,amount, cMonthLen, cMonthStart+msIntoMonth);
899
#endif
900
36
            setTimeInMillis(cMonthStart + msIntoMonth, status);
901
36
            return;
902
87
        }
903
904
134
    case UCAL_WEEK_OF_MONTH:
905
134
        if( !inCutoverMonth ) { 
906
64
            Calendar::roll(field, amount, status);
907
64
            return;
908
64
        }
909
70
        {
910
#if defined (U_DEBUG_CAL)
911
            fprintf(stderr, "%s:%d: roll WOM %d ??????????????????? \n", 
912
                __FILE__, __LINE__,amount);
913
#endif
914
            // NOTE: following copied from  the old
915
            //     GregorianCalendar::roll( WEEK_OF_MONTH )  code 
916
917
            // This is tricky, because during the roll we may have to shift
918
            // to a different day of the week.  For example:
919
920
            //    s  m  t  w  r  f  s
921
            //          1  2  3  4  5
922
            //    6  7  8  9 10 11 12
923
924
            // When rolling from the 6th or 7th back one week, we go to the
925
            // 1st (assuming that the first partial week counts).  The same
926
            // thing happens at the end of the month.
927
928
            // The other tricky thing is that we have to figure out whether
929
            // the first partial week actually counts or not, based on the
930
            // minimal first days in the week.  And we have to use the
931
            // correct first day of the week to delineate the week
932
            // boundaries.
933
934
            // Here's our algorithm.  First, we find the real boundaries of
935
            // the month.  Then we discard the first partial week if it
936
            // doesn't count in this locale.  Then we fill in the ends with
937
            // phantom days, so that the first partial week and the last
938
            // partial week are full weeks.  We then have a nice square
939
            // block of weeks.  We do the usual rolling within this block,
940
            // as is done elsewhere in this method.  If we wind up on one of
941
            // the phantom days that we added, we recognize this and pin to
942
            // the first or the last day of the month.  Easy, eh?
943
944
            // Another wrinkle: To fix jitterbug 81, we have to make all this
945
            // work in the oddball month containing the Gregorian cutover.
946
            // This month is 10 days shorter than usual, and also contains
947
            // a discontinuity in the days; e.g., the default cutover month
948
            // is Oct 1582, and goes from day of month 4 to day of month 15.
949
950
            // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
951
            // in this locale.  We have dow in 0..6.
952
70
            int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
953
70
            if (dow < 0) 
954
35
                dow += 7;
955
956
            // Find the day of month, compensating for cutover discontinuity.
957
70
            int32_t dom = cDayOfMonth;
958
959
            // Find the day of the week (normalized for locale) for the first
960
            // of the month.
961
70
            int32_t fdm = (dow - dom + 1) % 7;
962
70
            if (fdm < 0) 
963
38
                fdm += 7;
964
965
            // Get the first day of the first full week of the month,
966
            // including phantom days, if any.  Figure out if the first week
967
            // counts or not; if it counts, then fill in phantom days.  If
968
            // not, advance to the first real full week (skip the partial week).
969
70
            int32_t start;
970
70
            if ((7 - fdm) < getMinimalDaysInFirstWeek())
971
10
                start = 8 - fdm; // Skip the first partial week
972
60
            else
973
60
                start = 1 - fdm; // This may be zero or negative
974
975
            // Get the day of the week (normalized for locale) for the last
976
            // day of the month.
977
70
            int32_t monthLen = cMonthLen;
978
70
            int32_t ldm = (monthLen - dom + dow) % 7;
979
            // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.
980
981
            // Get the limit day for the blocked-off rectangular month; that
982
            // is, the day which is one past the last day of the month,
983
            // after the month has already been filled in with phantom days
984
            // to fill out the last week.  This day has a normalized DOW of 0.
985
70
            int32_t limit = monthLen + 7 - ldm;
986
987
            // Now roll between start and (limit - 1).
988
70
            int32_t gap = limit - start;
989
70
            int32_t newDom = (dom + amount*7 - start) % gap;
990
70
            if (newDom < 0) 
991
35
                newDom += gap;
992
70
            newDom += start;
993
994
            // Finally, pin to the real start and end of the month.
995
70
            if (newDom < 1) 
996
19
                newDom = 1;
997
70
            if (newDom > monthLen) 
998
21
                newDom = monthLen;
999
1000
            // Set the DAY_OF_MONTH.  We rely on the fact that this field
1001
            // takes precedence over everything else (since all other fields
1002
            // are also set at this point).  If this fact changes (if the
1003
            // disambiguation algorithm changes) then we will have to unset
1004
            // the appropriate fields here so that DAY_OF_MONTH is attended
1005
            // to.
1006
1007
            // If we are in the cutover month, manipulate ms directly.  Don't do
1008
            // this in general because it doesn't work across DST boundaries
1009
            // (details, details).  This takes care of the discontinuity.
1010
70
            setTimeInMillis(cMonthStart + (newDom-1)*kOneDay, status);                
1011
70
            return;
1012
134
        }
1013
1014
825
    default:
1015
825
        Calendar::roll(field, amount, status);
1016
825
        return;
1017
1.33k
    }
1018
1.33k
}
1019
1020
// -------------------------------------
1021
1022
1023
/**
1024
* Return the minimum value that this field could have, given the current date.
1025
* For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum().
1026
* @param field    the time field.
1027
* @return         the minimum value that this field could have, given the current date.
1028
* @deprecated ICU 2.6. Use getActualMinimum(UCalendarDateFields field) instead.
1029
*/
1030
int32_t GregorianCalendar::getActualMinimum(EDateFields field) const
1031
0
{
1032
0
    return getMinimum(static_cast<UCalendarDateFields>(field));
1033
0
}
1034
1035
int32_t GregorianCalendar::getActualMinimum(EDateFields field, UErrorCode& /* status */) const
1036
0
{
1037
0
    return getMinimum(static_cast<UCalendarDateFields>(field));
1038
0
}
1039
1040
/**
1041
* Return the minimum value that this field could have, given the current date.
1042
* For the Gregorian calendar, this is the same as getMinimum() and getGreatestMinimum().
1043
* @param field    the time field.
1044
* @return         the minimum value that this field could have, given the current date.
1045
* @draft ICU 2.6.
1046
*/
1047
int32_t GregorianCalendar::getActualMinimum(UCalendarDateFields field, UErrorCode& /* status */) const
1048
414k
{
1049
414k
    return getMinimum(field);
1050
414k
}
1051
1052
1053
// ------------------------------------
1054
1055
/**
1056
* Old year limits were least max 292269054, max 292278994.
1057
*/
1058
1059
/**
1060
* @stable ICU 2.0
1061
*/
1062
1.07M
int32_t GregorianCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const {
1063
1.07M
    return kGregorianCalendarLimits[field][limitType];
1064
1.07M
}
1065
1066
/**
1067
* Return the maximum value that this field could have, given the current date.
1068
* For example, with the date "Feb 3, 1997" and the DAY_OF_MONTH field, the actual
1069
* maximum would be 28; for "Feb 3, 1996" it s 29.  Similarly for a Hebrew calendar,
1070
* for some years the actual maximum for MONTH is 12, and for others 13.
1071
* @stable ICU 2.0
1072
*/
1073
int32_t GregorianCalendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const
1074
414k
{
1075
    /* It is a known limitation that the code here (and in getActualMinimum)
1076
    * won't behave properly at the extreme limits of GregorianCalendar's
1077
    * representable range (except for the code that handles the YEAR
1078
    * field).  That's because the ends of the representable range are at
1079
    * odd spots in the year.  For calendars with the default Gregorian
1080
    * cutover, these limits are Sun Dec 02 16:47:04 GMT 292269055 BC to Sun
1081
    * Aug 17 07:12:55 GMT 292278994 AD, somewhat different for non-GMT
1082
    * zones.  As a result, if the calendar is set to Aug 1 292278994 AD,
1083
    * the actual maximum of DAY_OF_MONTH is 17, not 30.  If the date is Mar
1084
    * 31 in that year, the actual maximum month might be Jul, whereas is
1085
    * the date is Mar 15, the actual maximum might be Aug -- depending on
1086
    * the precise semantics that are desired.  Similar considerations
1087
    * affect all fields.  Nonetheless, this effect is sufficiently arcane
1088
    * that we permit it, rather than complicating the code to handle such
1089
    * intricacies. - liu 8/20/98
1090
1091
    * UPDATE: No longer true, since we have pulled in the limit values on
1092
    * the year. - Liu 11/6/00 */
1093
1094
414k
    switch (field) {
1095
1096
56
    case UCAL_YEAR:
1097
        /* The year computation is no different, in principle, from the
1098
        * others, however, the range of possible maxima is large.  In
1099
        * addition, the way we know we've exceeded the range is different.
1100
        * For these reasons, we use the special case code below to handle
1101
        * this field.
1102
        *
1103
        * The actual maxima for YEAR depend on the type of calendar:
1104
        *
1105
        *     Gregorian = May 17, 292275056 BC - Aug 17, 292278994 AD
1106
        *     Julian    = Dec  2, 292269055 BC - Jan  3, 292272993 AD
1107
        *     Hybrid    = Dec  2, 292269055 BC - Aug 17, 292278994 AD
1108
        *
1109
        * We know we've exceeded the maximum when either the month, date,
1110
        * time, or era changes in response to setting the year.  We don't
1111
        * check for month, date, and time here because the year and era are
1112
        * sufficient to detect an invalid year setting.  NOTE: If code is
1113
        * added to check the month and date in the future for some reason,
1114
        * Feb 29 must be allowed to shift to Mar 1 when setting the year.
1115
        */
1116
56
        {
1117
56
            if(U_FAILURE(status)) return 0;
1118
56
            Calendar *cal = clone();
1119
56
            if(!cal) {
1120
0
                status = U_MEMORY_ALLOCATION_ERROR;
1121
0
                return 0;
1122
0
            }
1123
1124
56
            cal->setLenient(true);
1125
1126
56
            int32_t era = cal->get(UCAL_ERA, status);
1127
56
            UDate d = cal->getTime(status);
1128
1129
            /* Perform a binary search, with the invariant that lowGood is a
1130
            * valid year, and highBad is an out of range year.
1131
            */
1132
56
            int32_t lowGood = kGregorianCalendarLimits[UCAL_YEAR][1];
1133
56
            int32_t highBad = kGregorianCalendarLimits[UCAL_YEAR][2]+1;
1134
1.06k
            while ((lowGood + 1) < highBad) {
1135
1.00k
                int32_t y = (lowGood + highBad) / 2;
1136
1.00k
                cal->set(UCAL_YEAR, y);
1137
1.00k
                if (cal->get(UCAL_YEAR, status) == y && cal->get(UCAL_ERA, status) == era) {
1138
954
                    lowGood = y;
1139
954
                } else {
1140
51
                    highBad = y;
1141
51
                    cal->setTime(d, status); // Restore original fields
1142
51
                }
1143
1.00k
            }
1144
1145
56
            delete cal;
1146
56
            return lowGood;
1147
56
        }
1148
1149
414k
    default:
1150
414k
        return Calendar::getActualMaximum(field,status);
1151
414k
    }
1152
414k
}
1153
1154
1155
319k
int32_t GregorianCalendar::handleGetExtendedYear(UErrorCode& status) {
1156
319k
    if (U_FAILURE(status)) {
1157
0
        return 0;
1158
0
    }
1159
    // the year to return
1160
319k
    int32_t year = kEpochYear;
1161
1162
    // year field to use
1163
    // There are three separate fields which could be used to
1164
    // derive the proper year.  Use the one most recently set.
1165
319k
    UCalendarDateFields yearField = newerField(
1166
319k
        newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR), UCAL_YEAR_WOY);
1167
1168
    // based on the "best" year field, get the year
1169
319k
    switch(yearField) {
1170
4.33k
    case UCAL_EXTENDED_YEAR:
1171
4.33k
        year = internalGet(UCAL_EXTENDED_YEAR, kEpochYear);
1172
4.33k
        break;
1173
1174
5.38k
    case UCAL_YEAR:
1175
5.38k
        {
1176
            // The year defaults to the epoch start, the era to AD
1177
5.38k
            int32_t era = internalGet(UCAL_ERA, AD);
1178
5.38k
            if (era == BC) {
1179
2.77k
                year = 1 - internalGet(UCAL_YEAR, 1); // Convert to extended year
1180
2.77k
            } else if (era == AD) {
1181
2.55k
                year = internalGet(UCAL_YEAR, kEpochYear);
1182
2.55k
            } else {
1183
52
                status = U_ILLEGAL_ARGUMENT_ERROR;
1184
52
                return 0;
1185
52
            }
1186
5.38k
        }
1187
5.33k
        break;
1188
1189
310k
    case UCAL_YEAR_WOY:
1190
310k
        year = handleGetExtendedYearFromWeekFields(
1191
310k
            internalGet(UCAL_YEAR_WOY), internalGet(UCAL_WEEK_OF_YEAR), status);
1192
310k
        if (U_FAILURE(status)) {
1193
21
            return 0;
1194
21
        }
1195
#if defined (U_DEBUG_CAL)
1196
        //    if(internalGet(UCAL_YEAR_WOY) != year) {
1197
        fprintf(stderr, "%s:%d: hGEYFWF[%d,%d] ->  %d\n", 
1198
            __FILE__, __LINE__,internalGet(UCAL_YEAR_WOY),internalGet(UCAL_WEEK_OF_YEAR),year);
1199
        //}
1200
#endif
1201
310k
        break;
1202
1203
310k
    default:
1204
0
        year = kEpochYear;
1205
319k
    }
1206
319k
    return year;
1207
319k
}
1208
1209
int32_t GregorianCalendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy, UErrorCode& status)
1210
310k
{
1211
310k
    if (U_FAILURE(status)) {
1212
0
        return 0;
1213
0
    }
1214
    // convert year to extended form
1215
310k
    int32_t era = internalGet(UCAL_ERA, AD);
1216
310k
    if(era == BC) {
1217
415
        yearWoy = 1 - yearWoy;
1218
415
    }
1219
310k
    return Calendar::handleGetExtendedYearFromWeekFields(yearWoy, woy, status);
1220
310k
}
1221
1222
1223
// -------------------------------------
1224
1225
/**
1226
* Return the ERA.  We need a special method for this because the
1227
* default ERA is AD, but a zero (unset) ERA is BC.
1228
*/
1229
int32_t
1230
0
GregorianCalendar::internalGetEra() const {
1231
0
    return isSet(UCAL_ERA) ? internalGet(UCAL_ERA) : static_cast<int32_t>(AD);
1232
0
}
1233
1234
const char *
1235
83.9k
GregorianCalendar::getType() const {
1236
    //static const char kGregorianType = "gregorian";
1237
1238
83.9k
    return "gregorian";
1239
83.9k
}
1240
1241
IMPL_SYSTEM_DEFAULT_CENTURY(GregorianCalendar, "@calendar=gregory")
1242
1243
U_NAMESPACE_END
1244
1245
#endif /* #if !UCONFIG_NO_FORMATTING */
1246
1247
//eof