Coverage Report

Created: 2018-09-25 14:53

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