Coverage Report

Created: 2025-06-24 06:43

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