Coverage Report

Created: 2025-06-13 06:34

/src/icu/icu4c/source/i18n/calendar.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 CALENDAR.CPP
10
*
11
* Modification History:
12
*
13
*   Date        Name        Description
14
*   02/03/97    clhuang     Creation.
15
*   04/22/97    aliu        Cleaned up, fixed memory leak, made
16
*                           setWeekCountData() more robust.
17
*                           Moved platform code to TPlatformUtilities.
18
*   05/01/97    aliu        Made equals(), before(), after() arguments const.
19
*   05/20/97    aliu        Changed logic of when to compute fields and time
20
*                           to fix bugs.
21
*   08/12/97    aliu        Added equivalentTo.  Misc other fixes.
22
*   07/28/98    stephen     Sync up with JDK 1.2
23
*   09/02/98    stephen     Sync with JDK 1.2 8/31 build (getActualMin/Max)
24
*   03/17/99    stephen     Changed adoptTimeZone() - now fAreFieldsSet is
25
*                           set to false to force update of time.
26
*******************************************************************************
27
*/
28
29
#include "utypeinfo.h"  // for 'typeid' to work
30
31
#include "unicode/utypes.h"
32
33
#if !UCONFIG_NO_FORMATTING
34
35
#include "unicode/gregocal.h"
36
#include "unicode/basictz.h"
37
#include "unicode/simpletz.h"
38
#include "unicode/rbtz.h"
39
#include "unicode/vtzone.h"
40
#include "gregoimp.h"
41
#include "buddhcal.h"
42
#include "taiwncal.h"
43
#include "japancal.h"
44
#include "islamcal.h"
45
#include "hebrwcal.h"
46
#include "persncal.h"
47
#include "indiancal.h"
48
#include "iso8601cal.h"
49
#include "chnsecal.h"
50
#include "coptccal.h"
51
#include "dangical.h"
52
#include "ethpccal.h"
53
#include "unicode/calendar.h"
54
#include "cpputils.h"
55
#include "servloc.h"
56
#include "ucln_in.h"
57
#include "cstring.h"
58
#include "locbased.h"
59
#include "uresimp.h"
60
#include "ustrenum.h"
61
#include "uassert.h"
62
#include "olsontz.h"
63
#include "sharedcalendar.h"
64
#include "unifiedcache.h"
65
#include "ulocimp.h"
66
#include "charstr.h"
67
68
#if !UCONFIG_NO_SERVICE
69
static icu::ICULocaleService* gService = nullptr;
70
static icu::UInitOnce gServiceInitOnce {};
71
72
// INTERNAL - for cleanup
73
U_CDECL_BEGIN
74
0
static UBool calendar_cleanup() {
75
0
#if !UCONFIG_NO_SERVICE
76
0
    if (gService) {
77
0
        delete gService;
78
0
        gService = nullptr;
79
0
    }
80
0
    gServiceInitOnce.reset();
81
0
#endif
82
0
    return true;
83
0
}
84
U_CDECL_END
85
#endif
86
87
// ------------------------------------------
88
//
89
// Registration
90
//
91
//-------------------------------------------
92
//#define U_DEBUG_CALSVC 1
93
//
94
95
#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
96
97
/**
98
 * fldName was removed as a duplicate implementation.
99
 * use  udbg_ services instead,
100
 * which depend on include files and library from ../tools/toolutil, the following circular link:
101
 *   CPPFLAGS+=-I$(top_srcdir)/tools/toolutil
102
 *   LIBS+=$(LIBICUTOOLUTIL)
103
 */
104
#include "udbgutil.h"
105
#include <stdio.h>
106
107
/**
108
* convert a UCalendarDateFields into a string - for debugging
109
* @param f field enum
110
* @return static string to the field name
111
* @internal
112
*/
113
114
const char* fldName(UCalendarDateFields f) {
115
    return udbg_enumName(UDBG_UCalendarDateFields, (int32_t)f);
116
}
117
118
#if UCAL_DEBUG_DUMP
119
// from CalendarTest::calToStr - but doesn't modify contents.
120
void ucal_dump(const Calendar &cal) {
121
    cal.dump();
122
}
123
124
void Calendar::dump() const {
125
    int i;
126
    fprintf(stderr, "@calendar=%s, timeset=%c, fieldset=%c, allfields=%c, virtualset=%c, t=%.2f",
127
        getType(), fIsTimeSet?'y':'n',  fAreFieldsSet?'y':'n',  fAreAllFieldsSet?'y':'n',
128
        fAreFieldsVirtuallySet?'y':'n',
129
        fTime);
130
131
    // can add more things here: DST, zone, etc.
132
    fprintf(stderr, "\n");
133
    for(i = 0;i<UCAL_FIELD_COUNT;i++) {
134
        int n;
135
        const char *f = fldName((UCalendarDateFields)i);
136
        fprintf(stderr, "  %25s: %-11ld", f, fFields[i]);
137
        if(fStamp[i] == kUnset) {
138
            fprintf(stderr, " (unset) ");
139
        } else if(fStamp[i] == kInternallySet) {
140
            fprintf(stderr, " (internally set) ");
141
            //} else if(fStamp[i] == kInternalDefault) {
142
            //    fprintf(stderr, " (internal default) ");
143
        } else {
144
            fprintf(stderr, " %%%d ", fStamp[i]);
145
        }
146
        fprintf(stderr, "\n");
147
148
    }
149
}
150
151
U_CFUNC void ucal_dump(UCalendar* cal) {
152
    ucal_dump( *((Calendar*)cal)  );
153
}
154
#endif
155
156
#endif
157
158
/* Max value for stamp allowable before recalculation */
159
0
#define STAMP_MAX 127
160
161
static const char * const gCalTypes[] = {
162
    "gregorian",
163
    "japanese",
164
    "buddhist",
165
    "roc",
166
    "persian",
167
    "islamic-civil",
168
    "islamic",
169
    "hebrew",
170
    "chinese",
171
    "indian",
172
    "coptic",
173
    "ethiopic",
174
    "ethiopic-amete-alem",
175
    "iso8601",
176
    "dangi",
177
    "islamic-umalqura",
178
    "islamic-tbla",
179
    "islamic-rgsa",
180
    nullptr
181
};
182
183
// Must be in the order of gCalTypes above
184
typedef enum ECalType {
185
    CALTYPE_UNKNOWN = -1,
186
    CALTYPE_GREGORIAN = 0,
187
    CALTYPE_JAPANESE,
188
    CALTYPE_BUDDHIST,
189
    CALTYPE_ROC,
190
    CALTYPE_PERSIAN,
191
    CALTYPE_ISLAMIC_CIVIL,
192
    CALTYPE_ISLAMIC,
193
    CALTYPE_HEBREW,
194
    CALTYPE_CHINESE,
195
    CALTYPE_INDIAN,
196
    CALTYPE_COPTIC,
197
    CALTYPE_ETHIOPIC,
198
    CALTYPE_ETHIOPIC_AMETE_ALEM,
199
    CALTYPE_ISO8601,
200
    CALTYPE_DANGI,
201
    CALTYPE_ISLAMIC_UMALQURA,
202
    CALTYPE_ISLAMIC_TBLA,
203
    CALTYPE_ISLAMIC_RGSA
204
} ECalType;
205
206
U_NAMESPACE_BEGIN
207
208
0
SharedCalendar::~SharedCalendar() {
209
0
    delete ptr;
210
0
}
211
212
template<> U_I18N_API
213
const SharedCalendar *LocaleCacheKey<SharedCalendar>::createObject(
214
0
        const void * /*unusedCreationContext*/, UErrorCode &status) const {
215
0
    if (U_FAILURE(status)) {
216
0
       return nullptr;
217
0
    }
218
0
    Calendar *calendar = Calendar::makeInstance(fLoc, status);
219
0
    if (U_FAILURE(status)) {
220
0
        return nullptr;
221
0
    }
222
0
    SharedCalendar *shared = new SharedCalendar(calendar);
223
0
    if (shared == nullptr) {
224
0
        delete calendar;
225
0
        status = U_MEMORY_ALLOCATION_ERROR;
226
0
        return nullptr;
227
0
    }
228
0
    shared->addRef();
229
0
    return shared;
230
0
}
231
232
0
static ECalType getCalendarType(const char *s) {
233
0
    for (int i = 0; gCalTypes[i] != nullptr; i++) {
234
0
        if (uprv_stricmp(s, gCalTypes[i]) == 0) {
235
0
            return static_cast<ECalType>(i);
236
0
        }
237
0
    }
238
0
    return CALTYPE_UNKNOWN;
239
0
}
240
241
#if !UCONFIG_NO_SERVICE
242
// Only used with service registration.
243
0
static UBool isStandardSupportedKeyword(const char *keyword, UErrorCode& status) {
244
0
    if(U_FAILURE(status)) {
245
0
        return false;
246
0
    }
247
0
    ECalType calType = getCalendarType(keyword);
248
0
    return (calType != CALTYPE_UNKNOWN);
249
0
}
250
251
#endif
252
253
0
static ECalType getCalendarTypeForLocale(const char *locid) {
254
0
    UErrorCode status = U_ZERO_ERROR;
255
0
    ECalType calType = CALTYPE_UNKNOWN;
256
257
    // Canonicalize, so that an old-style variant will be transformed to keywords.
258
    // e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese
259
    // NOTE: Since ICU-20187, ja_JP_TRADITIONAL no longer canonicalizes, and
260
    // the Gregorian calendar is returned instead.
261
0
    CharString canonicalName = ulocimp_canonicalize(locid, status);
262
0
    if (U_FAILURE(status)) {
263
0
        return CALTYPE_GREGORIAN;
264
0
    }
265
266
0
    CharString calTypeBuf = ulocimp_getKeywordValue(canonicalName.data(), "calendar", status);
267
0
    if (U_SUCCESS(status)) {
268
0
        calType = getCalendarType(calTypeBuf.data());
269
0
        if (calType != CALTYPE_UNKNOWN) {
270
0
            return calType;
271
0
        }
272
0
    }
273
0
    status = U_ZERO_ERROR;
274
275
    // when calendar keyword is not available or not supported, read supplementalData
276
    // to get the default calendar type for the locale's region
277
0
    CharString region = ulocimp_getRegionForSupplementalData(canonicalName.data(), true, status);
278
0
    if (U_FAILURE(status)) {
279
0
        return CALTYPE_GREGORIAN;
280
0
    }
281
282
    // Read preferred calendar values from supplementalData calendarPreference
283
0
    UResourceBundle *rb = ures_openDirect(nullptr, "supplementalData", &status);
284
0
    ures_getByKey(rb, "calendarPreferenceData", rb, &status);
285
0
    UResourceBundle *order = ures_getByKey(rb, region.data(), nullptr, &status);
286
0
    if (status == U_MISSING_RESOURCE_ERROR && rb != nullptr) {
287
0
        status = U_ZERO_ERROR;
288
0
        order = ures_getByKey(rb, "001", nullptr, &status);
289
0
    }
290
291
0
    calTypeBuf.clear();
292
0
    if (U_SUCCESS(status) && order != nullptr) {
293
        // the first calendar type is the default for the region
294
0
        int32_t len = 0;
295
0
        const char16_t *uCalType = ures_getStringByIndex(order, 0, &len, &status);
296
0
        calTypeBuf.appendInvariantChars(uCalType, len, status);
297
0
        calType = getCalendarType(calTypeBuf.data());
298
0
    }
299
300
0
    ures_close(order);
301
0
    ures_close(rb);
302
303
0
    if (calType == CALTYPE_UNKNOWN) {
304
        // final fallback
305
0
        calType = CALTYPE_GREGORIAN;
306
0
    }
307
0
    return calType;
308
0
}
309
310
0
static Calendar *createStandardCalendar(ECalType calType, const Locale &loc, UErrorCode& status) {
311
0
    if (U_FAILURE(status)) {
312
0
        return nullptr;
313
0
    }
314
0
    LocalPointer<Calendar> cal;
315
316
0
    switch (calType) {
317
0
        case CALTYPE_GREGORIAN:
318
0
            cal.adoptInsteadAndCheckErrorCode(new GregorianCalendar(loc, status), status);
319
0
            break;
320
0
        case CALTYPE_JAPANESE:
321
0
            cal.adoptInsteadAndCheckErrorCode(new JapaneseCalendar(loc, status), status);
322
0
            break;
323
0
        case CALTYPE_BUDDHIST:
324
0
            cal.adoptInsteadAndCheckErrorCode(new BuddhistCalendar(loc, status), status);
325
0
            break;
326
0
        case CALTYPE_ROC:
327
0
            cal.adoptInsteadAndCheckErrorCode(new TaiwanCalendar(loc, status), status);
328
0
            break;
329
0
        case CALTYPE_PERSIAN:
330
0
            cal.adoptInsteadAndCheckErrorCode(new PersianCalendar(loc, status), status);
331
0
            break;
332
0
        case CALTYPE_ISLAMIC_TBLA:
333
0
            cal.adoptInsteadAndCheckErrorCode(new IslamicTBLACalendar(loc, status), status);
334
0
            break;
335
0
        case CALTYPE_ISLAMIC_CIVIL:
336
0
            cal.adoptInsteadAndCheckErrorCode(new IslamicCivilCalendar(loc, status), status);
337
0
            break;
338
0
        case CALTYPE_ISLAMIC_RGSA:
339
0
            cal.adoptInsteadAndCheckErrorCode(new IslamicRGSACalendar(loc, status), status);
340
0
            break;
341
0
        case CALTYPE_ISLAMIC:
342
0
            cal.adoptInsteadAndCheckErrorCode(new IslamicCalendar(loc, status), status);
343
0
            break;
344
0
        case CALTYPE_ISLAMIC_UMALQURA:
345
0
            cal.adoptInsteadAndCheckErrorCode(new IslamicUmalquraCalendar(loc, status), status);
346
0
            break;
347
0
        case CALTYPE_HEBREW:
348
0
            cal.adoptInsteadAndCheckErrorCode(new HebrewCalendar(loc, status), status);
349
0
            break;
350
0
        case CALTYPE_CHINESE:
351
0
            cal.adoptInsteadAndCheckErrorCode(new ChineseCalendar(loc, status), status);
352
0
            break;
353
0
        case CALTYPE_INDIAN:
354
0
            cal.adoptInsteadAndCheckErrorCode(new IndianCalendar(loc, status), status);
355
0
            break;
356
0
        case CALTYPE_COPTIC:
357
0
            cal.adoptInsteadAndCheckErrorCode(new CopticCalendar(loc, status), status);
358
0
            break;
359
0
        case CALTYPE_ETHIOPIC:
360
0
            cal.adoptInsteadAndCheckErrorCode(new EthiopicCalendar(loc, status), status);
361
0
            break;
362
0
        case CALTYPE_ETHIOPIC_AMETE_ALEM:
363
0
            cal.adoptInsteadAndCheckErrorCode(new EthiopicAmeteAlemCalendar(loc, status), status);
364
0
            break;
365
0
        case CALTYPE_ISO8601:
366
0
            cal.adoptInsteadAndCheckErrorCode(new ISO8601Calendar(loc, status), status);
367
0
            break;
368
0
        case CALTYPE_DANGI:
369
0
            cal.adoptInsteadAndCheckErrorCode(new DangiCalendar(loc, status), status);
370
0
            break;
371
0
        default:
372
0
            status = U_UNSUPPORTED_ERROR;
373
0
    }
374
0
    return cal.orphan();
375
0
}
376
377
378
#if !UCONFIG_NO_SERVICE
379
380
// -------------------------------------
381
382
/**
383
* a Calendar Factory which creates the "basic" calendar types, that is, those
384
* shipped with ICU.
385
*/
386
class BasicCalendarFactory : public LocaleKeyFactory {
387
public:
388
    /**
389
    * @param calendarType static const string (caller owns storage - will be aliased) to calendar type
390
    */
391
    BasicCalendarFactory()
392
0
        : LocaleKeyFactory(LocaleKeyFactory::INVISIBLE) { }
393
394
    virtual ~BasicCalendarFactory();
395
396
protected:
397
    //virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const {
398
    //  if(U_FAILURE(status)) {
399
    //    return false;
400
    //  }
401
    //  char keyword[ULOC_FULLNAME_CAPACITY];
402
    //  getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword));
403
    //  return isStandardSupportedKeyword(keyword, status);
404
    //}
405
406
    virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const override
407
0
    {
408
0
        if (U_SUCCESS(status)) {
409
0
            for(int32_t i=0;gCalTypes[i] != nullptr;i++) {
410
0
                UnicodeString id(static_cast<char16_t>(0x40)); /* '@' a variant character */
411
0
                id.append(UNICODE_STRING_SIMPLE("calendar="));
412
0
                id.append(UnicodeString(gCalTypes[i], -1, US_INV));
413
0
                result.put(id, (void*)this, status);
414
0
            }
415
0
        }
416
0
    }
417
418
0
    virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const override {
419
0
        if (U_FAILURE(status)) {
420
0
           return nullptr;
421
0
        }
422
#ifdef U_DEBUG_CALSVC
423
        if(dynamic_cast<const LocaleKey*>(&key) == nullptr) {
424
            fprintf(stderr, "::create - not a LocaleKey!\n");
425
        }
426
#endif
427
0
        const LocaleKey* lkey = dynamic_cast<const LocaleKey*>(&key);
428
0
        U_ASSERT(lkey != nullptr);
429
0
        Locale curLoc;  // current locale
430
0
        Locale canLoc;  // Canonical locale
431
432
0
        lkey->currentLocale(curLoc);
433
0
        lkey->canonicalLocale(canLoc);
434
435
0
        char keyword[ULOC_FULLNAME_CAPACITY];
436
0
        curLoc.getKeywordValue("calendar", keyword, static_cast<int32_t>(sizeof(keyword)), status);
437
438
#ifdef U_DEBUG_CALSVC
439
        fprintf(stderr, "BasicCalendarFactory::create() - cur %s, can %s\n", (const char*)curLoc.getName(), (const char*)canLoc.getName());
440
#endif
441
442
0
        if(!isStandardSupportedKeyword(keyword,status)) {  // Do we handle this type?
443
#ifdef U_DEBUG_CALSVC
444
445
            fprintf(stderr, "BasicCalendarFactory - not handling %s.[%s]\n", (const char*) curLoc.getName(), tmp );
446
#endif
447
0
            return nullptr;
448
0
        }
449
450
0
        return createStandardCalendar(getCalendarType(keyword), canLoc, status);
451
0
    }
452
};
453
454
0
BasicCalendarFactory::~BasicCalendarFactory() {}
455
456
/**
457
* A factory which looks up the DefaultCalendar resource to determine which class of calendar to use
458
*/
459
460
class DefaultCalendarFactory : public ICUResourceBundleFactory {
461
public:
462
0
    DefaultCalendarFactory() : ICUResourceBundleFactory() { }
463
    virtual ~DefaultCalendarFactory();
464
protected:
465
0
    virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const override {
466
0
        if (U_FAILURE(status)) {
467
0
           return nullptr;
468
0
        }
469
470
0
        const LocaleKey *lkey = dynamic_cast<const LocaleKey*>(&key);
471
0
        U_ASSERT(lkey != nullptr);
472
0
        Locale loc;
473
0
        lkey->currentLocale(loc);
474
475
0
        UnicodeString *ret = new UnicodeString();
476
0
        if (ret == nullptr) {
477
0
            status = U_MEMORY_ALLOCATION_ERROR;
478
0
        } else {
479
0
            ret->append(static_cast<char16_t>(0x40)); // '@' is a variant character
480
0
            ret->append(UNICODE_STRING("calendar=", 9));
481
0
            ret->append(UnicodeString(gCalTypes[getCalendarTypeForLocale(loc.getName())], -1, US_INV));
482
0
        }
483
0
        return ret;
484
0
    }
485
};
486
487
0
DefaultCalendarFactory::~DefaultCalendarFactory() {}
488
489
// -------------------------------------
490
class CalendarService : public ICULocaleService {
491
public:
492
    CalendarService()
493
0
        : ICULocaleService(UNICODE_STRING_SIMPLE("Calendar"))
494
0
    {
495
0
        UErrorCode status = U_ZERO_ERROR;
496
0
        registerFactory(new DefaultCalendarFactory(), status);
497
0
    }
498
499
    virtual ~CalendarService();
500
501
0
    virtual UObject* cloneInstance(UObject* instance) const override {
502
0
        UnicodeString *s = dynamic_cast<UnicodeString *>(instance);
503
0
        if(s != nullptr) {
504
0
            return s->clone();
505
0
        } else {
506
#ifdef U_DEBUG_CALSVC_F
507
            UErrorCode status2 = U_ZERO_ERROR;
508
            fprintf(stderr, "Cloning a %s calendar with tz=%ld\n", ((Calendar*)instance)->getType(), ((Calendar*)instance)->get(UCAL_ZONE_OFFSET, status2));
509
#endif
510
0
            return ((Calendar*)instance)->clone();
511
0
        }
512
0
    }
513
514
0
    virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /*actualID*/, UErrorCode& status) const override {
515
0
        if (U_FAILURE(status)) {
516
0
           return nullptr;
517
0
        }
518
0
        LocaleKey& lkey = static_cast<LocaleKey&>(const_cast<ICUServiceKey&>(key));
519
        //int32_t kind = lkey.kind();
520
521
0
        Locale loc;
522
0
        lkey.canonicalLocale(loc);
523
524
#ifdef U_DEBUG_CALSVC
525
        Locale loc2;
526
        lkey.currentLocale(loc2);
527
        fprintf(stderr, "CalSvc:handleDefault for currentLoc %s, canloc %s\n", (const char*)loc.getName(),  (const char*)loc2.getName());
528
#endif
529
0
        Calendar *nc =  new GregorianCalendar(loc, status);
530
0
        if (nc == nullptr) {
531
0
            status = U_MEMORY_ALLOCATION_ERROR;
532
0
            return nc;
533
0
        }
534
535
#ifdef U_DEBUG_CALSVC
536
        UErrorCode status2 = U_ZERO_ERROR;
537
        fprintf(stderr, "New default calendar has tz=%d\n", ((Calendar*)nc)->get(UCAL_ZONE_OFFSET, status2));
538
#endif
539
0
        return nc;
540
0
    }
541
542
0
    virtual UBool isDefault() const override {
543
0
        return countFactories() == 1;
544
0
    }
545
};
546
547
0
CalendarService::~CalendarService() {}
548
549
// -------------------------------------
550
551
static inline UBool
552
0
isCalendarServiceUsed() {
553
0
    return !gServiceInitOnce.isReset();
554
0
}
555
556
// -------------------------------------
557
558
static void U_CALLCONV
559
initCalendarService(UErrorCode &status)
560
0
{
561
#ifdef U_DEBUG_CALSVC
562
        fprintf(stderr, "Spinning up Calendar Service\n");
563
#endif
564
0
    if (U_FAILURE(status)) {
565
0
       return;
566
0
    }
567
0
    ucln_i18n_registerCleanup(UCLN_I18N_CALENDAR, calendar_cleanup);
568
0
    gService = new CalendarService();
569
0
    if (gService == nullptr) {
570
0
            status = U_MEMORY_ALLOCATION_ERROR;
571
0
        return;
572
0
        }
573
#ifdef U_DEBUG_CALSVC
574
        fprintf(stderr, "Registering classes..\n");
575
#endif
576
577
        // Register all basic instances.
578
0
    gService->registerFactory(new BasicCalendarFactory(),status);
579
580
#ifdef U_DEBUG_CALSVC
581
        fprintf(stderr, "Done..\n");
582
#endif
583
584
0
        if(U_FAILURE(status)) {
585
#ifdef U_DEBUG_CALSVC
586
            fprintf(stderr, "err (%s) registering classes, deleting service.....\n", u_errorName(status));
587
#endif
588
0
        delete gService;
589
0
        gService = nullptr;
590
0
    }
591
0
        }
592
593
static ICULocaleService*
594
getCalendarService(UErrorCode &status)
595
0
{
596
0
    umtx_initOnce(gServiceInitOnce, &initCalendarService, status);
597
0
    return gService;
598
0
}
599
600
URegistryKey Calendar::registerFactory(ICUServiceFactory* toAdopt, UErrorCode& status)
601
0
{
602
0
    return getCalendarService(status)->registerFactory(toAdopt, status);
603
0
}
604
605
0
UBool Calendar::unregister(URegistryKey key, UErrorCode& status) {
606
0
    return getCalendarService(status)->unregister(key, status);
607
0
}
608
#endif /* UCONFIG_NO_SERVICE */
609
610
// -------------------------------------
611
612
static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = {
613
    //    Minimum  Greatest min      Least max   Greatest max
614
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // ERA
615
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // YEAR
616
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // MONTH
617
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // WEEK_OF_YEAR
618
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // WEEK_OF_MONTH
619
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // DAY_OF_MONTH
620
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // DAY_OF_YEAR
621
    {           1,            1,             7,             7  }, // DAY_OF_WEEK
622
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // DAY_OF_WEEK_IN_MONTH
623
    {           0,            0,             1,             1  }, // AM_PM
624
    {           0,            0,            11,            11  }, // HOUR
625
    {           0,            0,            23,            23  }, // HOUR_OF_DAY
626
    {           0,            0,            59,            59  }, // MINUTE
627
    {           0,            0,            59,            59  }, // SECOND
628
    {           0,            0,           999,           999  }, // MILLISECOND
629
    {-24*kOneHour, -16*kOneHour,   12*kOneHour,   30*kOneHour  }, // ZONE_OFFSET
630
    { -1*kOneHour,  -1*kOneHour,    2*kOneHour,    2*kOneHour  }, // DST_OFFSET
631
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // YEAR_WOY
632
    {           1,            1,             7,             7  }, // DOW_LOCAL
633
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // EXTENDED_YEAR
634
    { -0x7F000000,  -0x7F000000,    0x7F000000,    0x7F000000  }, // JULIAN_DAY
635
    {           0,            0, 24*kOneHour-1, 24*kOneHour-1  }, // MILLISECONDS_IN_DAY
636
    {           0,            0,             1,             1  }, // IS_LEAP_MONTH
637
    {           0,            0,            11,            11  }  // ORDINAL_MONTH
638
};
639
640
// Resource bundle tags read by this class
641
static const char gCalendar[] = "calendar";
642
static const char gMonthNames[] = "monthNames";
643
static const char gGregorian[] = "gregorian";
644
645
// Data flow in Calendar
646
// ---------------------
647
648
// The current time is represented in two ways by Calendar: as UTC
649
// milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local
650
// fields such as MONTH, HOUR, AM_PM, etc.  It is possible to compute the
651
// millis from the fields, and vice versa.  The data needed to do this
652
// conversion is encapsulated by a TimeZone object owned by the Calendar.
653
// The data provided by the TimeZone object may also be overridden if the
654
// user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class
655
// keeps track of what information was most recently set by the caller, and
656
// uses that to compute any other information as needed.
657
658
// If the user sets the fields using set(), the data flow is as follows.
659
// This is implemented by the Calendar subclass's computeTime() method.
660
// During this process, certain fields may be ignored.  The disambiguation
661
// algorithm for resolving which fields to pay attention to is described
662
// above.
663
664
//   local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
665
//           |
666
//           | Using Calendar-specific algorithm
667
//           V
668
//   local standard millis
669
//           |
670
//           | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET
671
//           V
672
//   UTC millis (in time data member)
673
674
// If the user sets the UTC millis using setTime(), the data flow is as
675
// follows.  This is implemented by the Calendar subclass's computeFields()
676
// method.
677
678
//   UTC millis (in time data member)
679
//           |
680
//           | Using TimeZone getOffset()
681
//           V
682
//   local standard millis
683
//           |
684
//           | Using Calendar-specific algorithm
685
//           V
686
//   local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
687
688
// In general, a round trip from fields, through local and UTC millis, and
689
// back out to fields is made when necessary.  This is implemented by the
690
// complete() method.  Resolving a partial set of fields into a UTC millis
691
// value allows all remaining fields to be generated from that value.  If
692
// the Calendar is lenient, the fields are also renormalized to standard
693
// ranges when they are regenerated.
694
695
// -------------------------------------
696
697
Calendar::Calendar(UErrorCode& success)
698
0
:   UObject(),
699
0
fIsTimeSet(false),
700
0
fAreFieldsSet(false),
701
0
fAreAllFieldsSet(false),
702
0
fAreFieldsVirtuallySet(false),
703
0
fLenient(true),
704
0
fRepeatedWallTime(UCAL_WALLTIME_LAST),
705
0
fSkippedWallTime(UCAL_WALLTIME_LAST)
706
0
{
707
0
    clear();
708
0
    if (U_FAILURE(success)) {
709
0
        return;
710
0
    }
711
0
    fZone = TimeZone::createDefault();
712
0
    if (fZone == nullptr) {
713
0
        success = U_MEMORY_ALLOCATION_ERROR;
714
0
    }
715
0
    setWeekData(Locale::getDefault(), nullptr, success);
716
0
}
717
718
// -------------------------------------
719
720
Calendar::Calendar(TimeZone* adoptZone, const Locale& aLocale, UErrorCode& success)
721
0
:   UObject(),
722
0
fIsTimeSet(false),
723
0
fAreFieldsSet(false),
724
0
fAreAllFieldsSet(false),
725
0
fAreFieldsVirtuallySet(false),
726
0
fLenient(true),
727
0
fRepeatedWallTime(UCAL_WALLTIME_LAST),
728
0
fSkippedWallTime(UCAL_WALLTIME_LAST)
729
0
{
730
0
    LocalPointer<TimeZone> zone(adoptZone, success);
731
0
    if (U_FAILURE(success)) {
732
0
        return;
733
0
    }
734
0
    if (zone.isNull()) {
735
#if defined (U_DEBUG_CAL)
736
        fprintf(stderr, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n",
737
            __FILE__, __LINE__);
738
#endif
739
0
        success = U_ILLEGAL_ARGUMENT_ERROR;
740
0
        return;
741
0
    }
742
743
0
    clear();
744
0
    fZone = zone.orphan();
745
0
    setWeekData(aLocale, nullptr, success);
746
0
}
747
748
// -------------------------------------
749
750
Calendar::Calendar(const TimeZone& zone, const Locale& aLocale, UErrorCode& success)
751
0
:   UObject(),
752
0
fIsTimeSet(false),
753
0
fAreFieldsSet(false),
754
0
fAreAllFieldsSet(false),
755
0
fAreFieldsVirtuallySet(false),
756
0
fLenient(true),
757
0
fRepeatedWallTime(UCAL_WALLTIME_LAST),
758
0
fSkippedWallTime(UCAL_WALLTIME_LAST)
759
0
{
760
0
    if (U_FAILURE(success)) {
761
0
        return;
762
0
    }
763
0
    clear();
764
0
    fZone = zone.clone();
765
0
    if (fZone == nullptr) {
766
0
        success = U_MEMORY_ALLOCATION_ERROR;
767
0
        return;
768
0
    }
769
0
    setWeekData(aLocale, nullptr, success);
770
0
}
771
772
// -------------------------------------
773
774
Calendar::~Calendar()
775
0
{
776
0
    delete fZone;
777
0
    delete actualLocale;
778
0
    delete validLocale;
779
0
}
780
781
// -------------------------------------
782
783
Calendar::Calendar(const Calendar &source)
784
0
:   UObject(source)
785
0
{
786
0
    *this = source;
787
0
}
788
789
// -------------------------------------
790
791
Calendar &
792
Calendar::operator=(const Calendar &right)
793
0
{
794
0
    if (this != &right) {
795
0
        uprv_arrayCopy(right.fFields, fFields, UCAL_FIELD_COUNT);
796
0
        uprv_arrayCopy(right.fStamp, fStamp, UCAL_FIELD_COUNT);
797
0
        fTime                    = right.fTime;
798
0
        fIsTimeSet               = right.fIsTimeSet;
799
0
        fAreAllFieldsSet         = right.fAreAllFieldsSet;
800
0
        fAreFieldsSet            = right.fAreFieldsSet;
801
0
        fAreFieldsVirtuallySet   = right.fAreFieldsVirtuallySet;
802
0
        fLenient                 = right.fLenient;
803
0
        fRepeatedWallTime        = right.fRepeatedWallTime;
804
0
        fSkippedWallTime         = right.fSkippedWallTime;
805
0
        delete fZone;
806
0
        fZone = nullptr;
807
0
        if (right.fZone != nullptr) {
808
0
            fZone                = right.fZone->clone();
809
0
        }
810
0
        fFirstDayOfWeek          = right.fFirstDayOfWeek;
811
0
        fMinimalDaysInFirstWeek  = right.fMinimalDaysInFirstWeek;
812
0
        fWeekendOnset            = right.fWeekendOnset;
813
0
        fWeekendOnsetMillis      = right.fWeekendOnsetMillis;
814
0
        fWeekendCease            = right.fWeekendCease;
815
0
        fWeekendCeaseMillis      = right.fWeekendCeaseMillis;
816
0
        fNextStamp               = right.fNextStamp;
817
0
        UErrorCode status = U_ZERO_ERROR;
818
0
        U_LOCALE_BASED(locBased, *this);
819
0
        locBased.setLocaleIDs(right.validLocale, right.actualLocale, status);
820
0
        U_ASSERT(U_SUCCESS(status));
821
0
    }
822
823
0
    return *this;
824
0
}
825
826
// -------------------------------------
827
828
Calendar* U_EXPORT2
829
Calendar::createInstance(UErrorCode& success)
830
0
{
831
0
    return createInstance(TimeZone::createDefault(), Locale::getDefault(), success);
832
0
}
833
834
// -------------------------------------
835
836
Calendar* U_EXPORT2
837
Calendar::createInstance(const TimeZone& zone, UErrorCode& success)
838
0
{
839
0
    return createInstance(zone, Locale::getDefault(), success);
840
0
}
841
842
// -------------------------------------
843
844
Calendar* U_EXPORT2
845
Calendar::createInstance(const Locale& aLocale, UErrorCode& success)
846
0
{
847
0
    return createInstance(TimeZone::forLocaleOrDefault(aLocale), aLocale, success);
848
0
}
849
850
// ------------------------------------- Adopting
851
852
// Note: this is the bottleneck that actually calls the service routines.
853
854
Calendar * U_EXPORT2
855
0
Calendar::makeInstance(const Locale& aLocale, UErrorCode& success) {
856
0
    if (U_FAILURE(success)) {
857
0
        return nullptr;
858
0
    }
859
860
0
    Locale actualLoc;
861
0
    UObject* u = nullptr;
862
863
0
#if !UCONFIG_NO_SERVICE
864
0
    if (isCalendarServiceUsed()) {
865
0
        u = getCalendarService(success)->get(aLocale, LocaleKey::KIND_ANY, &actualLoc, success);
866
0
    }
867
0
    else
868
0
#endif
869
0
    {
870
0
        u = createStandardCalendar(getCalendarTypeForLocale(aLocale.getName()), aLocale, success);
871
0
    }
872
0
    Calendar* c = nullptr;
873
874
0
    if(U_FAILURE(success) || !u) {
875
0
        if(U_SUCCESS(success)) { // Propagate some kind of err
876
0
            success = U_INTERNAL_PROGRAM_ERROR;
877
0
        }
878
0
        return nullptr;
879
0
    }
880
881
0
#if !UCONFIG_NO_SERVICE
882
0
    const UnicodeString* str = dynamic_cast<const UnicodeString*>(u);
883
0
    if(str != nullptr) {
884
        // It's a unicode string telling us what type of calendar to load ("gregorian", etc)
885
        // Create a Locale over this string
886
0
        Locale l("");
887
0
        LocaleUtility::initLocaleFromName(*str, l);
888
889
#ifdef U_DEBUG_CALSVC
890
        fprintf(stderr, "Calendar::createInstance(%s), looking up [%s]\n", aLocale.getName(), l.getName());
891
#endif
892
893
0
        Locale actualLoc2;
894
0
        delete u;
895
0
        u = nullptr;
896
897
        // Don't overwrite actualLoc, since the actual loc from this call
898
        // may be something like "@calendar=gregorian" -- TODO investigate
899
        // further...
900
0
        c = (Calendar*)getCalendarService(success)->get(l, LocaleKey::KIND_ANY, &actualLoc2, success);
901
902
0
        if(U_FAILURE(success) || !c) {
903
0
            if(U_SUCCESS(success)) {
904
0
                success = U_INTERNAL_PROGRAM_ERROR; // Propagate some err
905
0
            }
906
0
            return nullptr;
907
0
        }
908
909
0
        str = dynamic_cast<const UnicodeString*>(c);
910
0
        if(str != nullptr) {
911
            // recursed! Second lookup returned a UnicodeString.
912
            // Perhaps DefaultCalendar{} was set to another locale.
913
#ifdef U_DEBUG_CALSVC
914
            char tmp[200];
915
            // Extract a char* out of it..
916
            int32_t len = str->length();
917
            int32_t actLen = sizeof(tmp)-1;
918
            if(len > actLen) {
919
                len = actLen;
920
            }
921
            str->extract(0,len,tmp);
922
            tmp[len]=0;
923
924
            fprintf(stderr, "err - recursed, 2nd lookup was unistring %s\n", tmp);
925
#endif
926
0
            success = U_MISSING_RESOURCE_ERROR;  // requested a calendar type which could NOT be found.
927
0
            delete c;
928
0
            return nullptr;
929
0
        }
930
#ifdef U_DEBUG_CALSVC
931
        fprintf(stderr, "%p: setting week count data to locale %s, actual locale %s\n", c, (const char*)aLocale.getName(), (const char *)actualLoc.getName());
932
#endif
933
0
        c->setWeekData(aLocale, c->getType(), success);  // set the correct locale (this was an indirect calendar)
934
935
0
        char keyword[ULOC_FULLNAME_CAPACITY] = "";
936
0
        UErrorCode tmpStatus = U_ZERO_ERROR;
937
0
        l.getKeywordValue("calendar", keyword, ULOC_FULLNAME_CAPACITY, tmpStatus);
938
0
        if (U_SUCCESS(tmpStatus) && uprv_strcmp(keyword, "iso8601") == 0) {
939
0
            c->setFirstDayOfWeek(UCAL_MONDAY);
940
0
            c->setMinimalDaysInFirstWeek(4);
941
0
        }
942
0
    }
943
0
    else
944
0
#endif /* UCONFIG_NO_SERVICE */
945
0
    {
946
        // a calendar was returned - we assume the factory did the right thing.
947
0
        c = (Calendar*)u;
948
0
    }
949
950
0
    return c;
951
0
}
952
953
Calendar* U_EXPORT2
954
Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success)
955
0
{
956
0
    LocalPointer<TimeZone> zonePtr(zone);
957
0
    const SharedCalendar *shared = nullptr;
958
0
    UnifiedCache::getByLocale(aLocale, shared, success);
959
0
    if (U_FAILURE(success)) {
960
0
        return nullptr;
961
0
    }
962
0
    Calendar *c = (*shared)->clone();
963
0
    shared->removeRef();
964
0
    if (c == nullptr) {
965
0
        success = U_MEMORY_ALLOCATION_ERROR;
966
0
        return nullptr;
967
0
    }
968
969
    // Now, reset calendar to default state:
970
0
    c->adoptTimeZone(zonePtr.orphan()); //  Set the correct time zone
971
0
    c->setTimeInMillis(getNow(), success); // let the new calendar have the current time.
972
973
0
    return c;
974
0
}
975
976
// -------------------------------------
977
978
Calendar* U_EXPORT2
979
Calendar::createInstance(const TimeZone& zone, const Locale& aLocale, UErrorCode& success)
980
0
{
981
0
    Calendar* c = createInstance(aLocale, success);
982
0
    if(U_SUCCESS(success) && c) {
983
0
        c->setTimeZone(zone);
984
0
    }
985
0
    return c;
986
0
}
987
988
// -------------------------------------
989
990
void U_EXPORT2
991
Calendar::getCalendarTypeFromLocale(
992
        const Locale &aLocale,
993
        char *typeBuffer,
994
        int32_t typeBufferSize,
995
0
        UErrorCode &success) {
996
0
    const SharedCalendar *shared = nullptr;
997
0
    UnifiedCache::getByLocale(aLocale, shared, success);
998
0
    if (U_FAILURE(success)) {
999
0
        return;
1000
0
    }
1001
0
    uprv_strncpy(typeBuffer, (*shared)->getType(), typeBufferSize);
1002
0
    shared->removeRef();
1003
0
    if (typeBuffer[typeBufferSize - 1]) {
1004
0
        success = U_BUFFER_OVERFLOW_ERROR;
1005
0
    }
1006
0
}
1007
1008
bool
1009
Calendar::operator==(const Calendar& that) const
1010
0
{
1011
0
    UErrorCode status = U_ZERO_ERROR;
1012
0
    return isEquivalentTo(that) &&
1013
0
        getTimeInMillis(status) == that.getTimeInMillis(status) &&
1014
0
        U_SUCCESS(status);
1015
0
}
1016
1017
UBool
1018
Calendar::isEquivalentTo(const Calendar& other) const
1019
0
{
1020
0
    return typeid(*this) == typeid(other) &&
1021
0
        fLenient                == other.fLenient &&
1022
0
        fRepeatedWallTime       == other.fRepeatedWallTime &&
1023
0
        fSkippedWallTime        == other.fSkippedWallTime &&
1024
0
        fFirstDayOfWeek         == other.fFirstDayOfWeek &&
1025
0
        fMinimalDaysInFirstWeek == other.fMinimalDaysInFirstWeek &&
1026
0
        fWeekendOnset           == other.fWeekendOnset &&
1027
0
        fWeekendOnsetMillis     == other.fWeekendOnsetMillis &&
1028
0
        fWeekendCease           == other.fWeekendCease &&
1029
0
        fWeekendCeaseMillis     == other.fWeekendCeaseMillis &&
1030
0
        *fZone                  == *other.fZone;
1031
0
}
1032
1033
// -------------------------------------
1034
1035
UBool
1036
Calendar::equals(const Calendar& when, UErrorCode& status) const
1037
0
{
1038
0
    return (this == &when ||
1039
0
        getTime(status) == when.getTime(status));
1040
0
}
1041
1042
// -------------------------------------
1043
1044
UBool
1045
Calendar::before(const Calendar& when, UErrorCode& status) const
1046
0
{
1047
0
    return (this != &when &&
1048
0
        getTimeInMillis(status) < when.getTimeInMillis(status));
1049
0
}
1050
1051
// -------------------------------------
1052
1053
UBool
1054
Calendar::after(const Calendar& when, UErrorCode& status) const
1055
0
{
1056
0
    return (this != &when &&
1057
0
        getTimeInMillis(status) > when.getTimeInMillis(status));
1058
0
}
1059
1060
// -------------------------------------
1061
1062
1063
const Locale* U_EXPORT2
1064
Calendar::getAvailableLocales(int32_t& count)
1065
0
{
1066
0
    return Locale::getAvailableLocales(count);
1067
0
}
1068
1069
// -------------------------------------
1070
1071
StringEnumeration* U_EXPORT2
1072
Calendar::getKeywordValuesForLocale(const char* key,
1073
                    const Locale& locale, UBool commonlyUsed, UErrorCode& status)
1074
0
{
1075
    // This is a wrapper over ucal_getKeywordValuesForLocale
1076
0
    UEnumeration *uenum = ucal_getKeywordValuesForLocale(key, locale.getName(),
1077
0
                                                        commonlyUsed, &status);
1078
0
    if (U_FAILURE(status)) {
1079
0
        uenum_close(uenum);
1080
0
        return nullptr;
1081
0
    }
1082
0
    UStringEnumeration* ustringenum = new UStringEnumeration(uenum);
1083
0
    if (ustringenum == nullptr) {
1084
0
        status = U_MEMORY_ALLOCATION_ERROR;
1085
0
    }
1086
0
    return ustringenum;
1087
0
}
1088
1089
// -------------------------------------
1090
1091
UDate U_EXPORT2
1092
Calendar::getNow()
1093
420
{
1094
420
    return uprv_getUTCtime(); // return as milliseconds
1095
420
}
1096
1097
// -------------------------------------
1098
1099
/**
1100
* Gets this Calendar's current time as a long.
1101
* @return the current time as UTC milliseconds from the epoch.
1102
*/
1103
double
1104
Calendar::getTimeInMillis(UErrorCode& status) const
1105
0
{
1106
0
    if(U_FAILURE(status))
1107
0
        return 0.0;
1108
1109
0
    if ( ! fIsTimeSet)
1110
0
        const_cast<Calendar*>(this)->updateTime(status);
1111
1112
    /* Test for buffer overflows */
1113
0
    if(U_FAILURE(status)) {
1114
0
        return 0.0;
1115
0
    }
1116
0
    return fTime;
1117
0
}
1118
1119
// -------------------------------------
1120
1121
/**
1122
* Sets this Calendar's current time from the given long value.
1123
* A status of U_ILLEGAL_ARGUMENT_ERROR is set when millis is
1124
* outside the range permitted by a Calendar object when not in lenient mode.
1125
* when in lenient mode the out of range values are pinned to their respective min/max.
1126
* @param date the new time in UTC milliseconds from the epoch.
1127
*/
1128
void
1129
0
Calendar::setTimeInMillis( double millis, UErrorCode& status ) {
1130
0
    if(U_FAILURE(status))
1131
0
        return;
1132
1133
0
    if (millis > MAX_MILLIS) {
1134
0
        if(isLenient()) {
1135
0
            millis = MAX_MILLIS;
1136
0
        } else {
1137
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
1138
0
        return;
1139
0
        }
1140
0
    } else if (millis < MIN_MILLIS) {
1141
0
        if(isLenient()) {
1142
0
            millis = MIN_MILLIS;
1143
0
        } else {
1144
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
1145
0
        return;
1146
0
        }
1147
0
    } else if (uprv_isNaN(millis)) {
1148
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
1149
0
        return;
1150
0
    }
1151
1152
0
    fTime = millis;
1153
0
    fAreFieldsSet = fAreAllFieldsSet = false;
1154
0
    fIsTimeSet = fAreFieldsVirtuallySet = true;
1155
1156
0
    uprv_memset(fFields, 0, sizeof(fFields));
1157
0
    uprv_memset(fStamp, kUnset, sizeof(fStamp));
1158
0
    fNextStamp = kMinimumUserStamp;
1159
0
}
1160
1161
// -------------------------------------
1162
1163
int32_t
1164
Calendar::get(UCalendarDateFields field, UErrorCode& status) const
1165
0
{
1166
0
    if (U_FAILURE(status)) {
1167
0
        return 0;
1168
0
    }
1169
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
1170
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
1171
0
        return 0;
1172
0
    }
1173
    // field values are only computed when actually requested; for more on when computation
1174
    // of various things happens, see the "data flow in Calendar" description at the top
1175
    // of this file
1176
0
    if (U_SUCCESS(status)) const_cast<Calendar*>(this)->complete(status); // Cast away const
1177
0
    return U_SUCCESS(status) ? fFields[field] : 0;
1178
0
}
1179
1180
// -------------------------------------
1181
1182
void
1183
Calendar::set(UCalendarDateFields field, int32_t value)
1184
0
{
1185
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
1186
0
        return;
1187
0
    }
1188
0
    if (fAreFieldsVirtuallySet) {
1189
0
        UErrorCode ec = U_ZERO_ERROR;
1190
0
        computeFields(ec);
1191
0
    }
1192
0
    fFields[field]     = value;
1193
    /* Ensure that the fNextStamp value doesn't go pass max value for int8_t */
1194
0
    if (fNextStamp == STAMP_MAX) {
1195
0
        recalculateStamp();
1196
0
    }
1197
0
    fStamp[field]     = fNextStamp++;
1198
0
    fIsTimeSet = fAreFieldsSet = fAreFieldsVirtuallySet = false;
1199
0
}
1200
1201
// -------------------------------------
1202
1203
void
1204
Calendar::set(int32_t year, int32_t month, int32_t date)
1205
0
{
1206
0
    set(UCAL_YEAR, year);
1207
0
    set(UCAL_MONTH, month);
1208
0
    set(UCAL_DATE, date);
1209
0
}
1210
1211
// -------------------------------------
1212
1213
void
1214
Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute)
1215
0
{
1216
0
    set(UCAL_YEAR, year);
1217
0
    set(UCAL_MONTH, month);
1218
0
    set(UCAL_DATE, date);
1219
0
    set(UCAL_HOUR_OF_DAY, hour);
1220
0
    set(UCAL_MINUTE, minute);
1221
0
}
1222
1223
// -------------------------------------
1224
1225
void
1226
Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute, int32_t second)
1227
0
{
1228
0
    set(UCAL_YEAR, year);
1229
0
    set(UCAL_MONTH, month);
1230
0
    set(UCAL_DATE, date);
1231
0
    set(UCAL_HOUR_OF_DAY, hour);
1232
0
    set(UCAL_MINUTE, minute);
1233
0
    set(UCAL_SECOND, second);
1234
0
}
1235
1236
// -------------------------------------
1237
int32_t Calendar::getRelatedYear(UErrorCode &status) const
1238
0
{
1239
0
    int32_t year = get(UCAL_EXTENDED_YEAR, status);
1240
0
    if (U_FAILURE(status)) {
1241
0
        return 0;
1242
0
    }
1243
0
    if (uprv_add32_overflow(year, getRelatedYearDifference(), &year)) {
1244
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
1245
0
        return 0;
1246
0
    }
1247
0
    return year;
1248
0
}
1249
1250
// -------------------------------------
1251
void Calendar::setRelatedYear(int32_t year)
1252
0
{
1253
    // set extended year
1254
0
    if (uprv_add32_overflow(year, -getRelatedYearDifference(), &year)) {
1255
0
        return;
1256
0
    }
1257
0
    set(UCAL_EXTENDED_YEAR, year);
1258
0
}
1259
1260
0
int32_t Calendar::getRelatedYearDifference() const {
1261
0
    return 0;
1262
0
}
1263
1264
// -------------------------------------
1265
1266
void
1267
Calendar::clear()
1268
0
{
1269
0
    uprv_memset(fFields, 0, sizeof(fFields));
1270
0
    uprv_memset(fStamp, kUnset, sizeof(fStamp));
1271
0
    fNextStamp = kMinimumUserStamp;
1272
0
    fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = false;
1273
    // fTime is not 'cleared' - may be used if no fields are set.
1274
0
}
1275
1276
// -------------------------------------
1277
1278
void
1279
Calendar::clear(UCalendarDateFields field)
1280
0
{
1281
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
1282
0
        return;
1283
0
    }
1284
0
    if (fAreFieldsVirtuallySet) {
1285
0
        UErrorCode ec = U_ZERO_ERROR;
1286
0
        computeFields(ec);
1287
0
    }
1288
0
    fFields[field]         = 0;
1289
0
    fStamp[field]         = kUnset;
1290
0
    if (field == UCAL_MONTH) {
1291
0
        fFields[UCAL_ORDINAL_MONTH]         = 0;
1292
0
        fStamp[UCAL_ORDINAL_MONTH]         = kUnset;
1293
0
    }
1294
0
    if (field == UCAL_ORDINAL_MONTH) {
1295
0
        fFields[UCAL_MONTH]         = 0;
1296
0
        fStamp[UCAL_MONTH]         = kUnset;
1297
0
    }
1298
0
    fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = false;
1299
0
}
1300
1301
// -------------------------------------
1302
1303
UBool
1304
Calendar::isSet(UCalendarDateFields field) const
1305
0
{
1306
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
1307
0
        return false;
1308
0
    }
1309
0
    return fAreFieldsVirtuallySet || (fStamp[field] != kUnset);
1310
0
}
1311
1312
1313
int32_t Calendar::newestStamp(UCalendarDateFields first, UCalendarDateFields last, int32_t bestStampSoFar) const
1314
0
{
1315
0
    int32_t bestStamp = bestStampSoFar;
1316
0
    for (int32_t i = static_cast<int32_t>(first); i <= static_cast<int32_t>(last); ++i) {
1317
0
        if (fStamp[i] > bestStamp) {
1318
0
            bestStamp = fStamp[i];
1319
0
        }
1320
0
    }
1321
0
    return bestStamp;
1322
0
}
1323
1324
1325
// -------------------------------------
1326
1327
void
1328
Calendar::complete(UErrorCode& status)
1329
0
{
1330
0
    if (U_FAILURE(status)) {
1331
0
       return;
1332
0
    }
1333
0
    if (!fIsTimeSet) {
1334
0
        updateTime(status);
1335
        /* Test for buffer overflows */
1336
0
        if(U_FAILURE(status)) {
1337
0
            return;
1338
0
        }
1339
0
    }
1340
0
    if (!fAreFieldsSet) {
1341
0
        computeFields(status); // fills in unset fields
1342
        /* Test for buffer overflows */
1343
0
        if(U_FAILURE(status)) {
1344
0
            return;
1345
0
        }
1346
0
        fAreFieldsSet         = true;
1347
0
        fAreAllFieldsSet     = true;
1348
0
    }
1349
0
}
1350
1351
//-------------------------------------------------------------------------
1352
// Protected utility methods for use by subclasses.  These are very handy
1353
// for implementing add, roll, and computeFields.
1354
//-------------------------------------------------------------------------
1355
1356
/**
1357
* Adjust the specified field so that it is within
1358
* the allowable range for the date to which this calendar is set.
1359
* For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH}
1360
* field for a calendar set to April 31 would cause it to be set
1361
* to April 30.
1362
* <p>
1363
* <b>Subclassing:</b>
1364
* <br>
1365
* This utility method is intended for use by subclasses that need to implement
1366
* their own overrides of {@link #roll roll} and {@link #add add}.
1367
* <p>
1368
* <b>Note:</b>
1369
* <code>pinField</code> is implemented in terms of
1370
* {@link #getActualMinimum getActualMinimum}
1371
* and {@link #getActualMaximum getActualMaximum}.  If either of those methods uses
1372
* a slow, iterative algorithm for a particular field, it would be
1373
* unwise to attempt to call <code>pinField</code> for that field.  If you
1374
* really do need to do so, you should override this method to do
1375
* something more efficient for that field.
1376
* <p>
1377
* @param field The calendar field whose value should be pinned.
1378
*
1379
* @see #getActualMinimum
1380
* @see #getActualMaximum
1381
* @stable ICU 2.0
1382
*/
1383
0
void Calendar::pinField(UCalendarDateFields field, UErrorCode& status) {
1384
0
    if (U_FAILURE(status)) {
1385
0
       return;
1386
0
    }
1387
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
1388
0
       status = U_ILLEGAL_ARGUMENT_ERROR;
1389
0
       return;
1390
0
    }
1391
0
    int32_t max = getActualMaximum(field, status);
1392
0
    int32_t min = getActualMinimum(field, status);
1393
1394
0
    if (fFields[field] > max) {
1395
0
        set(field, max);
1396
0
    } else if (fFields[field] < min) {
1397
0
        set(field, min);
1398
0
    }
1399
0
}
1400
1401
1402
void Calendar::computeFields(UErrorCode &ec)
1403
0
{
1404
0
    if (U_FAILURE(ec)) {
1405
0
        return;
1406
0
    }
1407
    // Compute local wall millis
1408
0
    double localMillis = internalGetTime();
1409
0
    int32_t rawOffset, dstOffset;
1410
0
    getTimeZone().getOffset(localMillis, false, rawOffset, dstOffset, ec);
1411
0
    if (U_FAILURE(ec)) {
1412
0
        return;
1413
0
    }
1414
0
    localMillis += (rawOffset + dstOffset);
1415
1416
    // Mark fields as set.  Do this before calling handleComputeFields().
1417
0
    uint32_t mask =   //fInternalSetMask;
1418
0
        (1 << UCAL_ERA) |
1419
0
        (1 << UCAL_YEAR) |
1420
0
        (1 << UCAL_MONTH) |
1421
0
        (1 << UCAL_DAY_OF_MONTH) | // = UCAL_DATE
1422
0
        (1 << UCAL_DAY_OF_YEAR) |
1423
0
        (1 << UCAL_EXTENDED_YEAR) |
1424
0
        (1 << UCAL_ORDINAL_MONTH);
1425
1426
0
    for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
1427
0
        if ((mask & 1) == 0) {
1428
0
            fStamp[i] = kInternallySet;
1429
0
        } else {
1430
0
            fStamp[i] = kUnset;
1431
0
        }
1432
0
        mask >>= 1;
1433
0
    }
1434
1435
    // We used to check for and correct extreme millis values (near
1436
    // Long.MIN_VALUE or Long.MAX_VALUE) here.  Such values would cause
1437
    // overflows from positive to negative (or vice versa) and had to
1438
    // be manually tweaked.  We no longer need to do this because we
1439
    // have limited the range of supported dates to those that have a
1440
    // Julian day that fits into an int.  This allows us to implement a
1441
    // JULIAN_DAY field and also removes some inelegant code. - Liu
1442
    // 11/6/00
1443
1444
0
    int32_t millisInDay;
1445
0
    double days = ClockMath::floorDivide(
1446
0
        localMillis, U_MILLIS_PER_DAY, &millisInDay) +
1447
0
        kEpochStartAsJulianDay;
1448
0
    if (days > INT32_MAX || days < INT32_MIN) {
1449
0
        ec = U_ILLEGAL_ARGUMENT_ERROR;
1450
0
        return;
1451
0
    }
1452
1453
0
    internalSet(UCAL_JULIAN_DAY, static_cast<int32_t>(days));
1454
1455
#if defined (U_DEBUG_CAL)
1456
    //fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n",
1457
    //__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
1458
#endif
1459
1460
0
    computeGregorianFields(fFields[UCAL_JULIAN_DAY], ec);
1461
1462
    // Call framework method to have subclass compute its fields.
1463
    // These must include, at a minimum, MONTH, DAY_OF_MONTH,
1464
    // EXTENDED_YEAR, YEAR, DAY_OF_YEAR.  This method will call internalSet(),
1465
    // which will update stamp[].
1466
0
    handleComputeFields(fFields[UCAL_JULIAN_DAY], ec);
1467
1468
    // Compute week-related fields, based on the subclass-computed
1469
    // fields computed by handleComputeFields().
1470
0
    computeWeekFields(ec);
1471
1472
    // Compute time-related fields.  These are independent of the date and
1473
    // of the subclass algorithm.  They depend only on the local zone
1474
    // wall milliseconds in day.
1475
0
    if (U_FAILURE(ec)) {
1476
0
        return;
1477
0
    }
1478
1479
0
    fFields[UCAL_MILLISECONDS_IN_DAY] = millisInDay;
1480
0
    U_ASSERT(getMinimum(UCAL_MILLISECONDS_IN_DAY) <=
1481
0
             fFields[UCAL_MILLISECONDS_IN_DAY]);
1482
0
    U_ASSERT(fFields[UCAL_MILLISECONDS_IN_DAY] <=
1483
0
             getMaximum(UCAL_MILLISECONDS_IN_DAY));
1484
1485
0
    fFields[UCAL_MILLISECOND] = millisInDay % 1000;
1486
0
    U_ASSERT(getMinimum(UCAL_MILLISECOND) <= fFields[UCAL_MILLISECOND]);
1487
0
    U_ASSERT(fFields[UCAL_MILLISECOND] <= getMaximum(UCAL_MILLISECOND));
1488
1489
0
    millisInDay /= 1000;
1490
0
    fFields[UCAL_SECOND] = millisInDay % 60;
1491
0
    U_ASSERT(getMinimum(UCAL_SECOND) <= fFields[UCAL_SECOND]);
1492
0
    U_ASSERT(fFields[UCAL_SECOND] <= getMaximum(UCAL_SECOND));
1493
1494
0
    millisInDay /= 60;
1495
0
    fFields[UCAL_MINUTE] = millisInDay % 60;
1496
0
    U_ASSERT(getMinimum(UCAL_MINUTE) <= fFields[UCAL_MINUTE]);
1497
0
    U_ASSERT(fFields[UCAL_MINUTE] <= getMaximum(UCAL_MINUTE));
1498
1499
0
    millisInDay /= 60;
1500
0
    fFields[UCAL_HOUR_OF_DAY] = millisInDay;
1501
0
    U_ASSERT(getMinimum(UCAL_HOUR_OF_DAY) <= fFields[UCAL_HOUR_OF_DAY]);
1502
0
    U_ASSERT(fFields[UCAL_HOUR_OF_DAY] <= getMaximum(UCAL_HOUR_OF_DAY));
1503
1504
0
    fFields[UCAL_AM_PM] = millisInDay / 12; // Assume AM == 0
1505
0
    U_ASSERT(getMinimum(UCAL_AM_PM) <= fFields[UCAL_AM_PM]);
1506
0
    U_ASSERT(fFields[UCAL_AM_PM] <= getMaximum(UCAL_AM_PM));
1507
1508
0
    fFields[UCAL_HOUR] = millisInDay % 12;
1509
0
    U_ASSERT(getMinimum(UCAL_HOUR) <= fFields[UCAL_HOUR]);
1510
0
    U_ASSERT(fFields[UCAL_HOUR] <= getMaximum(UCAL_HOUR));
1511
1512
0
    fFields[UCAL_ZONE_OFFSET] = rawOffset;
1513
0
    U_ASSERT(getMinimum(UCAL_ZONE_OFFSET) <= fFields[UCAL_ZONE_OFFSET]);
1514
0
    U_ASSERT(fFields[UCAL_ZONE_OFFSET] <= getMaximum(UCAL_ZONE_OFFSET));
1515
1516
0
    fFields[UCAL_DST_OFFSET] = dstOffset;
1517
0
    U_ASSERT(getMinimum(UCAL_DST_OFFSET) <= fFields[UCAL_DST_OFFSET]);
1518
0
    U_ASSERT(fFields[UCAL_DST_OFFSET] <= getMaximum(UCAL_DST_OFFSET));
1519
0
}
1520
1521
uint8_t Calendar::julianDayToDayOfWeek(int32_t julian)
1522
0
{
1523
    // If julian is negative, then julian%7 will be negative, so we adjust
1524
    // accordingly.  We add 1 because Julian day 0 is Monday.
1525
0
    int8_t dayOfWeek = static_cast<int8_t>((julian + 1LL) % 7);
1526
1527
0
    uint8_t result = static_cast<uint8_t>(dayOfWeek + ((dayOfWeek < 0) ? (7 + UCAL_SUNDAY) : UCAL_SUNDAY));
1528
0
    return result;
1529
0
}
1530
1531
/**
1532
* Compute the Gregorian calendar year, month, and day of month from the
1533
* Julian day.  These values are not stored in fields, but in member
1534
* variables gregorianXxx.  They are used for time zone computations and by
1535
* subclasses that are Gregorian derivatives.  Subclasses may call this
1536
* method to perform a Gregorian calendar millis->fields computation.
1537
*/
1538
0
void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode& ec) {
1539
0
    if (U_FAILURE(ec)) {
1540
0
        return;
1541
0
    }
1542
0
    if (uprv_add32_overflow(
1543
0
            julianDay, -kEpochStartAsJulianDay, &julianDay)) {
1544
0
        ec = U_ILLEGAL_ARGUMENT_ERROR;
1545
0
        return;
1546
0
    }
1547
0
    int8_t dayOfWeek;
1548
0
    Grego::dayToFields(julianDay, fGregorianYear, fGregorianMonth,
1549
0
                       fGregorianDayOfMonth,
1550
0
                       dayOfWeek,
1551
0
                       fGregorianDayOfYear, ec);
1552
0
    if (U_FAILURE(ec)) {
1553
0
        return;
1554
0
    }
1555
0
    internalSet(UCAL_DAY_OF_WEEK, dayOfWeek);
1556
0
}
1557
1558
/**
1559
* Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH,
1560
* DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR,
1561
* DAY_OF_WEEK, and DAY_OF_YEAR.  The latter fields are computed by the
1562
* subclass based on the calendar system.
1563
*
1564
* <p>The YEAR_WOY field is computed simplistically.  It is equal to YEAR
1565
* most of the time, but at the year boundary it may be adjusted to YEAR-1
1566
* or YEAR+1 to reflect the overlap of a week into an adjacent year.  In
1567
* this case, a simple increment or decrement is performed on YEAR, even
1568
* though this may yield an invalid YEAR value.  For instance, if the YEAR
1569
* is part of a calendar system with an N-year cycle field CYCLE, then
1570
* incrementing the YEAR may involve incrementing CYCLE and setting YEAR
1571
* back to 0 or 1.  This is not handled by this code, and in fact cannot be
1572
* simply handled without having subclasses define an entire parallel set of
1573
* fields for fields larger than or equal to a year.  This additional
1574
* complexity is not warranted, since the intention of the YEAR_WOY field is
1575
* to support ISO 8601 notation, so it will typically be used with a
1576
* proleptic Gregorian calendar, which has no field larger than a year.
1577
*/
1578
0
void Calendar::computeWeekFields(UErrorCode &ec) {
1579
0
    if(U_FAILURE(ec)) {
1580
0
        return;
1581
0
    }
1582
1583
    // Compute day of week: JD 0 = Monday
1584
0
    int32_t dayOfWeek = fFields[UCAL_DAY_OF_WEEK];
1585
0
    int32_t firstDayOfWeek = getFirstDayOfWeek();
1586
    // Calculate 1-based localized day of week
1587
0
    int32_t dowLocal = dayOfWeek - firstDayOfWeek + 1;
1588
0
    if (dowLocal < 1) {
1589
0
        dowLocal += 7;
1590
0
    }
1591
0
    internalSet(UCAL_DOW_LOCAL,dowLocal);
1592
1593
0
    int32_t eyear = fFields[UCAL_EXTENDED_YEAR];
1594
0
    int32_t dayOfYear = fFields[UCAL_DAY_OF_YEAR];
1595
1596
    // WEEK_OF_YEAR start
1597
    // Compute the week of the year.  For the Gregorian calendar, valid week
1598
    // numbers run from 1 to 52 or 53, depending on the year, the first day
1599
    // of the week, and the minimal days in the first week.  For other
1600
    // calendars, the valid range may be different -- it depends on the year
1601
    // length.  Days at the start of the year may fall into the last week of
1602
    // the previous year; days at the end of the year may fall into the
1603
    // first week of the next year.  ASSUME that the year length is less than
1604
    // 7000 days.
1605
0
    int32_t yearOfWeekOfYear = eyear;
1606
0
    int32_t relDow = (dayOfWeek + 7 - firstDayOfWeek) % 7; // 0..6
1607
0
    int32_t relDowJan1 = (dayOfWeek - dayOfYear + 7001 - firstDayOfWeek) % 7; // 0..6
1608
0
    int32_t woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53
1609
0
    int32_t minimalDaysInFirstWeek = getMinimalDaysInFirstWeek();
1610
0
    if ((7 - relDowJan1) >= minimalDaysInFirstWeek) {
1611
0
        ++woy;
1612
0
    }
1613
1614
    // Adjust for weeks at the year end that overlap into the previous or
1615
    // next calendar year.
1616
0
    if (woy == 0) {
1617
        // We are the last week of the previous year.
1618
        // Check to see if we are in the last week; if so, we need
1619
        // to handle the case in which we are the first week of the
1620
        // next year.
1621
1622
0
        int32_t prevDoy = dayOfYear + handleGetYearLength(eyear - 1, ec);
1623
0
        if(U_FAILURE(ec)) return;
1624
0
        woy = weekNumber(prevDoy, dayOfWeek);
1625
0
        yearOfWeekOfYear--;
1626
0
    } else {
1627
0
        int32_t lastDoy = handleGetYearLength(eyear, ec);
1628
0
        if(U_FAILURE(ec)) return;
1629
        // Fast check: For it to be week 1 of the next year, the DOY
1630
        // must be on or after L-5, where L is yearLength(), then it
1631
        // cannot possibly be week 1 of the next year:
1632
        //          L-5                  L
1633
        // doy: 359 360 361 362 363 364 365 001
1634
        // dow:      1   2   3   4   5   6   7
1635
0
        if (dayOfYear >= (lastDoy - 5)) {
1636
0
            int32_t lastRelDow = (relDow + lastDoy - dayOfYear) % 7;
1637
0
            if (lastRelDow < 0) {
1638
0
                lastRelDow += 7;
1639
0
            }
1640
0
            if (((6 - lastRelDow) >= minimalDaysInFirstWeek) &&
1641
0
                ((dayOfYear + 7 - relDow) > lastDoy)) {
1642
0
                    woy = 1;
1643
0
                    yearOfWeekOfYear++;
1644
0
                }
1645
0
        }
1646
0
    }
1647
0
    fFields[UCAL_WEEK_OF_YEAR] = woy;
1648
0
    fFields[UCAL_YEAR_WOY] = yearOfWeekOfYear;
1649
    // min/max of years are not constrains for caller, so not assert here.
1650
    // WEEK_OF_YEAR end
1651
1652
0
    int32_t dayOfMonth = fFields[UCAL_DAY_OF_MONTH];
1653
0
    fFields[UCAL_WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek);
1654
0
    U_ASSERT(getMinimum(UCAL_WEEK_OF_MONTH) <= fFields[UCAL_WEEK_OF_MONTH]);
1655
0
    U_ASSERT(fFields[UCAL_WEEK_OF_MONTH] <= getMaximum(UCAL_WEEK_OF_MONTH));
1656
1657
0
    fFields[UCAL_DAY_OF_WEEK_IN_MONTH] = (dayOfMonth-1) / 7 + 1;
1658
0
    U_ASSERT(getMinimum(UCAL_DAY_OF_WEEK_IN_MONTH) <=
1659
0
             fFields[UCAL_DAY_OF_WEEK_IN_MONTH]);
1660
0
    U_ASSERT(fFields[UCAL_DAY_OF_WEEK_IN_MONTH] <=
1661
0
             getMaximum(UCAL_DAY_OF_WEEK_IN_MONTH));
1662
1663
#if defined (U_DEBUG_CAL)
1664
    if(fFields[UCAL_DAY_OF_WEEK_IN_MONTH]==0) fprintf(stderr, "%s:%d: DOWIM %d on %g\n",
1665
        __FILE__, __LINE__,fFields[UCAL_DAY_OF_WEEK_IN_MONTH], fTime);
1666
#endif
1667
0
}
1668
1669
1670
int32_t Calendar::weekNumber(int32_t desiredDay, int32_t dayOfPeriod, int32_t dayOfWeek)
1671
0
{
1672
    // Determine the day of the week of the first day of the period
1673
    // in question (either a year or a month).  Zero represents the
1674
    // first day of the week on this calendar.
1675
0
    int32_t periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % 7;
1676
0
    if (periodStartDayOfWeek < 0) periodStartDayOfWeek += 7;
1677
1678
    // Compute the week number.  Initially, ignore the first week, which
1679
    // may be fractional (or may not be).  We add periodStartDayOfWeek in
1680
    // order to fill out the first week, if it is fractional.
1681
0
    int32_t weekNo = (desiredDay + periodStartDayOfWeek - 1)/7;
1682
1683
    // If the first week is long enough, then count it.  If
1684
    // the minimal days in the first week is one, or if the period start
1685
    // is zero, we always increment weekNo.
1686
0
    if ((7 - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo;
1687
1688
0
    return weekNo;
1689
0
}
1690
1691
void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode& status)
1692
0
{
1693
0
    if (U_FAILURE(status)) {
1694
0
        return;
1695
0
    }
1696
0
    int32_t month = getGregorianMonth();
1697
0
    internalSet(UCAL_MONTH, month);
1698
0
    internalSet(UCAL_ORDINAL_MONTH, month);
1699
0
    internalSet(UCAL_DAY_OF_MONTH, getGregorianDayOfMonth());
1700
0
    internalSet(UCAL_DAY_OF_YEAR, getGregorianDayOfYear());
1701
0
    int32_t eyear = getGregorianYear();
1702
0
    internalSet(UCAL_EXTENDED_YEAR, eyear);
1703
0
    int32_t era = GregorianCalendar::AD;
1704
0
    if (eyear < 1) {
1705
0
        era = GregorianCalendar::BC;
1706
0
        eyear = 1 - eyear;
1707
0
    }
1708
0
    internalSet(UCAL_ERA, era);
1709
0
    internalSet(UCAL_YEAR, eyear);
1710
0
}
1711
// -------------------------------------
1712
1713
1714
void Calendar::roll(EDateFields field, int32_t amount, UErrorCode& status)
1715
0
{
1716
0
    roll(static_cast<UCalendarDateFields>(field), amount, status);
1717
0
}
1718
1719
0
void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) UPRV_NO_SANITIZE_UNDEFINED {
1720
0
    if (amount == 0) {
1721
0
        return; // Nothing to do
1722
0
    }
1723
1724
0
    complete(status);
1725
1726
0
    if(U_FAILURE(status)) {
1727
0
        return;
1728
0
    }
1729
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
1730
0
       status = U_ILLEGAL_ARGUMENT_ERROR;
1731
0
       return;
1732
0
    }
1733
0
    switch (field) {
1734
0
    case UCAL_DAY_OF_MONTH:
1735
0
    case UCAL_AM_PM:
1736
0
    case UCAL_MINUTE:
1737
0
    case UCAL_SECOND:
1738
0
    case UCAL_MILLISECOND:
1739
0
    case UCAL_MILLISECONDS_IN_DAY:
1740
0
    case UCAL_ERA:
1741
        // These are the standard roll instructions.  These work for all
1742
        // simple cases, that is, cases in which the limits are fixed, such
1743
        // as the hour, the day of the month, and the era.
1744
0
        {
1745
0
            int32_t min = getActualMinimum(field,status);
1746
0
            int32_t max = getActualMaximum(field,status);
1747
0
            if (U_FAILURE(status)) {
1748
0
               return;
1749
0
            }
1750
0
            int32_t gap = max - min + 1;
1751
1752
0
            int64_t value = internalGet(field);
1753
0
            value = (value + amount - min) % gap;
1754
0
            if (value < 0) {
1755
0
                value += gap;
1756
0
            }
1757
0
            value += min;
1758
1759
0
            set(field, value);
1760
0
            return;
1761
0
        }
1762
1763
0
    case UCAL_HOUR:
1764
0
    case UCAL_HOUR_OF_DAY:
1765
        // Rolling the hour is difficult on the ONSET and CEASE days of
1766
        // daylight savings.  For example, if the change occurs at
1767
        // 2 AM, we have the following progression:
1768
        // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst
1769
        // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std
1770
        // To get around this problem we don't use fields; we manipulate
1771
        // the time in millis directly.
1772
0
        {
1773
            // Assume min == 0 in calculations below
1774
0
            double start = getTimeInMillis(status);
1775
0
            int64_t oldHour = internalGet(field);
1776
0
            int32_t max = getMaximum(field);
1777
0
            int32_t newHour = (oldHour + amount) % (max + 1);
1778
0
            if (newHour < 0) {
1779
0
                newHour += max + 1;
1780
0
            }
1781
0
            setTimeInMillis(start + kOneHour * (newHour - oldHour),status);
1782
0
            return;
1783
0
        }
1784
1785
0
    case UCAL_MONTH:
1786
0
    case UCAL_ORDINAL_MONTH:
1787
        // Rolling the month involves both pinning the final value
1788
        // and adjusting the DAY_OF_MONTH if necessary.  We only adjust the
1789
        // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
1790
        // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
1791
0
        {
1792
0
            int32_t max = getActualMaximum(UCAL_MONTH, status) + 1;
1793
0
            int64_t mon = internalGet(UCAL_MONTH);
1794
0
            mon = (mon + amount) % max;
1795
1796
0
            if (mon < 0) {
1797
0
                mon += max;
1798
0
            }
1799
0
            set(UCAL_MONTH, mon);
1800
1801
            // Keep the day of month in range.  We don't want to spill over
1802
            // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 ->
1803
            // mar3.
1804
0
            pinField(UCAL_DAY_OF_MONTH,status);
1805
0
            return;
1806
0
        }
1807
1808
0
    case UCAL_YEAR:
1809
0
    case UCAL_YEAR_WOY:
1810
0
        {
1811
            // * If era==0 and years go backwards in time, change sign of amount.
1812
            // * Until we have new API per #9393, we temporarily hardcode knowledge of
1813
            //   which calendars have era 0 years that go backwards.
1814
0
            int32_t era = internalGet(UCAL_ERA);
1815
0
            if (era == 0 && isEra0CountingBackward()) {
1816
0
                if (uprv_mul32_overflow(amount, -1, &amount)) {
1817
0
                    status = U_ILLEGAL_ARGUMENT_ERROR;
1818
0
                    return;
1819
0
                }
1820
0
            }
1821
0
            int32_t newYear;
1822
0
            if (uprv_add32_overflow(
1823
0
                amount, internalGet(field), &newYear)) {
1824
0
                status = U_ILLEGAL_ARGUMENT_ERROR;
1825
0
                return;
1826
0
            }
1827
0
            if (era > 0 || newYear >= 1) {
1828
0
                int32_t maxYear = getActualMaximum(field, status);
1829
0
                if (maxYear < 32768) {
1830
                    // this era has real bounds, roll should wrap years
1831
0
                    if (newYear < 1) {
1832
0
                        newYear = maxYear - ((-newYear) % maxYear);
1833
0
                    } else if (newYear > maxYear) {
1834
0
                        newYear = ((newYear - 1) % maxYear) + 1;
1835
0
                    }
1836
                // else era is unbounded, just pin low year instead of wrapping
1837
0
                } else if (newYear < 1) {
1838
0
                    newYear = 1;
1839
0
                }
1840
            // else we are in era 0 with newYear < 1;
1841
            // calendars with years that go backwards must pin the year value at 0,
1842
            // other calendars can have years < 0 in era 0
1843
0
            } else if (era == 0 && isEra0CountingBackward()) {
1844
0
                newYear = 1;
1845
0
            }
1846
0
            set(field, newYear);
1847
0
            pinField(UCAL_MONTH,status);
1848
0
            pinField(UCAL_DAY_OF_MONTH,status);
1849
0
            return;
1850
0
        }
1851
1852
0
    case UCAL_EXTENDED_YEAR:
1853
        // Rolling the year can involve pinning the DAY_OF_MONTH.
1854
0
        if (uprv_add32_overflow(
1855
0
            amount, internalGet(field), &amount)) {
1856
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
1857
0
            return;
1858
0
        }
1859
0
        set(field, amount);
1860
0
        pinField(UCAL_MONTH,status);
1861
0
        pinField(UCAL_DAY_OF_MONTH,status);
1862
0
        return;
1863
1864
0
    case UCAL_WEEK_OF_MONTH:
1865
0
        {
1866
            // This is tricky, because during the roll we may have to shift
1867
            // to a different day of the week.  For example:
1868
1869
            //    s  m  t  w  r  f  s
1870
            //          1  2  3  4  5
1871
            //    6  7  8  9 10 11 12
1872
1873
            // When rolling from the 6th or 7th back one week, we go to the
1874
            // 1st (assuming that the first partial week counts).  The same
1875
            // thing happens at the end of the month.
1876
1877
            // The other tricky thing is that we have to figure out whether
1878
            // the first partial week actually counts or not, based on the
1879
            // minimal first days in the week.  And we have to use the
1880
            // correct first day of the week to delineate the week
1881
            // boundaries.
1882
1883
            // Here's our algorithm.  First, we find the real boundaries of
1884
            // the month.  Then we discard the first partial week if it
1885
            // doesn't count in this locale.  Then we fill in the ends with
1886
            // phantom days, so that the first partial week and the last
1887
            // partial week are full weeks.  We then have a nice square
1888
            // block of weeks.  We do the usual rolling within this block,
1889
            // as is done elsewhere in this method.  If we wind up on one of
1890
            // the phantom days that we added, we recognize this and pin to
1891
            // the first or the last day of the month.  Easy, eh?
1892
1893
            // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
1894
            // in this locale.  We have dow in 0..6.
1895
0
            int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
1896
0
            if (dow < 0) dow += 7;
1897
1898
            // Find the day of the week (normalized for locale) for the first
1899
            // of the month.
1900
0
            int32_t fdm = (dow - internalGet(UCAL_DAY_OF_MONTH) + 1) % 7;
1901
0
            if (fdm < 0) fdm += 7;
1902
1903
            // Get the first day of the first full week of the month,
1904
            // including phantom days, if any.  Figure out if the first week
1905
            // counts or not; if it counts, then fill in phantom days.  If
1906
            // not, advance to the first real full week (skip the partial week).
1907
0
            int32_t start;
1908
0
            if ((7 - fdm) < getMinimalDaysInFirstWeek())
1909
0
                start = 8 - fdm; // Skip the first partial week
1910
0
            else
1911
0
                start = 1 - fdm; // This may be zero or negative
1912
1913
            // Get the day of the week (normalized for locale) for the last
1914
            // day of the month.
1915
0
            int32_t monthLen = getActualMaximum(UCAL_DAY_OF_MONTH, status);
1916
0
            int32_t ldm = (monthLen - internalGet(UCAL_DAY_OF_MONTH) + dow) % 7;
1917
            // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.
1918
1919
            // Get the limit day for the blocked-off rectangular month; that
1920
            // is, the day which is one past the last day of the month,
1921
            // after the month has already been filled in with phantom days
1922
            // to fill out the last week.  This day has a normalized DOW of 0.
1923
0
            int32_t limit = monthLen + 7 - ldm;
1924
1925
            // Now roll between start and (limit - 1).
1926
0
            int32_t gap = limit - start;
1927
0
            if (gap == 0) {
1928
0
                status =  U_INTERNAL_PROGRAM_ERROR;
1929
0
                return;
1930
0
            }
1931
0
            int32_t day_of_month = (internalGet(UCAL_DAY_OF_MONTH) + amount*7LL -
1932
0
                start) % gap;
1933
0
            if (day_of_month < 0) day_of_month += gap;
1934
0
            day_of_month += start;
1935
1936
            // Finally, pin to the real start and end of the month.
1937
0
            if (day_of_month < 1) day_of_month = 1;
1938
0
            if (day_of_month > monthLen) day_of_month = monthLen;
1939
1940
            // Set the DAY_OF_MONTH.  We rely on the fact that this field
1941
            // takes precedence over everything else (since all other fields
1942
            // are also set at this point).  If this fact changes (if the
1943
            // disambiguation algorithm changes) then we will have to unset
1944
            // the appropriate fields here so that DAY_OF_MONTH is attended
1945
            // to.
1946
0
            set(UCAL_DAY_OF_MONTH, day_of_month);
1947
0
            return;
1948
0
        }
1949
0
    case UCAL_WEEK_OF_YEAR:
1950
0
        {
1951
            // This follows the outline of WEEK_OF_MONTH, except it applies
1952
            // to the whole year.  Please see the comment for WEEK_OF_MONTH
1953
            // for general notes.
1954
1955
            // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
1956
            // in this locale.  We have dow in 0..6.
1957
0
            int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
1958
0
            if (dow < 0) dow += 7;
1959
1960
            // Find the day of the week (normalized for locale) for the first
1961
            // of the year.
1962
0
            int32_t fdy = (dow - internalGet(UCAL_DAY_OF_YEAR) + 1) % 7;
1963
0
            if (fdy < 0) fdy += 7;
1964
1965
            // Get the first day of the first full week of the year,
1966
            // including phantom days, if any.  Figure out if the first week
1967
            // counts or not; if it counts, then fill in phantom days.  If
1968
            // not, advance to the first real full week (skip the partial week).
1969
0
            int32_t start;
1970
0
            if ((7 - fdy) < getMinimalDaysInFirstWeek())
1971
0
                start = 8 - fdy; // Skip the first partial week
1972
0
            else
1973
0
                start = 1 - fdy; // This may be zero or negative
1974
1975
            // Get the day of the week (normalized for locale) for the last
1976
            // day of the year.
1977
0
            int32_t yearLen = getActualMaximum(UCAL_DAY_OF_YEAR,status);
1978
0
            int32_t ldy = (yearLen - internalGet(UCAL_DAY_OF_YEAR) + dow) % 7;
1979
            // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here.
1980
1981
            // Get the limit day for the blocked-off rectangular year; that
1982
            // is, the day which is one past the last day of the year,
1983
            // after the year has already been filled in with phantom days
1984
            // to fill out the last week.  This day has a normalized DOW of 0.
1985
0
            int32_t limit = yearLen + 7 - ldy;
1986
1987
            // Now roll between start and (limit - 1).
1988
0
            int32_t gap = limit - start;
1989
0
            if (gap == 0) {
1990
0
                status =  U_INTERNAL_PROGRAM_ERROR;
1991
0
                return;
1992
0
            }
1993
0
            int32_t day_of_year = (internalGet(UCAL_DAY_OF_YEAR) + amount*7LL -
1994
0
                start) % gap;
1995
0
            if (day_of_year < 0) day_of_year += gap;
1996
0
            day_of_year += start;
1997
1998
            // Finally, pin to the real start and end of the month.
1999
0
            if (day_of_year < 1) day_of_year = 1;
2000
0
            if (day_of_year > yearLen) day_of_year = yearLen;
2001
2002
            // Make sure that the year and day of year are attended to by
2003
            // clearing other fields which would normally take precedence.
2004
            // If the disambiguation algorithm is changed, this section will
2005
            // have to be updated as well.
2006
0
            set(UCAL_DAY_OF_YEAR, day_of_year);
2007
0
            clear(UCAL_MONTH);
2008
0
            clear(UCAL_ORDINAL_MONTH);
2009
0
            return;
2010
0
        }
2011
0
    case UCAL_DAY_OF_YEAR:
2012
0
        {
2013
            // Roll the day of year using millis.  Compute the millis for
2014
            // the start of the year, and get the length of the year.
2015
0
            double delta = amount * kOneDay; // Scale up from days to millis
2016
0
            double min2 = internalGet(UCAL_DAY_OF_YEAR)-1;
2017
0
            min2 *= kOneDay;
2018
0
            min2 = internalGetTime() - min2;
2019
2020
            //      double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay;
2021
0
            double newtime;
2022
2023
0
            double yearLength = getActualMaximum(UCAL_DAY_OF_YEAR,status);
2024
0
            double oneYear = yearLength;
2025
0
            oneYear *= kOneDay;
2026
0
            newtime = uprv_fmod((internalGetTime() + delta - min2), oneYear);
2027
0
            if (newtime < 0) newtime += oneYear;
2028
0
            setTimeInMillis(newtime + min2, status);
2029
0
            return;
2030
0
        }
2031
0
    case UCAL_DAY_OF_WEEK:
2032
0
    case UCAL_DOW_LOCAL:
2033
0
        {
2034
            // Roll the day of week using millis.  Compute the millis for
2035
            // the start of the week, using the first day of week setting.
2036
            // Restrict the millis to [start, start+7days).
2037
0
            double delta = amount * kOneDay; // Scale up from days to millis
2038
            // Compute the number of days before the current day in this
2039
            // week.  This will be a value 0..6.
2040
0
            int32_t leadDays = internalGet(field);
2041
0
            leadDays -= (field == UCAL_DAY_OF_WEEK) ? getFirstDayOfWeek() : 1;
2042
0
            if (leadDays < 0) leadDays += 7;
2043
0
            double min2 = internalGetTime() - leadDays * kOneDay;
2044
0
            double newtime = uprv_fmod((internalGetTime() + delta - min2), kOneWeek);
2045
0
            if (newtime < 0) newtime += kOneWeek;
2046
0
            setTimeInMillis(newtime + min2, status);
2047
0
            return;
2048
0
        }
2049
0
    case UCAL_DAY_OF_WEEK_IN_MONTH:
2050
0
        {
2051
            // Roll the day of week in the month using millis.  Determine
2052
            // the first day of the week in the month, and then the last,
2053
            // and then roll within that range.
2054
0
            double delta = amount * kOneWeek; // Scale up from weeks to millis
2055
            // Find the number of same days of the week before this one
2056
            // in this month.
2057
0
            int32_t preWeeks = (internalGet(UCAL_DAY_OF_MONTH) - 1) / 7;
2058
            // Find the number of same days of the week after this one
2059
            // in this month.
2060
0
            int32_t postWeeks = (getActualMaximum(UCAL_DAY_OF_MONTH,status) -
2061
0
                internalGet(UCAL_DAY_OF_MONTH)) / 7;
2062
            // From these compute the min and gap millis for rolling.
2063
0
            double min2 = internalGetTime() - preWeeks * kOneWeek;
2064
0
            double gap2 = kOneWeek * (preWeeks + postWeeks + 1); // Must add 1!
2065
            // Roll within this range
2066
0
            double newtime = uprv_fmod((internalGetTime() + delta - min2), gap2);
2067
0
            if (newtime < 0) newtime += gap2;
2068
0
            setTimeInMillis(newtime + min2, status);
2069
0
            return;
2070
0
        }
2071
0
    case UCAL_JULIAN_DAY:
2072
0
        if (uprv_add32_overflow(
2073
0
            amount, internalGet(field), &amount)) {
2074
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
2075
0
            return;
2076
0
        }
2077
0
        set(field, amount);
2078
0
        return;
2079
0
    default:
2080
        // Other fields cannot be rolled by this method
2081
#if defined (U_DEBUG_CAL)
2082
        fprintf(stderr, "%s:%d: ILLEGAL ARG because of roll on non-rollable field %s\n",
2083
            __FILE__, __LINE__,fldName(field));
2084
#endif
2085
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2086
0
    }
2087
0
}
2088
2089
void Calendar::add(EDateFields field, int32_t amount, UErrorCode& status)
2090
0
{
2091
0
    Calendar::add(static_cast<UCalendarDateFields>(field), amount, status);
2092
0
}
2093
2094
// -------------------------------------
2095
void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status)
2096
0
{
2097
0
    if (U_FAILURE(status)) {
2098
0
       return;
2099
0
    }
2100
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
2101
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2102
0
        return;
2103
0
    }
2104
0
    if (amount == 0) {
2105
0
        return;   // Do nothing!
2106
0
    }
2107
2108
    // We handle most fields in the same way.  The algorithm is to add
2109
    // a computed amount of millis to the current millis.  The only
2110
    // wrinkle is with DST (and/or a change to the zone's UTC offset, which
2111
    // we'll include with DST) -- for some fields, like the DAY_OF_MONTH,
2112
    // we don't want the wall time to shift due to changes in DST.  If the
2113
    // result of the add operation is to move from DST to Standard, or
2114
    // vice versa, we need to adjust by an hour forward or back,
2115
    // respectively.  For such fields we set keepWallTimeInvariant to true.
2116
2117
    // We only adjust the DST for fields larger than an hour.  For
2118
    // fields smaller than an hour, we cannot adjust for DST without
2119
    // causing problems.  for instance, if you add one hour to April 5,
2120
    // 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an
2121
    // illegal value), but then the adjustment sees the change and
2122
    // compensates by subtracting an hour.  As a result the time
2123
    // doesn't advance at all.
2124
2125
    // For some fields larger than a day, such as a UCAL_MONTH, we pin the
2126
    // UCAL_DAY_OF_MONTH.  This allows <March 31>.add(UCAL_MONTH, 1) to be
2127
    // <April 30>, rather than <April 31> => <May 1>.
2128
2129
0
    double delta = amount; // delta in ms
2130
0
    UBool keepWallTimeInvariant = true;
2131
2132
0
    switch (field) {
2133
0
    case UCAL_ERA:
2134
0
      {
2135
0
        int32_t era = get(UCAL_ERA, status);
2136
0
        if (U_FAILURE(status)) {
2137
0
            return;
2138
0
        }
2139
0
        if (uprv_add32_overflow(era, amount, &era)) {
2140
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
2141
0
            return;
2142
0
        }
2143
0
        set(UCAL_ERA, era);
2144
0
        pinField(UCAL_ERA, status);
2145
0
        return;
2146
0
      }
2147
2148
0
    case UCAL_YEAR:
2149
0
    case UCAL_YEAR_WOY:
2150
0
      {
2151
        // * If era=0 and years go backwards in time, change sign of amount.
2152
        // * Until we have new API per #9393, we temporarily hardcode knowledge of
2153
        //   which calendars have era 0 years that go backwards.
2154
        // * Note that for UCAL_YEAR (but not UCAL_YEAR_WOY) we could instead handle
2155
        //   this by applying the amount to the UCAL_EXTENDED_YEAR field; but since
2156
        //   we would still need to handle UCAL_YEAR_WOY as below, might as well
2157
        //   also handle UCAL_YEAR the same way.
2158
0
        if (get(UCAL_ERA, status) == 0 && isEra0CountingBackward()) {
2159
0
          if (uprv_mul32_overflow(amount, -1, &amount)) {
2160
0
              status = U_ILLEGAL_ARGUMENT_ERROR;
2161
0
              return;
2162
0
          }
2163
0
        }
2164
0
      }
2165
      // Fall through into normal handling
2166
0
      U_FALLTHROUGH;
2167
0
    case UCAL_EXTENDED_YEAR:
2168
0
    case UCAL_MONTH:
2169
0
    case UCAL_ORDINAL_MONTH:
2170
0
      {
2171
0
        UBool oldLenient = isLenient();
2172
0
        setLenient(true);
2173
0
        int32_t value = get(field, status);
2174
0
        if (U_FAILURE(status)) {
2175
0
            return;
2176
0
        }
2177
0
        if (uprv_add32_overflow(value, amount, &value)) {
2178
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
2179
0
            return;
2180
0
        }
2181
0
        set(field, value);
2182
2183
0
        pinField(UCAL_DAY_OF_MONTH, status);
2184
0
        if(oldLenient==false) {
2185
0
          complete(status); /* force recalculate */
2186
0
          setLenient(oldLenient);
2187
0
        }
2188
0
      }
2189
0
      return;
2190
2191
0
    case UCAL_WEEK_OF_YEAR:
2192
0
    case UCAL_WEEK_OF_MONTH:
2193
0
    case UCAL_DAY_OF_WEEK_IN_MONTH:
2194
0
        delta *= kOneWeek;
2195
0
        break;
2196
2197
0
    case UCAL_AM_PM:
2198
0
        delta *= 12 * kOneHour;
2199
0
        break;
2200
2201
0
    case UCAL_DAY_OF_MONTH:
2202
0
    case UCAL_DAY_OF_YEAR:
2203
0
    case UCAL_DAY_OF_WEEK:
2204
0
    case UCAL_DOW_LOCAL:
2205
0
    case UCAL_JULIAN_DAY:
2206
0
        delta *= kOneDay;
2207
0
        break;
2208
2209
0
    case UCAL_HOUR_OF_DAY:
2210
0
    case UCAL_HOUR:
2211
0
        delta *= kOneHour;
2212
0
        keepWallTimeInvariant = false;
2213
0
        break;
2214
2215
0
    case UCAL_MINUTE:
2216
0
        delta *= kOneMinute;
2217
0
        keepWallTimeInvariant = false;
2218
0
        break;
2219
2220
0
    case UCAL_SECOND:
2221
0
        delta *= kOneSecond;
2222
0
        keepWallTimeInvariant = false;
2223
0
        break;
2224
2225
0
    case UCAL_MILLISECOND:
2226
0
    case UCAL_MILLISECONDS_IN_DAY:
2227
0
        keepWallTimeInvariant = false;
2228
0
        break;
2229
2230
0
    default:
2231
#if defined (U_DEBUG_CAL)
2232
        fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s not addable",
2233
            __FILE__, __LINE__, fldName(field));
2234
#endif
2235
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2236
0
        return;
2237
        //  throw new IllegalArgumentException("Calendar.add(" + fieldName(field) +
2238
        //                                     ") not supported");
2239
0
    }
2240
2241
    // In order to keep the wall time invariant (for fields where this is
2242
    // appropriate), check the combined DST & ZONE offset before and
2243
    // after the add() operation. If it changes, then adjust the millis
2244
    // to compensate.
2245
0
    int32_t prevOffset = 0;
2246
0
    int32_t prevWallTime = 0;
2247
0
    if (keepWallTimeInvariant) {
2248
0
        prevOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status);
2249
0
        prevWallTime = get(UCAL_MILLISECONDS_IN_DAY, status);
2250
0
    }
2251
2252
0
    setTimeInMillis(getTimeInMillis(status) + delta, status);
2253
2254
0
    if (keepWallTimeInvariant) {
2255
0
        int32_t newWallTime = get(UCAL_MILLISECONDS_IN_DAY, status);
2256
0
        if (newWallTime != prevWallTime) {
2257
            // There is at least one zone transition between the base
2258
            // time and the result time. As the result, wall time has
2259
            // changed.
2260
0
            UDate t = internalGetTime();
2261
0
            int32_t newOffset = get(UCAL_DST_OFFSET, status) + get(UCAL_ZONE_OFFSET, status);
2262
0
            if (newOffset != prevOffset) {
2263
                // When the difference of the previous UTC offset and
2264
                // the new UTC offset exceeds 1 full day, we do not want
2265
                // to roll over/back the date. For now, this only happens
2266
                // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452.
2267
0
                int32_t adjAmount = prevOffset - newOffset;
2268
0
                adjAmount = adjAmount >= 0 ? adjAmount % static_cast<int32_t>(kOneDay) : -(-adjAmount % static_cast<int32_t>(kOneDay));
2269
0
                if (adjAmount != 0) {
2270
0
                    setTimeInMillis(t + adjAmount, status);
2271
0
                    newWallTime = get(UCAL_MILLISECONDS_IN_DAY, status);
2272
0
                }
2273
0
                if (newWallTime != prevWallTime) {
2274
                    // The result wall time or adjusted wall time was shifted because
2275
                    // the target wall time does not exist on the result date.
2276
0
                    switch (fSkippedWallTime) {
2277
0
                    case UCAL_WALLTIME_FIRST:
2278
0
                        if (adjAmount > 0) {
2279
0
                            setTimeInMillis(t, status);
2280
0
                        }
2281
0
                        break;
2282
0
                    case UCAL_WALLTIME_LAST:
2283
0
                        if (adjAmount < 0) {
2284
0
                            setTimeInMillis(t, status);
2285
0
                        }
2286
0
                        break;
2287
0
                    case UCAL_WALLTIME_NEXT_VALID:
2288
0
                        UDate tmpT = adjAmount > 0 ? internalGetTime() : t;
2289
0
                        UDate immediatePrevTrans;
2290
0
                        UBool hasTransition = getImmediatePreviousZoneTransition(tmpT, &immediatePrevTrans, status);
2291
0
                        if (U_SUCCESS(status) && hasTransition) {
2292
0
                            setTimeInMillis(immediatePrevTrans, status);
2293
0
                        }
2294
0
                        break;
2295
0
                    }
2296
0
                }
2297
0
            }
2298
0
        }
2299
0
    }
2300
0
}
2301
2302
// -------------------------------------
2303
0
int32_t Calendar::fieldDifference(UDate when, EDateFields field, UErrorCode& status) {
2304
0
    return fieldDifference(when, static_cast<UCalendarDateFields>(field), status);
2305
0
}
2306
2307
0
int32_t Calendar::fieldDifference(UDate targetMs, UCalendarDateFields field, UErrorCode& ec) {
2308
0
    if (U_FAILURE(ec)) {
2309
0
        return 0;
2310
0
    }
2311
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
2312
0
        ec = U_ILLEGAL_ARGUMENT_ERROR;
2313
0
        return 0;
2314
0
    }
2315
0
    int32_t min = 0;
2316
0
    double startMs = getTimeInMillis(ec);
2317
    // Always add from the start millis.  This accommodates
2318
    // operations like adding years from February 29, 2000 up to
2319
    // February 29, 2004.  If 1, 1, 1, 1 is added to the year
2320
    // field, the DOM gets pinned to 28 and stays there, giving an
2321
    // incorrect DOM difference of 1.  We have to add 1, reset, 2,
2322
    // reset, 3, reset, 4.
2323
0
    if (startMs < targetMs) {
2324
0
        int32_t max = 1;
2325
        // Find a value that is too large
2326
0
        while (U_SUCCESS(ec)) {
2327
0
            setTimeInMillis(startMs, ec);
2328
0
            add(field, max, ec);
2329
0
            double ms = getTimeInMillis(ec);
2330
0
            if (ms == targetMs) {
2331
0
                return max;
2332
0
            } else if (ms > targetMs) {
2333
0
                break;
2334
0
            } else if (max < INT32_MAX) {
2335
0
                min = max;
2336
0
                max <<= 1;
2337
0
                if (max < 0) {
2338
0
                    max = INT32_MAX;
2339
0
                }
2340
0
            } else {
2341
                // Field difference too large to fit into int32_t
2342
#if defined (U_DEBUG_CAL)
2343
                fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
2344
                    __FILE__, __LINE__, fldName(field));
2345
#endif
2346
0
                ec = U_ILLEGAL_ARGUMENT_ERROR;
2347
0
                return 0;
2348
0
            }
2349
0
        }
2350
        // Do a binary search
2351
0
        while ((max - min) > 1 && U_SUCCESS(ec)) {
2352
0
            int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX
2353
0
            setTimeInMillis(startMs, ec);
2354
0
            add(field, t, ec);
2355
0
            double ms = getTimeInMillis(ec);
2356
0
            if (ms == targetMs) {
2357
0
                return t;
2358
0
            } else if (ms > targetMs) {
2359
0
                max = t;
2360
0
            } else {
2361
0
                min = t;
2362
0
            }
2363
0
        }
2364
0
    } else if (startMs > targetMs) {
2365
0
        int32_t max = -1;
2366
        // Find a value that is too small
2367
0
        while (U_SUCCESS(ec)) {
2368
0
            setTimeInMillis(startMs, ec);
2369
0
            add(field, max, ec);
2370
0
            double ms = getTimeInMillis(ec);
2371
0
            if (ms == targetMs) {
2372
0
                return max;
2373
0
            } else if (ms < targetMs) {
2374
0
                break;
2375
0
            } else {
2376
0
                min = max;
2377
0
                max = static_cast<int32_t>(static_cast<uint32_t>(max) << 1);
2378
0
                if (max == 0) {
2379
                    // Field difference too large to fit into int32_t
2380
#if defined (U_DEBUG_CAL)
2381
                    fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
2382
                        __FILE__, __LINE__, fldName(field));
2383
#endif
2384
0
                    ec = U_ILLEGAL_ARGUMENT_ERROR;
2385
0
                    return 0;
2386
0
                }
2387
0
            }
2388
0
        }
2389
        // Do a binary search
2390
0
        while ((min - max) > 1 && U_SUCCESS(ec)) {
2391
0
            int32_t t = min + (max - min)/2; // make sure intermediate values don't exceed INT32_MAX
2392
0
            setTimeInMillis(startMs, ec);
2393
0
            add(field, t, ec);
2394
0
            double ms = getTimeInMillis(ec);
2395
0
            if (ms == targetMs) {
2396
0
                return t;
2397
0
            } else if (ms < targetMs) {
2398
0
                max = t;
2399
0
            } else {
2400
0
                min = t;
2401
0
            }
2402
0
        }
2403
0
    }
2404
    // Set calendar to end point
2405
0
    setTimeInMillis(startMs, ec);
2406
0
    add(field, min, ec);
2407
2408
    /* Test for buffer overflows */
2409
0
    if(U_FAILURE(ec)) {
2410
0
        return 0;
2411
0
    }
2412
0
    return min;
2413
0
}
2414
2415
// -------------------------------------
2416
2417
void
2418
Calendar::adoptTimeZone(TimeZone* zone)
2419
0
{
2420
    // Do nothing if passed-in zone is nullptr
2421
0
    if (zone == nullptr) {
2422
0
        return;
2423
0
    }
2424
2425
    // fZone should always be non-null
2426
0
    delete fZone;
2427
0
    fZone = zone;
2428
2429
    // if the zone changes, we need to recompute the time fields
2430
0
    fAreFieldsSet = false;
2431
0
}
2432
2433
// -------------------------------------
2434
void
2435
Calendar::setTimeZone(const TimeZone& zone)
2436
0
{
2437
0
    adoptTimeZone(zone.clone());
2438
0
}
2439
2440
// -------------------------------------
2441
2442
const TimeZone&
2443
Calendar::getTimeZone() const
2444
0
{
2445
0
    U_ASSERT(fZone != nullptr);
2446
0
    return *fZone;
2447
0
}
2448
2449
// -------------------------------------
2450
2451
TimeZone*
2452
Calendar::orphanTimeZone()
2453
0
{
2454
    // we let go of the time zone; the new time zone is the system default time zone
2455
0
    TimeZone *defaultZone = TimeZone::createDefault();
2456
0
    if (defaultZone == nullptr) {
2457
        // No error handling available. Must keep fZone non-nullptr, there are many unchecked uses.
2458
0
        return nullptr;
2459
0
    }
2460
0
    TimeZone *z = fZone;
2461
0
    fZone = defaultZone;
2462
0
    return z;
2463
0
}
2464
2465
// -------------------------------------
2466
2467
void
2468
Calendar::setLenient(UBool lenient)
2469
0
{
2470
0
    fLenient = lenient;
2471
0
}
2472
2473
// -------------------------------------
2474
2475
UBool
2476
Calendar::isLenient() const
2477
0
{
2478
0
    return fLenient;
2479
0
}
2480
2481
// -------------------------------------
2482
2483
void
2484
Calendar::setRepeatedWallTimeOption(UCalendarWallTimeOption option)
2485
0
{
2486
0
    if (option == UCAL_WALLTIME_LAST || option == UCAL_WALLTIME_FIRST) {
2487
0
        fRepeatedWallTime = option;
2488
0
    }
2489
0
}
2490
2491
// -------------------------------------
2492
2493
UCalendarWallTimeOption
2494
Calendar::getRepeatedWallTimeOption() const
2495
0
{
2496
0
    return fRepeatedWallTime;
2497
0
}
2498
2499
// -------------------------------------
2500
2501
void
2502
Calendar::setSkippedWallTimeOption(UCalendarWallTimeOption option)
2503
0
{
2504
0
    fSkippedWallTime = option;
2505
0
}
2506
2507
// -------------------------------------
2508
2509
UCalendarWallTimeOption
2510
Calendar::getSkippedWallTimeOption() const
2511
0
{
2512
0
    return fSkippedWallTime;
2513
0
}
2514
2515
// -------------------------------------
2516
2517
void
2518
0
Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value) UPRV_NO_SANITIZE_UNDEFINED {
2519
0
    if (fFirstDayOfWeek != value &&
2520
0
        value >= UCAL_SUNDAY && value <= UCAL_SATURDAY) {
2521
0
            fFirstDayOfWeek = value;
2522
0
            fAreFieldsSet = false;
2523
0
        }
2524
0
}
2525
2526
// -------------------------------------
2527
2528
Calendar::EDaysOfWeek
2529
Calendar::getFirstDayOfWeek() const
2530
0
{
2531
0
    return static_cast<Calendar::EDaysOfWeek>(fFirstDayOfWeek);
2532
0
}
2533
2534
UCalendarDaysOfWeek
2535
Calendar::getFirstDayOfWeek(UErrorCode & /*status*/) const
2536
0
{
2537
0
    return fFirstDayOfWeek;
2538
0
}
2539
// -------------------------------------
2540
2541
void
2542
Calendar::setMinimalDaysInFirstWeek(uint8_t value)
2543
0
{
2544
    // Values less than 1 have the same effect as 1; values greater
2545
    // than 7 have the same effect as 7. However, we normalize values
2546
    // so operator== and so forth work.
2547
0
    if (value < 1) {
2548
0
        value = 1;
2549
0
    } else if (value > 7) {
2550
0
        value = 7;
2551
0
    }
2552
0
    if (fMinimalDaysInFirstWeek != value) {
2553
0
        fMinimalDaysInFirstWeek = value;
2554
0
        fAreFieldsSet = false;
2555
0
    }
2556
0
}
2557
2558
// -------------------------------------
2559
2560
uint8_t
2561
Calendar::getMinimalDaysInFirstWeek() const
2562
0
{
2563
0
    return fMinimalDaysInFirstWeek;
2564
0
}
2565
2566
// -------------------------------------
2567
// weekend functions, just dummy implementations for now (for API freeze)
2568
2569
UCalendarWeekdayType
2570
Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const
2571
0
{
2572
0
    if (U_FAILURE(status)) {
2573
0
        return UCAL_WEEKDAY;
2574
0
    }
2575
0
    if (dayOfWeek < UCAL_SUNDAY || dayOfWeek > UCAL_SATURDAY) {
2576
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2577
0
        return UCAL_WEEKDAY;
2578
0
    }
2579
0
    if (fWeekendOnset == fWeekendCease) {
2580
0
        if (dayOfWeek != fWeekendOnset)
2581
0
            return UCAL_WEEKDAY;
2582
0
        return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET;
2583
0
    }
2584
0
    if (fWeekendOnset < fWeekendCease) {
2585
0
        if (dayOfWeek < fWeekendOnset || dayOfWeek > fWeekendCease) {
2586
0
            return UCAL_WEEKDAY;
2587
0
        }
2588
0
    } else {
2589
0
        if (dayOfWeek > fWeekendCease && dayOfWeek < fWeekendOnset) {
2590
0
            return UCAL_WEEKDAY;
2591
0
        }
2592
0
    }
2593
0
    if (dayOfWeek == fWeekendOnset) {
2594
0
        return (fWeekendOnsetMillis == 0) ? UCAL_WEEKEND : UCAL_WEEKEND_ONSET;
2595
0
    }
2596
0
    if (dayOfWeek == fWeekendCease) {
2597
0
        return (fWeekendCeaseMillis >= 86400000) ? UCAL_WEEKEND : UCAL_WEEKEND_CEASE;
2598
0
    }
2599
0
    return UCAL_WEEKEND;
2600
0
}
2601
2602
int32_t
2603
Calendar::getWeekendTransition(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const
2604
0
{
2605
0
    if (U_FAILURE(status)) {
2606
0
        return 0;
2607
0
    }
2608
0
    if (dayOfWeek == fWeekendOnset) {
2609
0
        return fWeekendOnsetMillis;
2610
0
    } else if (dayOfWeek == fWeekendCease) {
2611
0
        return fWeekendCeaseMillis;
2612
0
    }
2613
0
    status = U_ILLEGAL_ARGUMENT_ERROR;
2614
0
    return 0;
2615
0
}
2616
2617
UBool
2618
Calendar::isWeekend(UDate date, UErrorCode &status) const
2619
0
{
2620
0
    if (U_FAILURE(status)) {
2621
0
        return false;
2622
0
    }
2623
    // clone the calendar so we don't mess with the real one.
2624
0
    Calendar *work = this->clone();
2625
0
    if (work == nullptr) {
2626
0
        status = U_MEMORY_ALLOCATION_ERROR;
2627
0
        return false;
2628
0
    }
2629
0
    UBool result = false;
2630
0
    work->setTime(date, status);
2631
0
    if (U_SUCCESS(status)) {
2632
0
        result = work->isWeekend();
2633
0
    }
2634
0
    delete work;
2635
0
    return result;
2636
0
}
2637
2638
UBool
2639
Calendar::isWeekend() const
2640
0
{
2641
0
    UErrorCode status = U_ZERO_ERROR;
2642
0
    UCalendarDaysOfWeek dayOfWeek = static_cast<UCalendarDaysOfWeek>(get(UCAL_DAY_OF_WEEK, status));
2643
0
    UCalendarWeekdayType dayType = getDayOfWeekType(dayOfWeek, status);
2644
0
    if (U_SUCCESS(status)) {
2645
0
        switch (dayType) {
2646
0
            case UCAL_WEEKDAY:
2647
0
                return false;
2648
0
            case UCAL_WEEKEND:
2649
0
                return true;
2650
0
            case UCAL_WEEKEND_ONSET:
2651
0
            case UCAL_WEEKEND_CEASE:
2652
                // Use internalGet() because the above call to get() populated all fields.
2653
0
                {
2654
0
                    int32_t millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY);
2655
0
                    int32_t transitionMillis = getWeekendTransition(dayOfWeek, status);
2656
0
                    if (U_SUCCESS(status)) {
2657
0
                        return (dayType == UCAL_WEEKEND_ONSET)?
2658
0
                            (millisInDay >= transitionMillis):
2659
0
                            (millisInDay <  transitionMillis);
2660
0
                    }
2661
                    // else fall through, return false
2662
0
                    U_FALLTHROUGH;
2663
0
                }
2664
0
            default:
2665
0
                break;
2666
0
        }
2667
0
    }
2668
0
    return false;
2669
0
}
2670
2671
// ------------------------------------- limits
2672
2673
int32_t
2674
0
Calendar::getMinimum(EDateFields field) const {
2675
0
    return getLimit(static_cast<UCalendarDateFields>(field), UCAL_LIMIT_MINIMUM);
2676
0
}
2677
2678
int32_t
2679
Calendar::getMinimum(UCalendarDateFields field) const
2680
0
{
2681
0
    return getLimit(field,UCAL_LIMIT_MINIMUM);
2682
0
}
2683
2684
// -------------------------------------
2685
int32_t
2686
Calendar::getMaximum(EDateFields field) const
2687
0
{
2688
0
    return getLimit(static_cast<UCalendarDateFields>(field), UCAL_LIMIT_MAXIMUM);
2689
0
}
2690
2691
int32_t
2692
Calendar::getMaximum(UCalendarDateFields field) const
2693
0
{
2694
0
    return getLimit(field,UCAL_LIMIT_MAXIMUM);
2695
0
}
2696
2697
// -------------------------------------
2698
int32_t
2699
Calendar::getGreatestMinimum(EDateFields field) const
2700
0
{
2701
0
    return getLimit(static_cast<UCalendarDateFields>(field), UCAL_LIMIT_GREATEST_MINIMUM);
2702
0
}
2703
2704
int32_t
2705
Calendar::getGreatestMinimum(UCalendarDateFields field) const
2706
0
{
2707
0
    return getLimit(field,UCAL_LIMIT_GREATEST_MINIMUM);
2708
0
}
2709
2710
// -------------------------------------
2711
int32_t
2712
Calendar::getLeastMaximum(EDateFields field) const
2713
0
{
2714
0
    return getLimit(static_cast<UCalendarDateFields>(field), UCAL_LIMIT_LEAST_MAXIMUM);
2715
0
}
2716
2717
int32_t
2718
Calendar::getLeastMaximum(UCalendarDateFields field) const
2719
0
{
2720
0
    return getLimit( field,UCAL_LIMIT_LEAST_MAXIMUM);
2721
0
}
2722
2723
// -------------------------------------
2724
int32_t
2725
Calendar::getActualMinimum(EDateFields field, UErrorCode& status) const
2726
0
{
2727
0
    return getActualMinimum(static_cast<UCalendarDateFields>(field), status);
2728
0
}
2729
2730
0
int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) const {
2731
0
    switch (field) {
2732
0
    case UCAL_DAY_OF_WEEK:
2733
0
    case UCAL_AM_PM:
2734
0
    case UCAL_HOUR:
2735
0
    case UCAL_HOUR_OF_DAY:
2736
0
    case UCAL_MINUTE:
2737
0
    case UCAL_SECOND:
2738
0
    case UCAL_MILLISECOND:
2739
0
    case UCAL_ZONE_OFFSET:
2740
0
    case UCAL_DST_OFFSET:
2741
0
    case UCAL_DOW_LOCAL:
2742
0
    case UCAL_JULIAN_DAY:
2743
0
    case UCAL_MILLISECONDS_IN_DAY:
2744
0
    case UCAL_IS_LEAP_MONTH:
2745
0
        return kCalendarLimits[field][limitType];
2746
2747
0
    case UCAL_WEEK_OF_MONTH:
2748
0
        {
2749
0
            int32_t limit;
2750
0
            if (limitType == UCAL_LIMIT_MINIMUM) {
2751
0
                limit = getMinimalDaysInFirstWeek() == 1 ? 1 : 0;
2752
0
            } else if (limitType == UCAL_LIMIT_GREATEST_MINIMUM) {
2753
0
                limit = 1;
2754
0
            } else {
2755
0
                int32_t minDaysInFirst = getMinimalDaysInFirstWeek();
2756
0
                int32_t daysInMonth = handleGetLimit(UCAL_DAY_OF_MONTH, limitType);
2757
0
                if (limitType == UCAL_LIMIT_LEAST_MAXIMUM) {
2758
0
                    limit = (daysInMonth + (7 - minDaysInFirst)) / 7;
2759
0
                } else { // limitType == UCAL_LIMIT_MAXIMUM
2760
0
                    limit = (daysInMonth + 6 + (7 - minDaysInFirst)) / 7;
2761
0
                }
2762
0
            }
2763
0
            return limit;
2764
0
        }
2765
0
    default:
2766
0
        return handleGetLimit(field, limitType);
2767
0
    }
2768
0
}
2769
2770
int32_t
2771
Calendar::getActualMinimum(UCalendarDateFields field, UErrorCode& status) const
2772
0
{
2773
0
    if (U_FAILURE(status)) {
2774
0
       return 0;
2775
0
    }
2776
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
2777
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2778
0
        return 0;
2779
0
    }
2780
0
    int32_t fieldValue = getGreatestMinimum(field);
2781
0
    int32_t endValue = getMinimum(field);
2782
2783
    // if we know that the minimum value is always the same, just return it
2784
0
    if (fieldValue == endValue) {
2785
0
        return fieldValue;
2786
0
    }
2787
2788
    // clone the calendar so we don't mess with the real one, and set it to
2789
    // accept anything for the field values
2790
0
    Calendar *work = this->clone();
2791
0
    if (work == nullptr) {
2792
0
        status = U_MEMORY_ALLOCATION_ERROR;
2793
0
        return 0;
2794
0
    }
2795
0
    work->setLenient(true);
2796
2797
    // now try each value from getLeastMaximum() to getMaximum() one by one until
2798
    // we get a value that normalizes to another value.  The last value that
2799
    // normalizes to itself is the actual minimum for the current date
2800
0
    int32_t result = fieldValue;
2801
2802
0
    do {
2803
0
        work->set(field, fieldValue);
2804
0
        if (work->get(field, status) != fieldValue) {
2805
0
            break;
2806
0
        }
2807
0
        else {
2808
0
            result = fieldValue;
2809
0
            fieldValue--;
2810
0
        }
2811
0
    } while (fieldValue >= endValue);
2812
2813
0
    delete work;
2814
2815
    /* Test for buffer overflows */
2816
0
    if(U_FAILURE(status)) {
2817
0
        return 0;
2818
0
    }
2819
0
    return result;
2820
0
}
2821
2822
// -------------------------------------
2823
2824
UBool
2825
Calendar::inDaylightTime(UErrorCode& status) const
2826
0
{
2827
0
    if (U_FAILURE(status) || !getTimeZone().useDaylightTime()) {
2828
0
        return false;
2829
0
    }
2830
2831
    // Force an update of the state of the Calendar.
2832
0
    const_cast<Calendar*>(this)->complete(status); // cast away const
2833
2834
0
    return U_SUCCESS(status) ? internalGet(UCAL_DST_OFFSET) != 0 : false;
2835
0
}
2836
2837
bool
2838
Calendar::inTemporalLeapYear(UErrorCode& status) const
2839
0
{
2840
    // Default to Gregorian based leap year rule.
2841
0
    return getActualMaximum(UCAL_DAY_OF_YEAR, status) == 366;
2842
0
}
2843
2844
// -------------------------------------
2845
2846
static const char * const gTemporalMonthCodes[] = {
2847
    "M01", "M02", "M03", "M04", "M05", "M06",
2848
    "M07", "M08", "M09", "M10", "M11", "M12", nullptr
2849
};
2850
2851
const char*
2852
Calendar::getTemporalMonthCode(UErrorCode& status) const
2853
0
{
2854
0
    int32_t month = get(UCAL_MONTH, status);
2855
0
    if (U_FAILURE(status)) {
2856
0
        return nullptr;
2857
0
    }
2858
0
    U_ASSERT(month < 12);
2859
0
    U_ASSERT(internalGet(UCAL_IS_LEAP_MONTH) == 0);
2860
0
    return gTemporalMonthCodes[month];
2861
0
}
2862
2863
void
2864
Calendar::setTemporalMonthCode(const char* code, UErrorCode& status )
2865
0
{
2866
0
    if (U_FAILURE(status)) {
2867
0
        return;
2868
0
    }
2869
0
    int32_t len = static_cast<int32_t>(uprv_strlen(code));
2870
0
    if (len == 3 && code[0] == 'M') {
2871
0
        for (int m = 0; gTemporalMonthCodes[m] != nullptr; m++) {
2872
0
            if (uprv_strcmp(code, gTemporalMonthCodes[m]) == 0) {
2873
0
                set(UCAL_MONTH, m);
2874
0
                set(UCAL_IS_LEAP_MONTH, 0);
2875
0
                return;
2876
0
            }
2877
0
        }
2878
0
    }
2879
0
    status = U_ILLEGAL_ARGUMENT_ERROR;
2880
0
}
2881
2882
// -------------------------------------
2883
2884
/**
2885
* Ensure that each field is within its valid range by calling {@link
2886
* #validateField(int)} on each field that has been set.  This method
2887
* should only be called if this calendar is not lenient.
2888
* @see #isLenient
2889
* @see #validateField(int)
2890
*/
2891
0
void Calendar::validateFields(UErrorCode &status) {
2892
0
    if (U_FAILURE(status)) {
2893
0
       return;
2894
0
    }
2895
0
    for (int32_t field = 0; U_SUCCESS(status) && (field < UCAL_FIELD_COUNT); field++) {
2896
0
        if (fStamp[field] >= kMinimumUserStamp) {
2897
0
            validateField(static_cast<UCalendarDateFields>(field), status);
2898
0
        }
2899
0
    }
2900
0
}
2901
2902
/**
2903
* Validate a single field of this calendar.  Subclasses should
2904
* override this method to validate any calendar-specific fields.
2905
* Generic fields can be handled by
2906
* <code>Calendar.validateField()</code>.
2907
* @see #validateField(int, int, int)
2908
*/
2909
0
void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) {
2910
0
    if (U_FAILURE(status)) {
2911
0
       return;
2912
0
    }
2913
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
2914
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2915
0
        return;
2916
0
    }
2917
0
    int32_t y;
2918
0
    switch (field) {
2919
0
    case UCAL_DAY_OF_MONTH:
2920
0
        y = handleGetExtendedYear(status);
2921
0
        if (U_FAILURE(status)) {
2922
0
           return;
2923
0
        }
2924
0
        validateField(field, 1, handleGetMonthLength(y, internalGetMonth(status), status), status);
2925
0
        break;
2926
0
    case UCAL_DAY_OF_YEAR:
2927
0
        y = handleGetExtendedYear(status);
2928
0
        if (U_FAILURE(status)) {
2929
0
           return;
2930
0
        }
2931
0
        validateField(field, 1, handleGetYearLength(y, status), status);
2932
0
        break;
2933
0
    case UCAL_DAY_OF_WEEK_IN_MONTH:
2934
0
        if (internalGet(field) == 0) {
2935
#if defined (U_DEBUG_CAL)
2936
            fprintf(stderr, "%s:%d: ILLEGAL ARG because DOW in month cannot be 0\n",
2937
                __FILE__, __LINE__);
2938
#endif
2939
0
            status = U_ILLEGAL_ARGUMENT_ERROR; // "DAY_OF_WEEK_IN_MONTH cannot be zero"
2940
0
            return;
2941
0
        }
2942
0
        validateField(field, getMinimum(field), getMaximum(field), status);
2943
0
        break;
2944
0
    default:
2945
0
        validateField(field, getMinimum(field), getMaximum(field), status);
2946
0
        break;
2947
0
    }
2948
0
}
2949
2950
/**
2951
* Validate a single field of this calendar given its minimum and
2952
* maximum allowed value.  If the field is out of range, throw a
2953
* descriptive <code>IllegalArgumentException</code>.  Subclasses may
2954
* use this method in their implementation of {@link
2955
* #validateField(int)}.
2956
*/
2957
void Calendar::validateField(UCalendarDateFields field, int32_t min, int32_t max, UErrorCode& status)
2958
0
{
2959
0
    if (U_FAILURE(status)) {
2960
0
       return;
2961
0
    }
2962
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
2963
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2964
0
        return;
2965
0
    }
2966
0
    int32_t value = fFields[field];
2967
0
    if (value < min || value > max) {
2968
#if defined (U_DEBUG_CAL)
2969
        fprintf(stderr, "%s:%d: ILLEGAL ARG because of field %s out of range %d..%d  at %d\n",
2970
            __FILE__, __LINE__,fldName(field),min,max,value);
2971
#endif
2972
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2973
0
        return;
2974
0
    }
2975
0
}
2976
2977
// -------------------------
2978
2979
0
const UFieldResolutionTable* Calendar::getFieldResolutionTable() const {
2980
0
    return kDatePrecedence;
2981
0
}
2982
2983
2984
UCalendarDateFields Calendar::newerField(UCalendarDateFields defaultField, UCalendarDateFields alternateField) const
2985
0
{
2986
0
    if (fStamp[alternateField] > fStamp[defaultField]) {
2987
0
        return alternateField;
2988
0
    }
2989
0
    return defaultField;
2990
0
}
2991
2992
0
UCalendarDateFields Calendar::resolveFields(const UFieldResolutionTable* precedenceTable) const {
2993
0
    int32_t bestField = UCAL_FIELD_COUNT;
2994
0
    int32_t tempBestField;
2995
0
    for (int32_t g=0; precedenceTable[g][0][0] != -1 && (bestField == UCAL_FIELD_COUNT); ++g) {
2996
0
        int32_t bestStamp = kUnset;
2997
0
        for (int32_t l=0; precedenceTable[g][l][0] != -1; ++l) {
2998
0
            int32_t lineStamp = kUnset;
2999
            // Skip over first entry if it is negative
3000
0
            for (int32_t i=((precedenceTable[g][l][0]>=kResolveRemap)?1:0); precedenceTable[g][l][i]!=-1; ++i) {
3001
0
                U_ASSERT(precedenceTable[g][l][i] < UCAL_FIELD_COUNT);
3002
0
                int32_t s = fStamp[precedenceTable[g][l][i]];
3003
                // If any field is unset then don't use this line
3004
0
                if (s == kUnset) {
3005
0
                    goto linesInGroup;
3006
0
                } else if(s > lineStamp) {
3007
0
                    lineStamp = s;
3008
0
                }
3009
0
            }
3010
            // Record new maximum stamp & field no.
3011
0
            if (lineStamp > bestStamp) {
3012
0
                tempBestField = precedenceTable[g][l][0]; // First field refers to entire line
3013
0
                if (tempBestField >= kResolveRemap) {
3014
0
                    tempBestField &= (kResolveRemap-1);
3015
                    // This check is needed to resolve some issues with UCAL_YEAR precedence mapping
3016
0
                    if (tempBestField != UCAL_DATE || (fStamp[UCAL_WEEK_OF_MONTH] < fStamp[tempBestField])) {
3017
0
                        bestField = tempBestField;
3018
0
                    }
3019
0
                } else {
3020
0
                    bestField = tempBestField;
3021
0
                }
3022
3023
0
                if (bestField == tempBestField) {
3024
0
                    bestStamp = lineStamp;
3025
0
                }
3026
0
            }
3027
0
linesInGroup:
3028
0
            ;
3029
0
        }
3030
0
    }
3031
0
    return static_cast<UCalendarDateFields>(bestField);
3032
0
}
3033
3034
const UFieldResolutionTable Calendar::kDatePrecedence[] =
3035
{
3036
    {
3037
        { UCAL_DAY_OF_MONTH, kResolveSTOP },
3038
        { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP },
3039
        { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
3040
        { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
3041
        { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP },
3042
        { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
3043
        { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
3044
        { UCAL_DAY_OF_YEAR, kResolveSTOP },
3045
        { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_YEAR, kResolveSTOP },  // if YEAR is set over YEAR_WOY use DAY_OF_MONTH
3046
        { kResolveRemap | UCAL_WEEK_OF_YEAR, UCAL_YEAR_WOY, kResolveSTOP },  // if YEAR_WOY is set,  calc based on WEEK_OF_YEAR
3047
        { kResolveSTOP }
3048
    },
3049
    {
3050
        { UCAL_WEEK_OF_YEAR, kResolveSTOP },
3051
        { UCAL_WEEK_OF_MONTH, kResolveSTOP },
3052
        { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP },
3053
        { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
3054
        { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
3055
        { kResolveSTOP }
3056
    },
3057
    {{kResolveSTOP}}
3058
};
3059
3060
3061
const UFieldResolutionTable Calendar::kMonthPrecedence[] =
3062
{
3063
    {
3064
        { UCAL_MONTH,kResolveSTOP, kResolveSTOP },
3065
        { UCAL_ORDINAL_MONTH,kResolveSTOP, kResolveSTOP },
3066
        {kResolveSTOP}
3067
    },
3068
    {{kResolveSTOP}}
3069
};
3070
3071
const UFieldResolutionTable Calendar::kDOWPrecedence[] =
3072
{
3073
    {
3074
        { UCAL_DAY_OF_WEEK,kResolveSTOP, kResolveSTOP },
3075
        { UCAL_DOW_LOCAL,kResolveSTOP, kResolveSTOP },
3076
        {kResolveSTOP}
3077
    },
3078
    {{kResolveSTOP}}
3079
};
3080
3081
// precedence for calculating a year
3082
const UFieldResolutionTable Calendar::kYearPrecedence[] =
3083
{
3084
    {
3085
        { UCAL_YEAR, kResolveSTOP },
3086
        { UCAL_EXTENDED_YEAR, kResolveSTOP },
3087
        { UCAL_YEAR_WOY, UCAL_WEEK_OF_YEAR, kResolveSTOP },  // YEAR_WOY is useless without WEEK_OF_YEAR
3088
        { kResolveSTOP }
3089
    },
3090
    {{kResolveSTOP}}
3091
};
3092
3093
3094
// -------------------------
3095
3096
3097
0
void Calendar::computeTime(UErrorCode& status) {
3098
0
    if (U_FAILURE(status)) {
3099
0
       return;
3100
0
    }
3101
0
    if (!isLenient()) {
3102
0
        validateFields(status);
3103
0
        if (U_FAILURE(status)) {
3104
0
            return;
3105
0
        }
3106
0
    }
3107
3108
    // Compute the Julian day
3109
0
    int32_t julianDay = computeJulianDay(status);
3110
0
    if (U_FAILURE(status)) {
3111
0
        return;
3112
0
    }
3113
3114
0
    double millis = Grego::julianDayToMillis(julianDay);
3115
3116
#if defined (U_DEBUG_CAL)
3117
    //  int32_t julianInsanityCheck =  (int32_t)ClockMath::floorDivide(millis, kOneDay);
3118
    //  julianInsanityCheck += kEpochStartAsJulianDay;
3119
    //  if(1 || julianInsanityCheck != julianDay) {
3120
    //    fprintf(stderr, "%s:%d- D'oh- computed jules %d, to mills (%s)%.lf, recomputed %d\n",
3121
    //            __FILE__, __LINE__, julianDay, millis<0.0?"NEG":"", millis, julianInsanityCheck);
3122
    //  }
3123
#endif
3124
3125
0
    double millisInDay;
3126
3127
    // We only use MILLISECONDS_IN_DAY if it has been set by the user.
3128
    // This makes it possible for the caller to set the calendar to a
3129
    // time and call clear(MONTH) to reset the MONTH to January.  This
3130
    // is legacy behavior.  Without this, clear(MONTH) has no effect,
3131
    // since the internally set JULIAN_DAY is used.
3132
0
    if (fStamp[UCAL_MILLISECONDS_IN_DAY] >= static_cast<int32_t>(kMinimumUserStamp) &&
3133
0
            newestStamp(UCAL_AM_PM, UCAL_MILLISECOND, kUnset) <= fStamp[UCAL_MILLISECONDS_IN_DAY]) {
3134
0
        millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY);
3135
0
    } else {
3136
0
        millisInDay = computeMillisInDay();
3137
0
    }
3138
3139
0
    UDate t = 0;
3140
0
    if (fStamp[UCAL_ZONE_OFFSET] >= static_cast<int32_t>(kMinimumUserStamp) ||
3141
0
        fStamp[UCAL_DST_OFFSET] >= static_cast<int32_t>(kMinimumUserStamp)) {
3142
0
        t = millis + millisInDay - internalGet(UCAL_ZONE_OFFSET) - internalGet(UCAL_DST_OFFSET);
3143
0
    } else {
3144
        // Compute the time zone offset and DST offset.  There are two potential
3145
        // ambiguities here.  We'll assume a 2:00 am (wall time) switchover time
3146
        // for discussion purposes here.
3147
        //
3148
        // 1. The positive offset change such as transition into DST.
3149
        //    Here, a designated time of 2:00 am - 2:59 am does not actually exist.
3150
        //    For this case, skippedWallTime option specifies the behavior.
3151
        //    For example, 2:30 am is interpreted as;
3152
        //      - WALLTIME_LAST(default): 3:30 am (DST) (interpreting 2:30 am as 31 minutes after 1:59 am (STD))
3153
        //      - WALLTIME_FIRST: 1:30 am (STD) (interpreting 2:30 am as 30 minutes before 3:00 am (DST))
3154
        //      - WALLTIME_NEXT_VALID: 3:00 am (DST) (next valid time after 2:30 am on a wall clock)
3155
        // 2. The negative offset change such as transition out of DST.
3156
        //    Here, a designated time of 1:00 am - 1:59 am can be in standard or DST.  Both are valid
3157
        //    representations (the rep jumps from 1:59:59 DST to 1:00:00 Std).
3158
        //    For this case, repeatedWallTime option specifies the behavior.
3159
        //    For example, 1:30 am is interpreted as;
3160
        //      - WALLTIME_LAST(default): 1:30 am (STD) - latter occurrence
3161
        //      - WALLTIME_FIRST: 1:30 am (DST) - former occurrence
3162
        //
3163
        // In addition to above, when calendar is strict (not default), wall time falls into
3164
        // the skipped time range will be processed as an error case.
3165
        //
3166
        // These special cases are mostly handled in #computeZoneOffset(long), except WALLTIME_NEXT_VALID
3167
        // at positive offset change. The protected method computeZoneOffset(long) is exposed to Calendar
3168
        // subclass implementations and marked as @stable. Strictly speaking, WALLTIME_NEXT_VALID
3169
        // should be also handled in the same place, but we cannot change the code flow without deprecating
3170
        // the protected method.
3171
        //
3172
        // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
3173
        // or DST_OFFSET fields; then we use those fields.
3174
3175
0
        if (!isLenient() || fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID) {
3176
            // When strict, invalidate a wall time falls into a skipped wall time range.
3177
            // When lenient and skipped wall time option is WALLTIME_NEXT_VALID,
3178
            // the result time will be adjusted to the next valid time (on wall clock).
3179
0
            int32_t zoneOffset = computeZoneOffset(millis, millisInDay, status);
3180
0
            UDate tmpTime = millis + millisInDay - zoneOffset;
3181
3182
0
            int32_t raw, dst;
3183
0
            fZone->getOffset(tmpTime, false, raw, dst, status);
3184
3185
0
            if (U_SUCCESS(status)) {
3186
                // zoneOffset != (raw + dst) only when the given wall time fall into
3187
                // a skipped wall time range caused by positive zone offset transition.
3188
0
                if (zoneOffset != (raw + dst)) {
3189
0
                    if (!isLenient()) {
3190
0
                        status = U_ILLEGAL_ARGUMENT_ERROR;
3191
0
                    } else {
3192
0
                        U_ASSERT(fSkippedWallTime == UCAL_WALLTIME_NEXT_VALID);
3193
                        // Adjust time to the next valid wall clock time.
3194
                        // At this point, tmpTime is on or after the zone offset transition causing
3195
                        // the skipped time range.
3196
0
                        UDate immediatePrevTransition;
3197
0
                        UBool hasTransition = getImmediatePreviousZoneTransition(tmpTime, &immediatePrevTransition, status);
3198
0
                        if (U_SUCCESS(status) && hasTransition) {
3199
0
                            t = immediatePrevTransition;
3200
0
                        }
3201
0
                    }
3202
0
                } else {
3203
0
                    t = tmpTime;
3204
0
                }
3205
0
            }
3206
0
        } else {
3207
0
            t = millis + millisInDay - computeZoneOffset(millis, millisInDay, status);
3208
0
        }
3209
0
    }
3210
0
    if (U_SUCCESS(status)) {
3211
0
        internalSetTime(t);
3212
0
    }
3213
0
}
3214
3215
/**
3216
 * Find the previous zone transition near the given time.
3217
 */
3218
0
UBool Calendar::getImmediatePreviousZoneTransition(UDate base, UDate *transitionTime, UErrorCode& status) const {
3219
0
    if (U_FAILURE(status)) {
3220
0
       return false;
3221
0
    }
3222
0
    BasicTimeZone *btz = getBasicTimeZone();
3223
0
    if (btz) {
3224
0
        TimeZoneTransition trans;
3225
0
        UBool hasTransition = btz->getPreviousTransition(base, true, trans);
3226
0
        if (hasTransition) {
3227
0
            *transitionTime = trans.getTime();
3228
0
            return true;
3229
0
        } else {
3230
            // Could not find any transitions.
3231
            // Note: This should never happen.
3232
0
            status = U_INTERNAL_PROGRAM_ERROR;
3233
0
        }
3234
0
    } else {
3235
        // If not BasicTimeZone, return unsupported error for now.
3236
        // TODO: We may support non-BasicTimeZone in future.
3237
0
        status = U_UNSUPPORTED_ERROR;
3238
0
    }
3239
0
    return false;
3240
0
}
3241
3242
/**
3243
* Compute the milliseconds in the day from the fields.  This is a
3244
* value from 0 to 23:59:59.999 inclusive, unless fields are out of
3245
* range, in which case it can be an arbitrary value.  This value
3246
* reflects local zone wall time.
3247
* @stable ICU 2.0
3248
*/
3249
0
double Calendar::computeMillisInDay() {
3250
  // Do the time portion of the conversion.
3251
3252
0
    double millisInDay = 0;
3253
3254
    // Find the best set of fields specifying the time of day.  There
3255
    // are only two possibilities here; the HOUR_OF_DAY or the
3256
    // AM_PM and the HOUR.
3257
0
    int32_t hourOfDayStamp = fStamp[UCAL_HOUR_OF_DAY];
3258
0
    int32_t hourStamp = (fStamp[UCAL_HOUR] > fStamp[UCAL_AM_PM])?fStamp[UCAL_HOUR]:fStamp[UCAL_AM_PM];
3259
0
    int32_t bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp;
3260
3261
    // Hours
3262
0
    if (bestStamp != kUnset) {
3263
0
        if (bestStamp == hourOfDayStamp) {
3264
            // Don't normalize here; let overflow bump into the next period.
3265
            // This is consistent with how we handle other fields.
3266
0
            millisInDay += internalGet(UCAL_HOUR_OF_DAY);
3267
0
        } else {
3268
            // Don't normalize here; let overflow bump into the next period.
3269
            // This is consistent with how we handle other fields.
3270
0
            millisInDay += internalGet(UCAL_HOUR);
3271
            // Treat even number as AM and odd nubmber as PM to align with the
3272
            // logic in roll()
3273
0
            millisInDay += (internalGet(UCAL_AM_PM) % 2 == 0) ? 0 : 12;
3274
0
        }
3275
0
    }
3276
3277
    // We use the fact that unset == 0; we start with millisInDay
3278
    // == HOUR_OF_DAY.
3279
0
    millisInDay *= 60;
3280
0
    millisInDay += internalGet(UCAL_MINUTE); // now have minutes
3281
0
    millisInDay *= 60;
3282
0
    millisInDay += internalGet(UCAL_SECOND); // now have seconds
3283
0
    millisInDay *= 1000;
3284
0
    millisInDay += internalGet(UCAL_MILLISECOND); // now have millis
3285
3286
0
    return millisInDay;
3287
0
}
3288
3289
/**
3290
* This method can assume EXTENDED_YEAR has been set.
3291
* @param millis milliseconds of the date fields
3292
* @param millisInDay milliseconds of the time fields; may be out
3293
* or range.
3294
* @stable ICU 2.0
3295
*/
3296
0
int32_t Calendar::computeZoneOffset(double millis, double millisInDay, UErrorCode &ec) {
3297
0
    if (U_FAILURE(ec)) {
3298
0
       return 0;
3299
0
    }
3300
0
    int32_t rawOffset, dstOffset;
3301
0
    UDate wall = millis + millisInDay;
3302
0
    BasicTimeZone* btz = getBasicTimeZone();
3303
0
    if (btz) {
3304
0
        UTimeZoneLocalOption duplicatedTimeOpt = (fRepeatedWallTime == UCAL_WALLTIME_FIRST) ? UCAL_TZ_LOCAL_FORMER : UCAL_TZ_LOCAL_LATTER;
3305
0
        UTimeZoneLocalOption nonExistingTimeOpt = (fSkippedWallTime == UCAL_WALLTIME_FIRST) ? UCAL_TZ_LOCAL_LATTER : UCAL_TZ_LOCAL_FORMER;
3306
0
        btz->getOffsetFromLocal(wall, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, ec);
3307
0
    } else {
3308
0
        const TimeZone& tz = getTimeZone();
3309
        // By default, TimeZone::getOffset behaves UCAL_WALLTIME_LAST for both.
3310
0
        tz.getOffset(wall, true, rawOffset, dstOffset, ec);
3311
3312
0
        UBool sawRecentNegativeShift = false;
3313
0
        if (fRepeatedWallTime == UCAL_WALLTIME_FIRST) {
3314
            // Check if the given wall time falls into repeated time range
3315
0
            UDate tgmt = wall - (rawOffset + dstOffset);
3316
3317
            // Any negative zone transition within last 6 hours?
3318
            // Note: The maximum historic negative zone transition is -3 hours in the tz database.
3319
            // 6 hour window would be sufficient for this purpose.
3320
0
            int32_t tmpRaw, tmpDst;
3321
0
            tz.getOffset(tgmt - 6*60*60*1000, false, tmpRaw, tmpDst, ec);
3322
0
            int32_t offsetDelta = (rawOffset + dstOffset) - (tmpRaw + tmpDst);
3323
3324
0
            U_ASSERT(offsetDelta < -6*60*60*1000);
3325
0
            if (offsetDelta < 0) {
3326
0
                sawRecentNegativeShift = true;
3327
                // Negative shift within last 6 hours. When UCAL_WALLTIME_FIRST is used and the given wall time falls
3328
                // into the repeated time range, use offsets before the transition.
3329
                // Note: If it does not fall into the repeated time range, offsets remain unchanged below.
3330
0
                tz.getOffset(wall + offsetDelta, true, rawOffset, dstOffset, ec);
3331
0
            }
3332
0
        }
3333
0
        if (!sawRecentNegativeShift && fSkippedWallTime == UCAL_WALLTIME_FIRST) {
3334
            // When skipped wall time option is WALLTIME_FIRST,
3335
            // recalculate offsets from the resolved time (non-wall).
3336
            // When the given wall time falls into skipped wall time,
3337
            // the offsets will be based on the zone offsets AFTER
3338
            // the transition (which means, earliest possible interpretation).
3339
0
            UDate tgmt = wall - (rawOffset + dstOffset);
3340
0
            tz.getOffset(tgmt, false, rawOffset, dstOffset, ec);
3341
0
        }
3342
0
    }
3343
0
    return rawOffset + dstOffset;
3344
0
}
3345
3346
int32_t Calendar::computeJulianDay(UErrorCode &status)
3347
0
{
3348
    // We want to see if any of the date fields is newer than the
3349
    // JULIAN_DAY.  If not, then we use JULIAN_DAY.  If so, then we do
3350
    // the normal resolution.  We only use JULIAN_DAY if it has been
3351
    // set by the user.  This makes it possible for the caller to set
3352
    // the calendar to a time and call clear(MONTH) to reset the MONTH
3353
    // to January.  This is legacy behavior.  Without this,
3354
    // clear(MONTH) has no effect, since the internally set JULIAN_DAY
3355
    // is used.
3356
0
    if (fStamp[UCAL_JULIAN_DAY] >= static_cast<int32_t>(kMinimumUserStamp)) {
3357
0
        int32_t bestStamp = newestStamp(UCAL_ERA, UCAL_DAY_OF_WEEK_IN_MONTH, kUnset);
3358
0
        bestStamp = newestStamp(UCAL_YEAR_WOY, UCAL_EXTENDED_YEAR, bestStamp);
3359
0
        bestStamp = newestStamp(UCAL_ORDINAL_MONTH, UCAL_ORDINAL_MONTH, bestStamp);
3360
0
        if (bestStamp <= fStamp[UCAL_JULIAN_DAY]) {
3361
0
            return internalGet(UCAL_JULIAN_DAY);
3362
0
        }
3363
0
    }
3364
3365
0
    UCalendarDateFields bestField = resolveFields(getFieldResolutionTable());
3366
0
    if (bestField == UCAL_FIELD_COUNT) {
3367
0
        bestField = UCAL_DAY_OF_MONTH;
3368
0
    }
3369
3370
0
    return handleComputeJulianDay(bestField, status);
3371
0
}
3372
3373
// -------------------------------------------
3374
3375
0
int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField, UErrorCode &status)  {
3376
0
    if (U_FAILURE(status)) {
3377
0
       return 0;
3378
0
    }
3379
0
    UBool useMonth = (bestField == UCAL_DAY_OF_MONTH ||
3380
0
        bestField == UCAL_WEEK_OF_MONTH ||
3381
0
        bestField == UCAL_DAY_OF_WEEK_IN_MONTH);
3382
0
    int32_t year;
3383
3384
0
    if (bestField == UCAL_WEEK_OF_YEAR && newerField(UCAL_YEAR_WOY, UCAL_YEAR) == UCAL_YEAR_WOY) {
3385
0
        year = internalGet(UCAL_YEAR_WOY);
3386
0
    } else {
3387
0
        year = handleGetExtendedYear(status);
3388
0
        if (U_FAILURE(status)) {
3389
0
           return 0;
3390
0
        }
3391
0
    }
3392
3393
0
    internalSet(UCAL_EXTENDED_YEAR, year);
3394
    // Return U_ILLEGAL_ARGUMENT_ERROR if year is too large that may cuase int32_t overflow
3395
    // later.
3396
0
    if (year > INT32_MAX / 400) {
3397
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
3398
0
        return 0;
3399
0
    }
3400
3401
#if defined (U_DEBUG_CAL)
3402
    fprintf(stderr, "%s:%d: bestField= %s - y=%d\n", __FILE__, __LINE__, fldName(bestField), year);
3403
#endif
3404
3405
    // Get the Julian day of the day BEFORE the start of this year.
3406
    // If useMonth is true, get the day before the start of the month.
3407
3408
    // give calendar subclass a chance to have a default 'first' month
3409
0
    int32_t month;
3410
3411
0
    if(isSet(UCAL_MONTH) || isSet(UCAL_ORDINAL_MONTH)) {
3412
0
        month = internalGetMonth(status);
3413
0
        if (U_FAILURE(status)) {
3414
0
           return 0;
3415
0
        }
3416
0
    } else {
3417
0
        month = getDefaultMonthInYear(year, status);
3418
0
        if (U_FAILURE(status)) {
3419
0
           return 0;
3420
0
        }
3421
0
    }
3422
3423
0
    int32_t julianDay = handleComputeMonthStart(year, useMonth ? month : 0, useMonth, status);
3424
0
    if (U_FAILURE(status)) {
3425
0
        return 0;
3426
0
    }
3427
3428
0
    if (bestField == UCAL_DAY_OF_MONTH) {
3429
3430
        // give calendar subclass a chance to have a default 'first' dom
3431
0
        int32_t dayOfMonth;
3432
0
        if(isSet(UCAL_DAY_OF_MONTH)) {
3433
0
            dayOfMonth = internalGet(UCAL_DAY_OF_MONTH,1);
3434
0
        } else {
3435
0
            dayOfMonth = getDefaultDayInMonth(year, month, status);
3436
0
            if (U_FAILURE(status)) {
3437
0
               return 0;
3438
0
            }
3439
0
        }
3440
0
        if (uprv_add32_overflow(dayOfMonth, julianDay, &dayOfMonth)) {
3441
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
3442
0
            return 0;
3443
0
        }
3444
0
        return dayOfMonth;
3445
0
    }
3446
3447
0
    if (bestField == UCAL_DAY_OF_YEAR) {
3448
0
        int32_t result;
3449
0
        if (uprv_add32_overflow(internalGet(UCAL_DAY_OF_YEAR), julianDay, &result)) {
3450
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
3451
0
            return 0;
3452
0
        }
3453
0
        return result;
3454
0
    }
3455
3456
0
    int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw
3457
3458
    // At this point julianDay is the 0-based day BEFORE the first day of
3459
    // January 1, year 1 of the given calendar.  If julianDay == 0, it
3460
    // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
3461
    // or Gregorian). (or it is before the month we are in, if useMonth is True)
3462
3463
    // At this point we need to process the WEEK_OF_MONTH or
3464
    // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
3465
    // First, perform initial shared computations.  These locate the
3466
    // first week of the period.
3467
3468
    // Get the 0-based localized DOW of day one of the month or year.
3469
    // Valid range 0..6.
3470
0
    int32_t first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek;
3471
0
    if (first < 0) {
3472
0
        first += 7;
3473
0
    }
3474
3475
0
    int32_t dowLocal = getLocalDOW(status);
3476
0
    if (U_FAILURE(status)) {
3477
0
        return 0;
3478
0
    }
3479
3480
    // Find the first target DOW (dowLocal) in the month or year.
3481
    // Actually, it may be just before the first of the month or year.
3482
    // It will be an integer from -5..7.
3483
0
    int32_t date = 1 - first + dowLocal;
3484
3485
0
    if (bestField == UCAL_DAY_OF_WEEK_IN_MONTH) {
3486
        // Adjust the target DOW to be in the month or year.
3487
0
        if (date < 1) {
3488
0
            date += 7;
3489
0
        }
3490
3491
        // The only trickiness occurs if the day-of-week-in-month is
3492
        // negative.
3493
0
        int32_t dim = internalGet(UCAL_DAY_OF_WEEK_IN_MONTH, 1);
3494
0
        if (dim >= 0) {
3495
0
            int32_t temp;
3496
0
            if (uprv_mul32_overflow(7, dim - 1, &temp)  ||
3497
0
                uprv_add32_overflow(date, temp, &date)) {
3498
0
                status = U_ILLEGAL_ARGUMENT_ERROR;
3499
0
                return 0;
3500
0
            }
3501
3502
0
        } else {
3503
            // Move date to the last of this day-of-week in this month,
3504
            // then back up as needed.  If dim==-1, we don't back up at
3505
            // all.  If dim==-2, we back up once, etc.  Don't back up
3506
            // past the first of the given day-of-week in this month.
3507
            // Note that we handle -2, -3, etc. correctly, even though
3508
            // values < -1 are technically disallowed.
3509
0
            int32_t m = internalGetMonth(UCAL_JANUARY, status);
3510
0
            int32_t monthLength = handleGetMonthLength(year, m, status);
3511
0
            if (U_FAILURE(status)) {
3512
0
                return 0;
3513
0
            }
3514
0
            int32_t temp;
3515
0
            if (uprv_add32_overflow((monthLength - date) / 7, dim+1, &temp) ||
3516
0
                uprv_mul32_overflow(temp, 7, &temp) ||
3517
0
                uprv_add32_overflow(date, temp, &date)) {
3518
0
                status = U_ILLEGAL_ARGUMENT_ERROR;
3519
0
                return 0;
3520
0
            }
3521
0
        }
3522
0
    } else {
3523
#if defined (U_DEBUG_CAL)
3524
        fprintf(stderr, "%s:%d - bf= %s\n", __FILE__, __LINE__, fldName(bestField));
3525
#endif
3526
3527
0
        if(bestField == UCAL_WEEK_OF_YEAR) {  // ------------------------------------- WOY -------------
3528
0
            if(!isSet(UCAL_YEAR_WOY) ||  // YWOY not set at all or
3529
0
                ( (resolveFields(kYearPrecedence) != UCAL_YEAR_WOY) // YWOY doesn't have precedence
3530
0
                && (fStamp[UCAL_YEAR_WOY]!=kInternallySet) ) ) // (excluding where all fields are internally set - then YWOY is used)
3531
0
            {
3532
                // need to be sure to stay in 'real' year.
3533
0
                int32_t woy = internalGet(bestField);
3534
3535
0
                int32_t nextJulianDay = handleComputeMonthStart(year+1, 0, false, status); // jd of day before jan 1
3536
0
                if (U_FAILURE(status)) {
3537
0
                    return 0;
3538
0
                }
3539
0
                int32_t nextFirst = julianDayToDayOfWeek(nextJulianDay + 1) - firstDayOfWeek;
3540
3541
0
                if (nextFirst < 0) { // 0..6 ldow of Jan 1
3542
0
                    nextFirst += 7;
3543
0
                }
3544
3545
0
                if(woy==1) {  // FIRST WEEK ---------------------------------
3546
#if defined (U_DEBUG_CAL)
3547
                    fprintf(stderr, "%s:%d - woy=%d, yp=%d, nj(%d)=%d, nf=%d", __FILE__, __LINE__,
3548
                        internalGet(bestField), resolveFields(kYearPrecedence), year+1,
3549
                        nextJulianDay, nextFirst);
3550
3551
                    fprintf(stderr, " next: %d DFW,  min=%d   \n", (7-nextFirst), getMinimalDaysInFirstWeek() );
3552
#endif
3553
3554
                    // nextFirst is now the localized DOW of Jan 1  of y-woy+1
3555
0
                    if((nextFirst > 0) &&   // Jan 1 starts on FDOW
3556
0
                        (7-nextFirst) >= getMinimalDaysInFirstWeek()) // or enough days in the week
3557
0
                    {
3558
                        // Jan 1 of (yearWoy+1) is in yearWoy+1 - recalculate JD to next year
3559
#if defined (U_DEBUG_CAL)
3560
                        fprintf(stderr, "%s:%d - was going to move JD from %d to %d [d%d]\n", __FILE__, __LINE__,
3561
                            julianDay, nextJulianDay, (nextJulianDay-julianDay));
3562
#endif
3563
0
                        julianDay = nextJulianDay;
3564
3565
                        // recalculate 'first' [0-based local dow of jan 1]
3566
0
                        first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek;
3567
0
                        if (first < 0) {
3568
0
                            first += 7;
3569
0
                        }
3570
                        // recalculate date.
3571
0
                        date = 1 - first + dowLocal;
3572
0
                    }
3573
0
                } else if(woy>=getLeastMaximum(bestField)) {
3574
                    // could be in the last week- find out if this JD would overstep
3575
0
                    int32_t testDate = date;
3576
0
                    if ((7 - first) < getMinimalDaysInFirstWeek()) {
3577
0
                        testDate += 7;
3578
0
                    }
3579
3580
                    // Now adjust for the week number.
3581
0
                    int32_t weeks;
3582
0
                    if (uprv_mul32_overflow(woy-1, 7, &weeks) ||
3583
0
                        uprv_add32_overflow(weeks, testDate, &testDate)) {
3584
0
                        status = U_ILLEGAL_ARGUMENT_ERROR;
3585
0
                        return 0;
3586
0
                    }
3587
3588
#if defined (U_DEBUG_CAL)
3589
                    fprintf(stderr, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n",
3590
                        __FILE__, __LINE__, year, year-1, testDate, julianDay+testDate, nextJulianDay);
3591
#endif
3592
0
                    if (uprv_add32_overflow(julianDay, testDate, &testDate)) {
3593
0
                        status = U_ILLEGAL_ARGUMENT_ERROR;
3594
0
                        return 0;
3595
0
                    }
3596
3597
0
                    if(testDate > nextJulianDay) { // is it past Dec 31?  (nextJulianDay is day BEFORE year+1's  Jan 1)
3598
                        // Fire up the calculating engines.. retry YWOY = (year-1)
3599
0
                        int32_t prevYear;
3600
0
                        if (uprv_add32_overflow(year, -1, &prevYear)) {
3601
0
                            status = U_ILLEGAL_ARGUMENT_ERROR;
3602
0
                            return 0;
3603
0
                        }
3604
0
                        julianDay = handleComputeMonthStart(prevYear, 0, false, status); // jd before Jan 1 of previous year
3605
0
                        if (U_FAILURE(status)) {
3606
0
                            return 0;
3607
0
                        }
3608
0
                        first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; // 0 based local dow   of first week
3609
3610
0
                        if(first < 0) { // 0..6
3611
0
                            first += 7;
3612
0
                        }
3613
0
                        date = 1 - first + dowLocal;
3614
3615
#if defined (U_DEBUG_CAL)
3616
                        fprintf(stderr, "%s:%d - date now %d, jd%d, ywoy%d\n",
3617
                            __FILE__, __LINE__, date, julianDay, year-1);
3618
#endif
3619
3620
3621
0
                    } /* correction needed */
3622
0
                } /* leastmaximum */
3623
0
            } /* resolvefields(year) != year_woy */
3624
0
        } /* bestfield != week_of_year */
3625
3626
        // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR)
3627
        // Adjust for minimal days in first week
3628
0
        if ((7 - first) < getMinimalDaysInFirstWeek()) {
3629
0
            date += 7;
3630
0
        }
3631
3632
        // Now adjust for the week number.
3633
0
        int32_t weeks = internalGet(bestField);
3634
0
        if (uprv_add32_overflow(weeks, -1, &weeks) ||
3635
0
            uprv_mul32_overflow(7, weeks, &weeks) ||
3636
0
            uprv_add32_overflow(date, weeks, &date)) {
3637
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
3638
0
            return 0;
3639
0
        }
3640
0
    }
3641
3642
0
    if (uprv_add32_overflow(julianDay, date, &julianDay)) {
3643
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
3644
0
        return 0;
3645
0
    }
3646
0
    return julianDay;
3647
0
}
3648
3649
int32_t
3650
Calendar::getDefaultMonthInYear(int32_t /*eyear*/, UErrorCode& /* status */)
3651
0
{
3652
0
    return 0;
3653
0
}
3654
3655
int32_t
3656
Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/, UErrorCode& /* status */)
3657
0
{
3658
0
    return 1;
3659
0
}
3660
3661
3662
int32_t Calendar::getLocalDOW(UErrorCode& status)
3663
0
{
3664
0
    if (U_FAILURE(status)) {
3665
0
        return 0;
3666
0
    }
3667
    // Get zero-based localized DOW, valid range 0..6.  This is the DOW
3668
    // we are looking for.
3669
0
    int32_t dowLocal = 0;
3670
0
    switch (resolveFields(kDOWPrecedence)) {
3671
0
    case UCAL_DAY_OF_WEEK:
3672
0
        dowLocal = internalGet(UCAL_DAY_OF_WEEK);
3673
0
        if (uprv_add32_overflow(dowLocal, -fFirstDayOfWeek, &dowLocal)) {
3674
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
3675
0
            return 0;
3676
0
        }
3677
0
        break;
3678
0
    case UCAL_DOW_LOCAL:
3679
0
        dowLocal = internalGet(UCAL_DOW_LOCAL);
3680
0
        if (uprv_add32_overflow(dowLocal, -1, &dowLocal)) {
3681
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
3682
0
            return 0;
3683
0
        }
3684
0
        break;
3685
0
    default:
3686
0
        break;
3687
0
    }
3688
0
    dowLocal = dowLocal % 7;
3689
0
    if (dowLocal < 0) {
3690
0
        dowLocal += 7;
3691
0
    }
3692
0
    return dowLocal;
3693
0
}
3694
3695
int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy, UErrorCode& status)
3696
0
{
3697
0
    if (U_FAILURE(status)) {
3698
0
        return 0;
3699
0
    }
3700
    // We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine
3701
    // what year we fall in, so that other code can set it properly.
3702
    // (code borrowed from computeWeekFields and handleComputeJulianDay)
3703
    //return yearWoy;
3704
3705
    // First, we need a reliable DOW.
3706
0
    UCalendarDateFields bestField = resolveFields(kDatePrecedence); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields
3707
3708
    // Now, a local DOW
3709
0
    int32_t dowLocal = getLocalDOW(status); // 0..6
3710
0
    if (U_FAILURE(status)) {
3711
0
      return 0;
3712
0
    }
3713
0
    int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw
3714
0
    int32_t jan1Start = handleComputeMonthStart(yearWoy, 0, false, status);
3715
0
    int32_t yearWoyPlus1;
3716
0
    if (uprv_add32_overflow(yearWoy, 1, &yearWoyPlus1)) {
3717
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
3718
0
        return 0;
3719
0
    }
3720
0
    int32_t nextJan1Start = handleComputeMonthStart(yearWoyPlus1, 0, false, status); // next year's Jan1 start
3721
0
    if (U_FAILURE(status)) {
3722
0
        return 0;
3723
0
    }
3724
3725
    // At this point julianDay is the 0-based day BEFORE the first day of
3726
    // January 1, year 1 of the given calendar.  If julianDay == 0, it
3727
    // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
3728
    // or Gregorian). (or it is before the month we are in, if useMonth is True)
3729
3730
    // At this point we need to process the WEEK_OF_MONTH or
3731
    // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
3732
    // First, perform initial shared computations.  These locate the
3733
    // first week of the period.
3734
3735
    // Get the 0-based localized DOW of day one of the month or year.
3736
    // Valid range 0..6.
3737
0
    int32_t first = julianDayToDayOfWeek(jan1Start + 1) - firstDayOfWeek;
3738
0
    if (first < 0) {
3739
0
        first += 7;
3740
0
    }
3741
3742
    //// (nextFirst was not used below)
3743
    // int32_t nextFirst = julianDayToDayOfWeek(nextJan1Start + 1) - firstDayOfWeek;
3744
    // if (nextFirst < 0) {
3745
    //     nextFirst += 7;
3746
    //}
3747
3748
0
    int32_t minDays = getMinimalDaysInFirstWeek();
3749
0
    UBool jan1InPrevYear = false;  // January 1st in the year of WOY is the 1st week?  (i.e. first week is < minimal )
3750
    //UBool nextJan1InPrevYear = false; // January 1st of Year of WOY + 1 is in the first week?
3751
3752
0
    if((7 - first) < minDays) {
3753
0
        jan1InPrevYear = true;
3754
0
    }
3755
3756
    //   if((7 - nextFirst) < minDays) {
3757
    //     nextJan1InPrevYear = true;
3758
    //   }
3759
3760
0
    switch(bestField) {
3761
0
    case UCAL_WEEK_OF_YEAR:
3762
0
        if(woy == 1) {
3763
0
            if(jan1InPrevYear) {
3764
                // the first week of January is in the previous year
3765
                // therefore WOY1 is always solidly within yearWoy
3766
0
                return yearWoy;
3767
0
            } else {
3768
                // First WOY is split between two years
3769
0
                if( dowLocal < first) { // we are prior to Jan 1
3770
0
                    return yearWoy-1; // previous year
3771
0
                } else {
3772
0
                    return yearWoy; // in this year
3773
0
                }
3774
0
            }
3775
0
        } else if(woy >= getLeastMaximum(bestField)) {
3776
            // we _might_ be in the last week..
3777
0
            int32_t jd =  // Calculate JD of our target day:
3778
0
                jan1Start +  // JD of Jan 1
3779
0
                (7-first) + //  days in the first week (Jan 1.. )
3780
0
                (woy-1)*7 + // add the weeks of the year
3781
0
                dowLocal;   // the local dow (0..6) of last week
3782
0
            if(jan1InPrevYear==false) {
3783
0
                jd -= 7; // woy already includes Jan 1's week.
3784
0
            }
3785
3786
0
            if( (jd+1) >= nextJan1Start ) {
3787
                // we are in week 52 or 53 etc. - actual year is yearWoy+1
3788
0
                return yearWoy+1;
3789
0
            } else {
3790
                // still in yearWoy;
3791
0
                return yearWoy;
3792
0
            }
3793
0
        } else {
3794
            // we're not possibly in the last week -must be ywoy
3795
0
            return yearWoy;
3796
0
        }
3797
3798
0
    case UCAL_DATE:
3799
0
        {
3800
0
            int32_t m = internalGetMonth(status);
3801
0
            if (U_FAILURE(status)) {
3802
0
                return 0;
3803
0
            }
3804
0
            if((m == 0) &&
3805
0
            (woy >= getLeastMaximum(UCAL_WEEK_OF_YEAR))) {
3806
0
                return yearWoy+1; // month 0, late woy = in the next year
3807
0
            } else if(woy==1) {
3808
                //if(nextJan1InPrevYear) {
3809
0
                if(m == 0) {
3810
0
                    return yearWoy;
3811
0
                } else {
3812
0
                    return yearWoy-1;
3813
0
                }
3814
                //}
3815
0
            }
3816
0
        }
3817
        //(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow  */ ) {
3818
        //within 1st week and in this month..
3819
        //return yearWoy+1;
3820
0
        return yearWoy;
3821
3822
0
    default: // assume the year is appropriate
3823
0
        return yearWoy;
3824
0
    }
3825
0
}
3826
3827
int32_t Calendar::handleGetMonthLength(int32_t extendedYear, int32_t month, UErrorCode& status) const
3828
0
{
3829
0
    int32_t nextMonth;
3830
0
    if (uprv_add32_overflow(month, 1, &nextMonth)) {
3831
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
3832
0
        return 0;
3833
0
    }
3834
0
    return handleComputeMonthStart(extendedYear, nextMonth, true, status) -
3835
0
        handleComputeMonthStart(extendedYear, month, true, status);
3836
0
}
3837
3838
int32_t Calendar::handleGetYearLength(int32_t eyear, UErrorCode& status) const
3839
0
{
3840
0
    int32_t result = handleComputeMonthStart(eyear+1, 0, false, status) -
3841
0
        handleComputeMonthStart(eyear, 0, false, status);
3842
0
    if (U_FAILURE(status)) return 0;
3843
0
    return result;
3844
0
}
3845
3846
int32_t
3847
Calendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const
3848
0
{
3849
0
    if (U_FAILURE(status)) {
3850
0
       return 0;
3851
0
    }
3852
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
3853
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
3854
0
        return 0;
3855
0
    }
3856
0
    int32_t result;
3857
0
    switch (field) {
3858
0
    case UCAL_DATE:
3859
0
        {
3860
0
            Calendar *cal = clone();
3861
0
            if(!cal) {
3862
0
                status = U_MEMORY_ALLOCATION_ERROR;
3863
0
                return 0;
3864
0
            }
3865
0
            cal->setLenient(true);
3866
0
            cal->prepareGetActual(field,false,status);
3867
0
            result = handleGetMonthLength(cal->get(UCAL_EXTENDED_YEAR, status), cal->get(UCAL_MONTH, status), status);
3868
0
            delete cal;
3869
0
        }
3870
0
        break;
3871
3872
0
    case UCAL_DAY_OF_YEAR:
3873
0
        {
3874
0
            Calendar *cal = clone();
3875
0
            if(!cal) {
3876
0
                status = U_MEMORY_ALLOCATION_ERROR;
3877
0
                return 0;
3878
0
            }
3879
0
            cal->setLenient(true);
3880
0
            cal->prepareGetActual(field,false,status);
3881
0
            result = handleGetYearLength(cal->get(UCAL_EXTENDED_YEAR, status), status);
3882
0
            delete cal;
3883
0
        }
3884
0
        break;
3885
3886
0
    case UCAL_DAY_OF_WEEK:
3887
0
    case UCAL_AM_PM:
3888
0
    case UCAL_HOUR:
3889
0
    case UCAL_HOUR_OF_DAY:
3890
0
    case UCAL_MINUTE:
3891
0
    case UCAL_SECOND:
3892
0
    case UCAL_MILLISECOND:
3893
0
    case UCAL_ZONE_OFFSET:
3894
0
    case UCAL_DST_OFFSET:
3895
0
    case UCAL_DOW_LOCAL:
3896
0
    case UCAL_JULIAN_DAY:
3897
0
    case UCAL_MILLISECONDS_IN_DAY:
3898
        // These fields all have fixed minima/maxima
3899
0
        result = getMaximum(field);
3900
0
        break;
3901
3902
0
    case UCAL_ORDINAL_MONTH:
3903
0
        result = inTemporalLeapYear(status) ? getMaximum(UCAL_ORDINAL_MONTH) : getLeastMaximum(UCAL_ORDINAL_MONTH);
3904
0
        break;
3905
3906
0
    default:
3907
        // For all other fields, do it the hard way....
3908
0
        result = getActualHelper(field, getLeastMaximum(field), getMaximum(field),status);
3909
0
        break;
3910
0
    }
3911
0
    return result;
3912
0
}
3913
3914
3915
/**
3916
* Prepare this calendar for computing the actual minimum or maximum.
3917
* This method modifies this calendar's fields; it is called on a
3918
* temporary calendar.
3919
*
3920
* <p>Rationale: The semantics of getActualXxx() is to return the
3921
* maximum or minimum value that the given field can take, taking into
3922
* account other relevant fields.  In general these other fields are
3923
* larger fields.  For example, when computing the actual maximum
3924
* DATE, the current value of DATE itself is ignored,
3925
* as is the value of any field smaller.
3926
*
3927
* <p>The time fields all have fixed minima and maxima, so we don't
3928
* need to worry about them.  This also lets us set the
3929
* MILLISECONDS_IN_DAY to zero to erase any effects the time fields
3930
* might have when computing date fields.
3931
*
3932
* <p>DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and
3933
* WEEK_OF_YEAR fields to ensure that they are computed correctly.
3934
* @internal
3935
*/
3936
void Calendar::prepareGetActual(UCalendarDateFields field, UBool isMinimum, UErrorCode &status)
3937
0
{
3938
0
    if (U_FAILURE(status)) {
3939
0
       return;
3940
0
    }
3941
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
3942
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
3943
0
        return;
3944
0
    }
3945
0
    set(UCAL_MILLISECONDS_IN_DAY, 0);
3946
3947
0
    switch (field) {
3948
0
    case UCAL_YEAR:
3949
0
    case UCAL_EXTENDED_YEAR:
3950
0
        set(UCAL_DAY_OF_YEAR, getGreatestMinimum(UCAL_DAY_OF_YEAR));
3951
0
        break;
3952
3953
0
    case UCAL_YEAR_WOY:
3954
0
        set(UCAL_WEEK_OF_YEAR, getGreatestMinimum(UCAL_WEEK_OF_YEAR));
3955
0
        U_FALLTHROUGH;
3956
0
    case UCAL_MONTH:
3957
0
        set(UCAL_DATE, getGreatestMinimum(UCAL_DATE));
3958
0
        break;
3959
3960
0
    case UCAL_DAY_OF_WEEK_IN_MONTH:
3961
        // For dowim, the maximum occurs for the DOW of the first of the
3962
        // month.
3963
0
        set(UCAL_DATE, 1);
3964
0
        set(UCAL_DAY_OF_WEEK, get(UCAL_DAY_OF_WEEK, status)); // Make this user set
3965
0
        break;
3966
3967
0
    case UCAL_WEEK_OF_MONTH:
3968
0
    case UCAL_WEEK_OF_YEAR:
3969
        // If we're counting weeks, set the day of the week to either the
3970
        // first or last localized DOW.  We know the last week of a month
3971
        // or year will contain the first day of the week, and that the
3972
        // first week will contain the last DOW.
3973
0
        {
3974
0
            int32_t dow = fFirstDayOfWeek;
3975
0
            if (isMinimum) {
3976
0
                dow = (dow + 6) % 7; // set to last DOW
3977
0
                if (dow < UCAL_SUNDAY) {
3978
0
                    dow += 7;
3979
0
                }
3980
0
            }
3981
#if defined (U_DEBUG_CAL)
3982
            fprintf(stderr, "prepareGetActualHelper(WOM/WOY) - dow=%d\n", dow);
3983
#endif
3984
0
            set(UCAL_DAY_OF_WEEK, dow);
3985
0
        }
3986
0
        break;
3987
0
    default:
3988
0
        break;
3989
0
    }
3990
3991
    // Do this last to give it the newest time stamp
3992
0
    set(field, getGreatestMinimum(field));
3993
0
}
3994
3995
int32_t Calendar::getActualHelper(UCalendarDateFields field, int32_t startValue, int32_t endValue, UErrorCode &status) const
3996
0
{
3997
#if defined (U_DEBUG_CAL)
3998
    fprintf(stderr, "getActualHelper(%d,%d .. %d, %s)\n", field, startValue, endValue, u_errorName(status));
3999
#endif
4000
0
    if (U_FAILURE(status)) {
4001
0
       return 0;
4002
0
    }
4003
0
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
4004
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
4005
0
        return 0;
4006
0
    }
4007
0
    if (startValue == endValue) {
4008
        // if we know that the maximum value is always the same, just return it
4009
0
        return startValue;
4010
0
    }
4011
4012
0
    int32_t delta = (endValue > startValue) ? 1 : -1;
4013
4014
    // clone the calendar so we don't mess with the real one, and set it to
4015
    // accept anything for the field values
4016
0
    if(U_FAILURE(status)) {
4017
0
        return startValue;
4018
0
    }
4019
0
    Calendar *work = clone();
4020
0
    if(!work) {
4021
0
        status = U_MEMORY_ALLOCATION_ERROR;
4022
0
        return startValue;
4023
0
    }
4024
4025
    // need to resolve time here, otherwise, fields set for actual limit
4026
    // may cause conflict with fields previously set (but not yet resolved).
4027
0
    work->complete(status);
4028
4029
0
    work->setLenient(true);
4030
0
    work->prepareGetActual(field, delta < 0, status);
4031
4032
    // now try each value from the start to the end one by one until
4033
    // we get a value that normalizes to another value.  The last value that
4034
    // normalizes to itself is the actual maximum for the current date
4035
0
    work->set(field, startValue);
4036
4037
    // prepareGetActual sets the first day of week in the same week with
4038
    // the first day of a month.  Unlike WEEK_OF_YEAR, week number for the
4039
    // week which contains days from both previous and current month is
4040
    // not unique.  For example, last several days in the previous month
4041
    // is week 5, and the rest of week is week 1.
4042
0
    int32_t result = startValue;
4043
0
    if ((work->get(field, status) != startValue
4044
0
         && field != UCAL_WEEK_OF_MONTH && delta > 0 ) || U_FAILURE(status)) {
4045
#if defined (U_DEBUG_CAL)
4046
        fprintf(stderr, "getActualHelper(fld %d) - got  %d (not %d) - %s\n", field, work->get(field,status), startValue, u_errorName(status));
4047
#endif
4048
0
    } else {
4049
0
        do {
4050
0
            startValue += delta;
4051
0
            work->add(field, delta, status);
4052
0
            if (work->get(field, status) != startValue || U_FAILURE(status)) {
4053
#if defined (U_DEBUG_CAL)
4054
                fprintf(stderr, "getActualHelper(fld %d) - got  %d (not %d), BREAK - %s\n", field, work->get(field,status), startValue, u_errorName(status));
4055
#endif
4056
0
                break;
4057
0
            }
4058
0
            result = startValue;
4059
0
        } while (startValue != endValue);
4060
0
    }
4061
0
    delete work;
4062
#if defined (U_DEBUG_CAL)
4063
    fprintf(stderr, "getActualHelper(%d) = %d\n", field, result);
4064
#endif
4065
0
    return result;
4066
0
}
4067
4068
4069
4070
4071
// -------------------------------------
4072
4073
void
4074
Calendar::setWeekData(const Locale& desiredLocale, const char *type, UErrorCode& status)
4075
0
{
4076
4077
0
    if (U_FAILURE(status)) {
4078
0
        return;
4079
0
    }
4080
4081
0
    fFirstDayOfWeek = UCAL_SUNDAY;
4082
0
    fMinimalDaysInFirstWeek = 1;
4083
0
    fWeekendOnset = UCAL_SATURDAY;
4084
0
    fWeekendOnsetMillis = 0;
4085
0
    fWeekendCease = UCAL_SUNDAY;
4086
0
    fWeekendCeaseMillis = 86400000; // 24*60*60*1000
4087
4088
    // Since week and weekend data is territory based instead of language based,
4089
    // we may need to tweak the locale that we are using to try to get the appropriate
4090
    // values, using the following logic:
4091
    // 1). If the locale has a language but no territory, use the territory as defined by
4092
    //     the likely subtags.
4093
    // 2). If the locale has a script designation then we ignore it,
4094
    //     then remove it ( i.e. "en_Latn_US" becomes "en_US" )
4095
4096
0
    UErrorCode myStatus = U_ZERO_ERROR;
4097
4098
0
    Locale min(desiredLocale);
4099
0
    min.minimizeSubtags(myStatus);
4100
0
    Locale useLocale;
4101
0
    if ( uprv_strlen(desiredLocale.getCountry()) == 0 ||
4102
0
         (uprv_strlen(desiredLocale.getScript()) > 0 && uprv_strlen(min.getScript()) == 0) ) {
4103
0
        myStatus = U_ZERO_ERROR;
4104
0
        Locale max(desiredLocale);
4105
0
        max.addLikelySubtags(myStatus);
4106
0
        useLocale = Locale(max.getLanguage(),max.getCountry());
4107
0
    } else {
4108
0
        useLocale = desiredLocale;
4109
0
    }
4110
4111
    /* The code here is somewhat of a hack, since week data and weekend data aren't really tied to
4112
       a specific calendar, they aren't truly locale data.  But this is the only place where valid and
4113
       actual locale can be set, so we take a shot at it here by loading a representative resource
4114
       from the calendar data.  The code used to use the dateTimeElements resource to get first day
4115
       of week data, but this was moved to supplemental data under ticket 7755. (JCE) */
4116
4117
    // Get the monthNames resource bundle for the calendar 'type'. Fallback to gregorian if the resource is not
4118
    // found.
4119
0
    LocalUResourceBundlePointer calData(ures_open(nullptr, useLocale.getBaseName(), &status));
4120
0
    ures_getByKey(calData.getAlias(), gCalendar, calData.getAlias(), &status);
4121
4122
0
    LocalUResourceBundlePointer monthNames;
4123
0
    if (type != nullptr && *type != '\0' && uprv_strcmp(type, gGregorian) != 0) {
4124
0
        monthNames.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), type, nullptr, &status));
4125
0
        ures_getByKeyWithFallback(monthNames.getAlias(), gMonthNames,
4126
0
                                  monthNames.getAlias(), &status);
4127
0
    }
4128
4129
0
    if (monthNames.isNull() || status == U_MISSING_RESOURCE_ERROR) {
4130
0
        status = U_ZERO_ERROR;
4131
0
        monthNames.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), gGregorian,
4132
0
                                                          monthNames.orphan(), &status));
4133
0
        ures_getByKeyWithFallback(monthNames.getAlias(), gMonthNames,
4134
0
                                  monthNames.getAlias(), &status);
4135
0
    }
4136
4137
0
    if (U_SUCCESS(status)) {
4138
0
        U_LOCALE_BASED(locBased,*this);
4139
0
        locBased.setLocaleIDs(ures_getLocaleByType(monthNames.getAlias(), ULOC_VALID_LOCALE, &status),
4140
0
                              ures_getLocaleByType(monthNames.getAlias(), ULOC_ACTUAL_LOCALE, &status), status);
4141
0
    } else {
4142
0
        status = U_USING_FALLBACK_WARNING;
4143
0
        return;
4144
0
    }
4145
4146
0
    CharString region = ulocimp_getRegionForSupplementalData(desiredLocale.getName(), true, status);
4147
4148
    // Read week data values from supplementalData week data
4149
0
    UResourceBundle *rb = ures_openDirect(nullptr, "supplementalData", &status);
4150
0
    ures_getByKey(rb, "weekData", rb, &status);
4151
0
    UResourceBundle *weekData = ures_getByKey(rb, region.data(), nullptr, &status);
4152
0
    if (status == U_MISSING_RESOURCE_ERROR && rb != nullptr) {
4153
0
        status = U_ZERO_ERROR;
4154
0
        weekData = ures_getByKey(rb, "001", nullptr, &status);
4155
0
    }
4156
4157
0
    if (U_FAILURE(status)) {
4158
0
        status = U_USING_FALLBACK_WARNING;
4159
0
    } else {
4160
0
        int32_t arrLen;
4161
0
        const int32_t *weekDataArr = ures_getIntVector(weekData,&arrLen,&status);
4162
0
        if( U_SUCCESS(status) && arrLen == 6
4163
0
                && 1 <= weekDataArr[0] && weekDataArr[0] <= 7
4164
0
                && 1 <= weekDataArr[1] && weekDataArr[1] <= 7
4165
0
                && 1 <= weekDataArr[2] && weekDataArr[2] <= 7
4166
0
                && 1 <= weekDataArr[4] && weekDataArr[4] <= 7) {
4167
0
            fFirstDayOfWeek = static_cast<UCalendarDaysOfWeek>(weekDataArr[0]);
4168
0
            fMinimalDaysInFirstWeek = static_cast<uint8_t>(weekDataArr[1]);
4169
0
            fWeekendOnset = static_cast<UCalendarDaysOfWeek>(weekDataArr[2]);
4170
0
            fWeekendOnsetMillis = weekDataArr[3];
4171
0
            fWeekendCease = static_cast<UCalendarDaysOfWeek>(weekDataArr[4]);
4172
0
            fWeekendCeaseMillis = weekDataArr[5];
4173
0
        } else {
4174
0
            status = U_INVALID_FORMAT_ERROR;
4175
0
        }
4176
4177
        // Check if the locale has a "fw" u extension and we honor it if present.
4178
        // And we don't change the overal status, as the presence / lack of "fw" is not an error.
4179
0
        UErrorCode fwStatus = U_ZERO_ERROR;
4180
0
        char fwExt[ULOC_FULLNAME_CAPACITY] = "";
4181
0
        desiredLocale.getKeywordValue("fw", fwExt, ULOC_FULLNAME_CAPACITY, fwStatus);
4182
0
        if (U_SUCCESS(fwStatus)) {
4183
0
            if (uprv_strcmp(fwExt, "sun") == 0) {
4184
0
                fFirstDayOfWeek = UCAL_SUNDAY;
4185
0
            } else if (uprv_strcmp(fwExt, "mon") == 0) {
4186
0
                fFirstDayOfWeek = UCAL_MONDAY;
4187
0
            } else if (uprv_strcmp(fwExt, "tue") == 0) {
4188
0
                fFirstDayOfWeek = UCAL_TUESDAY;
4189
0
            } else if (uprv_strcmp(fwExt, "wed") == 0) {
4190
0
                fFirstDayOfWeek = UCAL_WEDNESDAY;
4191
0
            } else if (uprv_strcmp(fwExt, "thu") == 0) {
4192
0
                fFirstDayOfWeek = UCAL_THURSDAY;
4193
0
            } else if (uprv_strcmp(fwExt, "fri") == 0) {
4194
0
                fFirstDayOfWeek = UCAL_FRIDAY;
4195
0
            } else if (uprv_strcmp(fwExt, "sat") == 0) {
4196
0
                fFirstDayOfWeek = UCAL_SATURDAY;
4197
0
            }
4198
0
        }
4199
0
    }
4200
0
    ures_close(weekData);
4201
0
    ures_close(rb);
4202
0
}
4203
4204
/**
4205
* Recompute the time and update the status fields isTimeSet
4206
* and areFieldsSet.  Callers should check isTimeSet and only
4207
* call this method if isTimeSet is false.
4208
*/
4209
void
4210
Calendar::updateTime(UErrorCode& status)
4211
0
{
4212
0
    computeTime(status);
4213
0
    if(U_FAILURE(status))
4214
0
        return;
4215
4216
    // If we are lenient, we need to recompute the fields to normalize
4217
    // the values.  Also, if we haven't set all the fields yet (i.e.,
4218
    // in a newly-created object), we need to fill in the fields. [LIU]
4219
0
    if (isLenient() || ! fAreAllFieldsSet)
4220
0
        fAreFieldsSet = false;
4221
4222
0
    fIsTimeSet = true;
4223
0
    fAreFieldsVirtuallySet = false;
4224
0
}
4225
4226
Locale
4227
0
Calendar::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
4228
0
    return LocaleBased::getLocale(validLocale, actualLocale, type, status);
4229
0
}
4230
4231
const char *
4232
0
Calendar::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const {
4233
0
    return LocaleBased::getLocaleID(validLocale, actualLocale, type, status);
4234
0
}
4235
4236
void
4237
0
Calendar::recalculateStamp() {
4238
0
    int32_t index;
4239
0
    int32_t currentValue;
4240
0
    int32_t j, i;
4241
4242
0
    fNextStamp = kInternallySet;
4243
4244
0
    for (j = 0; j < UCAL_FIELD_COUNT; j++) {
4245
0
        currentValue = STAMP_MAX;
4246
0
        index = -1;
4247
0
        for (i = 0; i < UCAL_FIELD_COUNT; i++) {
4248
0
            if (fStamp[i] > fNextStamp && fStamp[i] < currentValue) {
4249
0
                currentValue = fStamp[i];
4250
0
                index = i;
4251
0
            }
4252
0
        }
4253
4254
0
        if (index >= 0) {
4255
0
            fStamp[index] = ++fNextStamp;
4256
0
        } else {
4257
0
            break;
4258
0
        }
4259
0
    }
4260
0
    fNextStamp++;
4261
0
}
4262
4263
// Deprecated function. This doesn't need to be inline.
4264
void
4265
Calendar::internalSet(EDateFields field, int32_t value)
4266
0
{
4267
0
    internalSet(static_cast<UCalendarDateFields>(field), value);
4268
0
}
4269
4270
0
int32_t Calendar::internalGetMonth(UErrorCode& status) const {
4271
0
    if (U_FAILURE(status)) {
4272
0
        return 0;
4273
0
    }
4274
0
    if (resolveFields(kMonthPrecedence) == UCAL_ORDINAL_MONTH) {
4275
0
        return internalGet(UCAL_ORDINAL_MONTH);
4276
0
    }
4277
0
    return internalGet(UCAL_MONTH);
4278
0
}
4279
4280
0
int32_t Calendar::internalGetMonth(int32_t defaultValue, UErrorCode& status) const {
4281
0
    if (U_FAILURE(status)) {
4282
0
        return 0;
4283
0
    }
4284
0
    if (resolveFields(kMonthPrecedence) == UCAL_ORDINAL_MONTH) {
4285
0
        return internalGet(UCAL_ORDINAL_MONTH);
4286
0
    }
4287
0
    return internalGet(UCAL_MONTH, defaultValue);
4288
0
}
4289
4290
BasicTimeZone*
4291
0
Calendar::getBasicTimeZone() const {
4292
0
    if (dynamic_cast<const OlsonTimeZone *>(fZone) != nullptr
4293
0
        || dynamic_cast<const SimpleTimeZone *>(fZone) != nullptr
4294
0
        || dynamic_cast<const RuleBasedTimeZone *>(fZone) != nullptr
4295
0
        || dynamic_cast<const VTimeZone *>(fZone) != nullptr) {
4296
0
        return (BasicTimeZone*)fZone;
4297
0
    }
4298
0
    return nullptr;
4299
0
}
4300
4301
U_NAMESPACE_END
4302
4303
#endif /* #if !UCONFIG_NO_FORMATTING */
4304
4305
4306
//eof