~ubuntu-branches/ubuntu/maverick/icu/maverick-updates

« back to all changes in this revision

Viewing changes to source/i18n/calendar.cpp

  • Committer: Package Import Robot
  • Author(s): Jay Berkenbilt
  • Date: 2009-09-04 11:56:06 UTC
  • mfrom: (10.1.6 sid)
  • Revision ID: package-import@ubuntu.com-20090904115606-sqxxuizelam5tozb
Tags: 4.2.1-3
Change install-doc target to not fail if there are subdirectories of
doc/html.  This is necessary to handle the doc/html/search directory
created by doxygen 3.6.1.  (Closes: #544799)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
*******************************************************************************
 
3
* Copyright (C) 1997-2009, International Business Machines Corporation and    *
 
4
* others. All Rights Reserved.                                                *
 
5
*******************************************************************************
 
6
*
 
7
* File CALENDAR.CPP
 
8
*
 
9
* Modification History:
 
10
*
 
11
*   Date        Name        Description
 
12
*   02/03/97    clhuang     Creation.
 
13
*   04/22/97    aliu        Cleaned up, fixed memory leak, made 
 
14
*                           setWeekCountData() more robust.  
 
15
*                           Moved platform code to TPlatformUtilities.
 
16
*   05/01/97    aliu        Made equals(), before(), after() arguments const.
 
17
*   05/20/97    aliu        Changed logic of when to compute fields and time
 
18
*                           to fix bugs.
 
19
*   08/12/97    aliu        Added equivalentTo.  Misc other fixes.
 
20
*   07/28/98    stephen     Sync up with JDK 1.2
 
21
*   09/02/98    stephen     Sync with JDK 1.2 8/31 build (getActualMin/Max)
 
22
*   03/17/99    stephen     Changed adoptTimeZone() - now fAreFieldsSet is
 
23
*                           set to FALSE to force update of time.
 
24
*******************************************************************************
 
25
*/
 
26
 
 
27
#include "unicode/utypes.h"
 
28
 
 
29
#if !UCONFIG_NO_FORMATTING
 
30
 
 
31
#include "unicode/gregocal.h"
 
32
#include "gregoimp.h"
 
33
#include "buddhcal.h"
 
34
#include "taiwncal.h"
 
35
#include "japancal.h"
 
36
#include "islamcal.h"
 
37
#include "hebrwcal.h"
 
38
#include "persncal.h"
 
39
#include "indiancal.h"
 
40
#include "chnsecal.h"
 
41
#include "coptccal.h"
 
42
#include "ethpccal.h"
 
43
#include "unicode/calendar.h"
 
44
#include "cpputils.h"
 
45
#include "servloc.h"
 
46
#include "ucln_in.h"
 
47
#include "cstring.h"
 
48
#include "locbased.h"
 
49
#include "uresimp.h"
 
50
#include "ustrenum.h"
 
51
 
 
52
#if !UCONFIG_NO_SERVICE
 
53
static U_NAMESPACE_QUALIFIER ICULocaleService* gService = NULL;
 
54
#endif
 
55
 
 
56
// INTERNAL - for cleanup
 
57
 
 
58
U_CDECL_BEGIN
 
59
static UBool calendar_cleanup(void) {
 
60
#if !UCONFIG_NO_SERVICE
 
61
    if (gService) {
 
62
        delete gService;
 
63
        gService = NULL;
 
64
    }
 
65
#endif
 
66
    return TRUE;
 
67
}
 
68
U_CDECL_END
 
69
 
 
70
// ------------------------------------------
 
71
//
 
72
// Registration
 
73
//
 
74
//-------------------------------------------
 
75
//#define U_DEBUG_CALSVC 1
 
76
//
 
77
 
 
78
#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
 
79
 
 
80
/** 
 
81
 * fldName was removed as a duplicate implementation. 
 
82
 * use  udbg_ services instead, 
 
83
 * which depend on include files and library from ../tools/ctestfw
 
84
 */
 
85
#include "unicode/udbgutil.h"
 
86
#include <stdio.h>
 
87
 
 
88
/**
 
89
* convert a UCalendarDateFields into a string - for debugging
 
90
* @param f field enum
 
91
* @return static string to the field name
 
92
* @internal
 
93
*/
 
94
 
 
95
const char* fldName(UCalendarDateFields f) {
 
96
        return udbg_enumName(UDBG_UCalendarDateFields, (int32_t)f);
 
97
}
 
98
 
 
99
#if UCAL_DEBUG_DUMP
 
100
// from CalendarTest::calToStr - but doesn't modify contents.
 
101
void ucal_dump(const Calendar &cal) {
 
102
    cal.dump();
 
103
}
 
104
 
 
105
void Calendar::dump() const {
 
106
    int i;
 
107
    fprintf(stderr, "@calendar=%s, timeset=%c, fieldset=%c, allfields=%c, virtualset=%c, t=%.2f",
 
108
        getType(), fIsTimeSet?'y':'n',  fAreFieldsSet?'y':'n',  fAreAllFieldsSet?'y':'n',  
 
109
        fAreFieldsVirtuallySet?'y':'n',
 
110
        fTime);
 
111
 
 
112
    // can add more things here: DST, zone, etc.
 
113
    fprintf(stderr, "\n");
 
114
    for(i = 0;i<UCAL_FIELD_COUNT;i++) {
 
115
        int n;
 
116
        const char *f = fldName((UCalendarDateFields)i);
 
117
        fprintf(stderr, "  %25s: %-11ld", f, fFields[i]);
 
118
        if(fStamp[i] == kUnset) {
 
119
            fprintf(stderr, " (unset) ");
 
120
        } else if(fStamp[i] == kInternallySet) { 
 
121
            fprintf(stderr, " (internally set) ");
 
122
            //} else if(fStamp[i] == kInternalDefault) { 
 
123
            //    fprintf(stderr, " (internal default) ");
 
124
        } else {
 
125
            fprintf(stderr, " %%%d ", fStamp[i]);
 
126
        }
 
127
        fprintf(stderr, "\n");
 
128
 
 
129
    }
 
130
}
 
131
 
 
132
U_CFUNC void ucal_dump(UCalendar* cal) {
 
133
    ucal_dump( *((Calendar*)cal)  );
 
134
}
 
135
#endif
 
136
 
 
137
#endif
 
138
 
 
139
static const char * const gCalTypes[] = {
 
140
    "gregorian",
 
141
    "japanese",
 
142
    "buddhist",
 
143
    "roc",
 
144
    "persian",
 
145
    "islamic-civil",
 
146
    "islamic",
 
147
    "hebrew",
 
148
    "chinese",
 
149
    "indian",
 
150
    "coptic",
 
151
    "ethiopic",
 
152
    "ethiopic-amete-alem",
 
153
    NULL
 
154
};
 
155
 
 
156
// Must be in the order of gCalTypes above
 
157
typedef enum ECalType {
 
158
    CALTYPE_UNKNOWN = -1,
 
159
    CALTYPE_GREGORIAN = 0,
 
160
    CALTYPE_JAPANESE,
 
161
    CALTYPE_BUDDHIST,
 
162
    CALTYPE_ROC,
 
163
    CALTYPE_PERSIAN,
 
164
    CALTYPE_ISLAMIC_CIVIL,
 
165
    CALTYPE_ISLAMIC,
 
166
    CALTYPE_HEBREW,
 
167
    CALTYPE_CHINESE,
 
168
    CALTYPE_INDIAN,
 
169
    CALTYPE_COPTIC,
 
170
    CALTYPE_ETHIOPIC,
 
171
    CALTYPE_ETHIOPIC_AMETE_ALEM
 
172
} ECalType;
 
173
 
 
174
U_NAMESPACE_BEGIN
 
175
 
 
176
static ECalType getCalendarType(const char *s) {
 
177
    for (int i = 0; gCalTypes[i] != NULL; i++) {
 
178
        if (uprv_stricmp(s, gCalTypes[i]) == 0) {
 
179
            return (ECalType)i;
 
180
        }
 
181
    }
 
182
    return CALTYPE_UNKNOWN;
 
183
}
 
184
 
 
185
static UBool isStandardSupportedKeyword(const char *keyword, UErrorCode& status) { 
 
186
    if(U_FAILURE(status)) {
 
187
        return FALSE;
 
188
    }
 
189
    ECalType calType = getCalendarType(keyword);
 
190
    return (calType != CALTYPE_UNKNOWN);
 
191
}
 
192
 
 
193
static void getCalendarKeyword(const UnicodeString &id, char *targetBuffer, int32_t targetBufferSize) {
 
194
    UnicodeString calendarKeyword = UNICODE_STRING_SIMPLE("calendar=");
 
195
    int32_t calKeyLen = calendarKeyword.length();
 
196
    int32_t keyLen = 0;
 
197
 
 
198
    int32_t keywordIdx = id.indexOf((UChar)0x003D); /* '=' */
 
199
    if (id[0] == 0x40/*'@'*/
 
200
        && id.compareBetween(1, keywordIdx+1, calendarKeyword, 0, calKeyLen) == 0)
 
201
    {
 
202
        keyLen = id.extract(keywordIdx+1, id.length(), targetBuffer, targetBufferSize, US_INV);
 
203
    }
 
204
    targetBuffer[keyLen] = 0;
 
205
}
 
206
 
 
207
static ECalType getCalendarTypeForLocale(const char *locid) {
 
208
    UErrorCode status = U_ZERO_ERROR;
 
209
    ECalType calType = CALTYPE_UNKNOWN;
 
210
 
 
211
    //TODO: ULOC_FULL_NAME is out of date and too small..
 
212
    char canonicalName[256];
 
213
 
 
214
    // canonicalize, so grandfathered variant will be transformed to keywords
 
215
    // e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese
 
216
    int32_t canonicalLen = uloc_canonicalize(locid, canonicalName, sizeof(canonicalName) - 1, &status);
 
217
    if (U_FAILURE(status)) {
 
218
        return CALTYPE_GREGORIAN;
 
219
    }
 
220
    canonicalName[canonicalLen] = 0;    // terminate
 
221
 
 
222
    char calTypeBuf[32];
 
223
    int32_t calTypeBufLen;
 
224
 
 
225
    calTypeBufLen = uloc_getKeywordValue(canonicalName, "calendar", calTypeBuf, sizeof(calTypeBuf) - 1, &status);
 
226
    if (U_SUCCESS(status)) {
 
227
        calTypeBuf[calTypeBufLen] = 0;
 
228
        calType = getCalendarType(calTypeBuf);
 
229
        if (calType != CALTYPE_UNKNOWN) {
 
230
            return calType;
 
231
        }
 
232
    }
 
233
    status = U_ZERO_ERROR;
 
234
 
 
235
    // when calendar keyword is not available or not supported, read supplementalData
 
236
    // to get the default calendar type for the locale's region
 
237
    char region[ULOC_COUNTRY_CAPACITY];
 
238
    int32_t regionLen = 0;
 
239
    regionLen = uloc_getCountry(canonicalName, region, sizeof(region) - 1, &status);
 
240
    if (regionLen == 0) {
 
241
        char fullLoc[256];
 
242
        int32_t fullLocLen = 0;
 
243
        fullLocLen = uloc_addLikelySubtags(locid, fullLoc, sizeof(fullLoc) - 1, &status);
 
244
        regionLen = uloc_getCountry(fullLoc, region, sizeof(region) - 1, &status);
 
245
    }
 
246
    if (U_FAILURE(status)) {
 
247
        return CALTYPE_GREGORIAN;
 
248
    }
 
249
    region[regionLen] = 0;
 
250
    
 
251
    // Read preferred calendar values from supplementalData calendarPreference
 
252
    UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", &status);
 
253
    ures_getByKey(rb, "calendarPreferenceData", rb, &status);
 
254
    UResourceBundle *order = ures_getByKey(rb, region, NULL, &status);
 
255
    if (status == U_MISSING_RESOURCE_ERROR && rb != NULL) {
 
256
        status = U_ZERO_ERROR;
 
257
        order = ures_getByKey(rb, "001", NULL, &status);
 
258
    }
 
259
 
 
260
    calTypeBuf[0] = 0;
 
261
    if (U_SUCCESS(status) && order != NULL) {
 
262
        // the first calender type is the default for the region
 
263
        int32_t len = 0;
 
264
        const UChar *uCalType = ures_getStringByIndex(order, 0, &len, &status);
 
265
        if (len < (int32_t)sizeof(calTypeBuf)) {
 
266
            u_UCharsToChars(uCalType, calTypeBuf, len);
 
267
            *(calTypeBuf + len) = 0; // terminate;
 
268
            calType = getCalendarType(calTypeBuf);
 
269
        }
 
270
    }
 
271
 
 
272
    ures_close(order);
 
273
    ures_close(rb);
 
274
 
 
275
    if (calType == CALTYPE_UNKNOWN) {
 
276
        // final fallback
 
277
        calType = CALTYPE_GREGORIAN;
 
278
    }
 
279
    return calType;
 
280
}
 
281
 
 
282
static Calendar *createStandardCalendar(ECalType calType, const Locale &loc, UErrorCode& status) {
 
283
    Calendar *cal = NULL;
 
284
 
 
285
    switch (calType) {
 
286
        case CALTYPE_GREGORIAN:
 
287
            cal = new GregorianCalendar(loc, status);
 
288
            break;
 
289
        case CALTYPE_JAPANESE:
 
290
            cal = new JapaneseCalendar(loc, status);
 
291
            break;
 
292
        case CALTYPE_BUDDHIST:
 
293
            cal = new BuddhistCalendar(loc, status);
 
294
            break;
 
295
        case CALTYPE_ROC:
 
296
            cal = new TaiwanCalendar(loc, status);
 
297
            break;
 
298
        case CALTYPE_PERSIAN:
 
299
            cal = new PersianCalendar(loc, status);
 
300
            break;
 
301
        case CALTYPE_ISLAMIC_CIVIL:
 
302
            cal = new IslamicCalendar(loc, status, IslamicCalendar::CIVIL);
 
303
            break;
 
304
        case CALTYPE_ISLAMIC:
 
305
            cal = new IslamicCalendar(loc, status, IslamicCalendar::ASTRONOMICAL);
 
306
            break;
 
307
        case CALTYPE_HEBREW:
 
308
            cal = new HebrewCalendar(loc, status);
 
309
            break;
 
310
        case CALTYPE_CHINESE:
 
311
            cal = new ChineseCalendar(loc, status);
 
312
            break;
 
313
        case CALTYPE_INDIAN:
 
314
            cal = new IndianCalendar(loc, status);
 
315
            break;
 
316
        case CALTYPE_COPTIC:
 
317
            cal = new CopticCalendar(loc, status);
 
318
            break;
 
319
        case CALTYPE_ETHIOPIC:
 
320
            cal = new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_MIHRET_ERA);
 
321
            break;
 
322
        case CALTYPE_ETHIOPIC_AMETE_ALEM:
 
323
            cal = new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_ALEM_ERA);
 
324
            break;
 
325
        default:
 
326
            status = U_UNSUPPORTED_ERROR;
 
327
    }
 
328
    return cal;
 
329
}
 
330
 
 
331
 
 
332
#if !UCONFIG_NO_SERVICE
 
333
 
 
334
// -------------------------------------
 
335
 
 
336
/**
 
337
* a Calendar Factory which creates the "basic" calendar types, that is, those 
 
338
* shipped with ICU.
 
339
*/
 
340
class BasicCalendarFactory : public LocaleKeyFactory {
 
341
public:
 
342
    /**
 
343
    * @param calendarType static const string (caller owns storage - will be aliased) to calendar type
 
344
    */
 
345
    BasicCalendarFactory()
 
346
        : LocaleKeyFactory(LocaleKeyFactory::INVISIBLE) { }
 
347
 
 
348
        virtual ~BasicCalendarFactory() {}
 
349
 
 
350
protected:
 
351
    //virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const { 
 
352
    //  if(U_FAILURE(status)) {
 
353
    //    return FALSE;
 
354
    //  }
 
355
    //  char keyword[ULOC_FULLNAME_CAPACITY];
 
356
    //  getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword));
 
357
    //  return isStandardSupportedKeyword(keyword, status);
 
358
    //}
 
359
 
 
360
    virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const
 
361
    {
 
362
        if (U_SUCCESS(status)) {
 
363
            for(int32_t i=0;gCalTypes[i] != NULL;i++) {
 
364
                UnicodeString id((UChar)0x40); /* '@' a variant character */
 
365
                id.append(UNICODE_STRING_SIMPLE("calendar="));
 
366
                id.append(UnicodeString(gCalTypes[i], -1, US_INV));
 
367
                result.put(id, (void*)this, status);
 
368
            }
 
369
        }
 
370
    }
 
371
 
 
372
    virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const {
 
373
#ifdef U_DEBUG_CALSVC
 
374
        if(key.getDynamicClassID() != LocaleKey::getStaticClassID()) {
 
375
            fprintf(stderr, "::create - not a LocaleKey!\n");
 
376
        }
 
377
#endif
 
378
        const LocaleKey& lkey = (LocaleKey&)key;
 
379
        Locale curLoc;  // current locale
 
380
        Locale canLoc;  // Canonical locale
 
381
 
 
382
        lkey.currentLocale(curLoc);
 
383
        lkey.canonicalLocale(canLoc);
 
384
 
 
385
        char keyword[ULOC_FULLNAME_CAPACITY];
 
386
        UnicodeString str;
 
387
 
 
388
        key.currentID(str);
 
389
        getCalendarKeyword(str, keyword, (int32_t) sizeof(keyword));
 
390
 
 
391
#ifdef U_DEBUG_CALSVC
 
392
        fprintf(stderr, "BasicCalendarFactory::create() - cur %s, can %s\n", (const char*)curLoc.getName(), (const char*)canLoc.getName());
 
393
#endif
 
394
 
 
395
        if(!isStandardSupportedKeyword(keyword,status)) {  // Do we handle this type?
 
396
#ifdef U_DEBUG_CALSVC
 
397
 
 
398
            fprintf(stderr, "BasicCalendarFactory - not handling %s.[%s]\n", (const char*) curLoc.getName(), tmp );
 
399
#endif
 
400
            return NULL;
 
401
        }
 
402
 
 
403
        return createStandardCalendar(getCalendarType(keyword), canLoc, status);
 
404
    }
 
405
};
 
406
 
 
407
 
 
408
/** 
 
409
* A factory which looks up the DefaultCalendar resource to determine which class of calendar to use
 
410
*/
 
411
 
 
412
class DefaultCalendarFactory : public ICUResourceBundleFactory {
 
413
public:
 
414
    DefaultCalendarFactory():  ICUResourceBundleFactory() { } 
 
415
protected:
 
416
    virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const  {
 
417
 
 
418
        LocaleKey &lkey = (LocaleKey&)key;
 
419
        Locale loc;
 
420
        lkey.currentLocale(loc);
 
421
 
 
422
        UnicodeString *ret = new UnicodeString();
 
423
        if (ret == NULL) {
 
424
            status = U_MEMORY_ALLOCATION_ERROR;
 
425
        } else {
 
426
            ret->append((UChar)0x40); // '@' is a variant character
 
427
            ret->append(UNICODE_STRING("calendar=", 9));
 
428
            ret->append(UnicodeString(gCalTypes[getCalendarTypeForLocale(loc.getName())]));
 
429
        }
 
430
        return ret;
 
431
    }
 
432
};
 
433
 
 
434
// -------------------------------------
 
435
class CalendarService : public ICULocaleService {
 
436
public:
 
437
    CalendarService()
 
438
        : ICULocaleService(UNICODE_STRING_SIMPLE("Calendar"))
 
439
    {
 
440
        UErrorCode status = U_ZERO_ERROR;
 
441
        registerFactory(new DefaultCalendarFactory(), status);
 
442
    }
 
443
 
 
444
    virtual UObject* cloneInstance(UObject* instance) const {
 
445
        if(instance->getDynamicClassID() == UnicodeString::getStaticClassID()) {
 
446
            return ((UnicodeString*)instance)->clone(); 
 
447
        } else {
 
448
#ifdef U_DEBUG_CALSVC_F
 
449
            UErrorCode status2 = U_ZERO_ERROR;
 
450
            fprintf(stderr, "Cloning a %s calendar with tz=%ld\n", ((Calendar*)instance)->getType(), ((Calendar*)instance)->get(UCAL_ZONE_OFFSET, status2));
 
451
#endif
 
452
            return ((Calendar*)instance)->clone();
 
453
        }
 
454
    }
 
455
 
 
456
    virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /*actualID*/, UErrorCode& status) const {
 
457
        LocaleKey& lkey = (LocaleKey&)key;
 
458
        //int32_t kind = lkey.kind();
 
459
 
 
460
        Locale loc;
 
461
        lkey.canonicalLocale(loc);
 
462
 
 
463
#ifdef U_DEBUG_CALSVC
 
464
        Locale loc2;
 
465
        lkey.currentLocale(loc2);
 
466
        fprintf(stderr, "CalSvc:handleDefault for currentLoc %s, canloc %s\n", (const char*)loc.getName(),  (const char*)loc2.getName());
 
467
#endif
 
468
        Calendar *nc =  new GregorianCalendar(loc, status);
 
469
 
 
470
#ifdef U_DEBUG_CALSVC
 
471
        UErrorCode status2 = U_ZERO_ERROR;
 
472
        fprintf(stderr, "New default calendar has tz=%d\n", ((Calendar*)nc)->get(UCAL_ZONE_OFFSET, status2));
 
473
#endif
 
474
        return nc;
 
475
    }
 
476
 
 
477
    virtual UBool isDefault() const {
 
478
        return countFactories() == 1;
 
479
    }
 
480
};
 
481
 
 
482
// -------------------------------------
 
483
 
 
484
static inline UBool
 
485
isCalendarServiceUsed() {
 
486
    UBool retVal;
 
487
    UMTX_CHECK(NULL, gService != NULL, retVal);
 
488
    return retVal;
 
489
}
 
490
 
 
491
// -------------------------------------
 
492
 
 
493
static ICULocaleService* 
 
494
getCalendarService(UErrorCode &status)
 
495
{
 
496
    UBool needInit;
 
497
    UMTX_CHECK(NULL, (UBool)(gService == NULL), needInit);
 
498
    if (needInit) {
 
499
#ifdef U_DEBUG_CALSVC
 
500
        fprintf(stderr, "Spinning up Calendar Service\n");
 
501
#endif
 
502
        ICULocaleService * newservice = new CalendarService();
 
503
        if (newservice == NULL) {
 
504
            status = U_MEMORY_ALLOCATION_ERROR;
 
505
            return newservice;
 
506
        }
 
507
#ifdef U_DEBUG_CALSVC
 
508
        fprintf(stderr, "Registering classes..\n");
 
509
#endif
 
510
 
 
511
        // Register all basic instances. 
 
512
        newservice->registerFactory(new BasicCalendarFactory(),status);
 
513
 
 
514
#ifdef U_DEBUG_CALSVC
 
515
        fprintf(stderr, "Done..\n");
 
516
#endif
 
517
 
 
518
        if(U_FAILURE(status)) {
 
519
#ifdef U_DEBUG_CALSVC
 
520
            fprintf(stderr, "err (%s) registering classes, deleting service.....\n", u_errorName(status));
 
521
#endif
 
522
            delete newservice;
 
523
            newservice = NULL;
 
524
        }
 
525
 
 
526
        if (newservice) {
 
527
            umtx_lock(NULL);
 
528
            if (gService == NULL) {
 
529
                gService = newservice;
 
530
                newservice = NULL;
 
531
            }
 
532
            umtx_unlock(NULL);
 
533
        }
 
534
        if (newservice) {
 
535
            delete newservice;
 
536
        } else {
 
537
            // we won the contention - we can register the cleanup.
 
538
            ucln_i18n_registerCleanup(UCLN_I18N_CALENDAR, calendar_cleanup);
 
539
        }
 
540
    }
 
541
    return gService;
 
542
}
 
543
 
 
544
URegistryKey Calendar::registerFactory(ICUServiceFactory* toAdopt, UErrorCode& status)
 
545
{
 
546
    return getCalendarService(status)->registerFactory(toAdopt, status);
 
547
}
 
548
 
 
549
UBool Calendar::unregister(URegistryKey key, UErrorCode& status) {
 
550
    return getCalendarService(status)->unregister(key, status);
 
551
}
 
552
#endif /* UCONFIG_NO_SERVICE */
 
553
 
 
554
// -------------------------------------
 
555
 
 
556
static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = {
 
557
    //    Minimum  Greatest min      Least max   Greatest max
 
558
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // ERA
 
559
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // YEAR
 
560
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // MONTH
 
561
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // WEEK_OF_YEAR
 
562
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // WEEK_OF_MONTH
 
563
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // DAY_OF_MONTH
 
564
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // DAY_OF_YEAR
 
565
    {           1,            1,             7,             7  }, // DAY_OF_WEEK
 
566
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // DAY_OF_WEEK_IN_MONTH
 
567
    {           0,            0,             1,             1  }, // AM_PM
 
568
    {           0,            0,            11,            11  }, // HOUR
 
569
    {           0,            0,            23,            23  }, // HOUR_OF_DAY
 
570
    {           0,            0,            59,            59  }, // MINUTE
 
571
    {           0,            0,            59,            59  }, // SECOND
 
572
    {           0,            0,           999,           999  }, // MILLISECOND
 
573
    {-12*kOneHour, -12*kOneHour,   12*kOneHour,   15*kOneHour  }, // ZONE_OFFSET
 
574
    {           0,            0,    1*kOneHour,    1*kOneHour  }, // DST_OFFSET
 
575
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // YEAR_WOY
 
576
    {           1,            1,             7,             7  }, // DOW_LOCAL
 
577
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // EXTENDED_YEAR
 
578
    { -0x7F000000,  -0x7F000000,    0x7F000000,    0x7F000000  }, // JULIAN_DAY
 
579
    {           0,            0, 24*kOneHour-1, 24*kOneHour-1  },  // MILLISECONDS_IN_DAY
 
580
    {           0,            0,             1,             1  },  // IS_LEAP_MONTH
 
581
};
 
582
 
 
583
// Resource bundle tags read by this class
 
584
static const char gDateTimeElements[] = "DateTimeElements";
 
585
 
 
586
// Data flow in Calendar
 
587
// ---------------------
 
588
 
 
589
// The current time is represented in two ways by Calendar: as UTC
 
590
// milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local
 
591
// fields such as MONTH, HOUR, AM_PM, etc.  It is possible to compute the
 
592
// millis from the fields, and vice versa.  The data needed to do this
 
593
// conversion is encapsulated by a TimeZone object owned by the Calendar.
 
594
// The data provided by the TimeZone object may also be overridden if the
 
595
// user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class
 
596
// keeps track of what information was most recently set by the caller, and
 
597
// uses that to compute any other information as needed.
 
598
 
 
599
// If the user sets the fields using set(), the data flow is as follows.
 
600
// This is implemented by the Calendar subclass's computeTime() method.
 
601
// During this process, certain fields may be ignored.  The disambiguation
 
602
// algorithm for resolving which fields to pay attention to is described
 
603
// above.
 
604
 
 
605
//   local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
 
606
//           |
 
607
//           | Using Calendar-specific algorithm
 
608
//           V
 
609
//   local standard millis
 
610
//           |
 
611
//           | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET
 
612
//           V
 
613
//   UTC millis (in time data member)
 
614
 
 
615
// If the user sets the UTC millis using setTime(), the data flow is as
 
616
// follows.  This is implemented by the Calendar subclass's computeFields()
 
617
// method.
 
618
 
 
619
//   UTC millis (in time data member)
 
620
//           |
 
621
//           | Using TimeZone getOffset()
 
622
//           V
 
623
//   local standard millis
 
624
//           |
 
625
//           | Using Calendar-specific algorithm
 
626
//           V
 
627
//   local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.)
 
628
 
 
629
// In general, a round trip from fields, through local and UTC millis, and
 
630
// back out to fields is made when necessary.  This is implemented by the
 
631
// complete() method.  Resolving a partial set of fields into a UTC millis
 
632
// value allows all remaining fields to be generated from that value.  If
 
633
// the Calendar is lenient, the fields are also renormalized to standard
 
634
// ranges when they are regenerated.
 
635
 
 
636
// -------------------------------------
 
637
 
 
638
Calendar::Calendar(UErrorCode& success)
 
639
:   UObject(),
 
640
fIsTimeSet(FALSE),
 
641
fAreFieldsSet(FALSE),
 
642
fAreAllFieldsSet(FALSE),
 
643
fAreFieldsVirtuallySet(FALSE),
 
644
fNextStamp((int32_t)kMinimumUserStamp),
 
645
fTime(0),
 
646
fLenient(TRUE),
 
647
fZone(0)
 
648
{
 
649
    clear();
 
650
    fZone = TimeZone::createDefault();
 
651
    if (fZone == NULL) {
 
652
        success = U_MEMORY_ALLOCATION_ERROR;
 
653
    }
 
654
    setWeekCountData(Locale::getDefault(), NULL, success);
 
655
}
 
656
 
 
657
// -------------------------------------
 
658
 
 
659
Calendar::Calendar(TimeZone* zone, const Locale& aLocale, UErrorCode& success)
 
660
:   UObject(),
 
661
fIsTimeSet(FALSE),
 
662
fAreFieldsSet(FALSE),
 
663
fAreAllFieldsSet(FALSE),
 
664
fAreFieldsVirtuallySet(FALSE),
 
665
fNextStamp((int32_t)kMinimumUserStamp),
 
666
fTime(0),
 
667
fLenient(TRUE),
 
668
fZone(0)
 
669
{
 
670
    if(zone == 0) {
 
671
#if defined (U_DEBUG_CAL)
 
672
        fprintf(stderr, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n",
 
673
            __FILE__, __LINE__);
 
674
#endif
 
675
        success = U_ILLEGAL_ARGUMENT_ERROR;
 
676
        return;
 
677
    }
 
678
 
 
679
    clear();    
 
680
    fZone = zone;
 
681
 
 
682
    setWeekCountData(aLocale, NULL, success);
 
683
}
 
684
 
 
685
// -------------------------------------
 
686
 
 
687
Calendar::Calendar(const TimeZone& zone, const Locale& aLocale, UErrorCode& success)
 
688
:   UObject(),
 
689
fIsTimeSet(FALSE),
 
690
fAreFieldsSet(FALSE),
 
691
fAreAllFieldsSet(FALSE),
 
692
fAreFieldsVirtuallySet(FALSE),
 
693
fNextStamp((int32_t)kMinimumUserStamp),
 
694
fTime(0),
 
695
fLenient(TRUE),
 
696
fZone(0)
 
697
{
 
698
    clear();
 
699
    fZone = zone.clone();
 
700
    if (fZone == NULL) {
 
701
        success = U_MEMORY_ALLOCATION_ERROR;
 
702
    }
 
703
    setWeekCountData(aLocale, NULL, success);
 
704
}
 
705
 
 
706
// -------------------------------------
 
707
 
 
708
Calendar::~Calendar()
 
709
{
 
710
    delete fZone;
 
711
}
 
712
 
 
713
// -------------------------------------
 
714
 
 
715
Calendar::Calendar(const Calendar &source)
 
716
:   UObject(source)
 
717
{
 
718
    fZone = 0;
 
719
    *this = source;
 
720
}
 
721
 
 
722
// -------------------------------------
 
723
 
 
724
Calendar &
 
725
Calendar::operator=(const Calendar &right)
 
726
{
 
727
    if (this != &right) {
 
728
        uprv_arrayCopy(right.fFields, fFields, UCAL_FIELD_COUNT);
 
729
        uprv_arrayCopy(right.fIsSet, fIsSet, UCAL_FIELD_COUNT);
 
730
        uprv_arrayCopy(right.fStamp, fStamp, UCAL_FIELD_COUNT);
 
731
        fTime                    = right.fTime;
 
732
        fIsTimeSet               = right.fIsTimeSet;
 
733
        fAreAllFieldsSet         = right.fAreAllFieldsSet;
 
734
        fAreFieldsSet            = right.fAreFieldsSet;
 
735
        fAreFieldsVirtuallySet   = right.fAreFieldsVirtuallySet;
 
736
        fLenient                 = right.fLenient;
 
737
        if (fZone != NULL) {
 
738
            delete fZone;
 
739
        }
 
740
        if (right.fZone != NULL) {
 
741
            fZone                = right.fZone->clone();
 
742
        }
 
743
        fFirstDayOfWeek          = right.fFirstDayOfWeek;
 
744
        fMinimalDaysInFirstWeek  = right.fMinimalDaysInFirstWeek;
 
745
        fNextStamp               = right.fNextStamp;
 
746
    }
 
747
 
 
748
    return *this;
 
749
}
 
750
 
 
751
// -------------------------------------
 
752
 
 
753
Calendar* U_EXPORT2
 
754
Calendar::createInstance(UErrorCode& success)
 
755
{
 
756
    return createInstance(TimeZone::createDefault(), Locale::getDefault(), success);
 
757
}
 
758
 
 
759
// -------------------------------------
 
760
 
 
761
Calendar* U_EXPORT2
 
762
Calendar::createInstance(const TimeZone& zone, UErrorCode& success)
 
763
{
 
764
    return createInstance(zone, Locale::getDefault(), success);
 
765
}
 
766
 
 
767
// -------------------------------------
 
768
 
 
769
Calendar* U_EXPORT2
 
770
Calendar::createInstance(const Locale& aLocale, UErrorCode& success)
 
771
{
 
772
    return createInstance(TimeZone::createDefault(), aLocale, success);
 
773
}
 
774
 
 
775
// ------------------------------------- Adopting 
 
776
 
 
777
// Note: this is the bottleneck that actually calls the service routines.
 
778
 
 
779
Calendar* U_EXPORT2
 
780
Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success)
 
781
{
 
782
    if (U_FAILURE(success)) {
 
783
        return NULL;
 
784
    }
 
785
 
 
786
    Locale actualLoc;
 
787
    UObject* u = NULL;
 
788
 
 
789
#if !UCONFIG_NO_SERVICE
 
790
    if (isCalendarServiceUsed()) {
 
791
        u = getCalendarService(success)->get(aLocale, LocaleKey::KIND_ANY, &actualLoc, success);
 
792
    }
 
793
    else
 
794
#endif
 
795
    {
 
796
        u = createStandardCalendar(getCalendarTypeForLocale(aLocale.getName()), aLocale, success);
 
797
    }
 
798
    Calendar* c = NULL;
 
799
 
 
800
    if(U_FAILURE(success) || !u) {
 
801
        delete zone;
 
802
        if(U_SUCCESS(success)) { // Propagate some kind of err
 
803
            success = U_INTERNAL_PROGRAM_ERROR;
 
804
        }
 
805
        return NULL;
 
806
    }
 
807
 
 
808
#if !UCONFIG_NO_SERVICE
 
809
    if(u->getDynamicClassID() == UnicodeString::getStaticClassID()) {
 
810
        // It's a unicode string telling us what type of calendar to load ("gregorian", etc)
 
811
        const UnicodeString& str = *(UnicodeString*)u;
 
812
 
 
813
        // Create a Locale over this string
 
814
        Locale l("");
 
815
        LocaleUtility::initLocaleFromName(str, l);
 
816
 
 
817
#ifdef U_DEBUG_CALSVC
 
818
        fprintf(stderr, "Calendar::createInstance(%s), looking up [%s]\n", aLocale.getName(), l.getName());
 
819
#endif
 
820
 
 
821
        Locale actualLoc2;
 
822
        delete u;
 
823
        u = NULL;
 
824
 
 
825
        // Don't overwrite actualLoc, since the actual loc from this call
 
826
        // may be something like "@calendar=gregorian" -- TODO investigate
 
827
        // further...
 
828
        c = (Calendar*)getCalendarService(success)->get(l, LocaleKey::KIND_ANY, &actualLoc2, success);
 
829
 
 
830
        if(U_FAILURE(success) || !c) {
 
831
            delete zone;
 
832
            if(U_SUCCESS(success)) { 
 
833
                success = U_INTERNAL_PROGRAM_ERROR; // Propagate some err
 
834
            }
 
835
            return NULL;
 
836
        }
 
837
 
 
838
        if(c->getDynamicClassID() == UnicodeString::getStaticClassID()) {
 
839
            // recursed! Second lookup returned a UnicodeString. 
 
840
            // Perhaps DefaultCalendar{} was set to another locale.
 
841
#ifdef U_DEBUG_CALSVC
 
842
            char tmp[200];
 
843
            const UnicodeString& str = *(UnicodeString*)c;
 
844
            // Extract a char* out of it..
 
845
            int32_t len = str.length();
 
846
            int32_t actLen = sizeof(tmp)-1;
 
847
            if(len > actLen) {
 
848
                len = actLen;
 
849
            }
 
850
            str.extract(0,len,tmp);
 
851
            tmp[len]=0;
 
852
 
 
853
            fprintf(stderr, "err - recursed, 2nd lookup was unistring %s\n", tmp);
 
854
#endif
 
855
            success = U_MISSING_RESOURCE_ERROR;  // requested a calendar type which could NOT be found.
 
856
            delete c;
 
857
            delete zone;
 
858
            return NULL;
 
859
        }
 
860
#ifdef U_DEBUG_CALSVC
 
861
        fprintf(stderr, "%p: setting week count data to locale %s, actual locale %s\n", c, (const char*)aLocale.getName(), (const char *)actualLoc.getName());
 
862
#endif
 
863
        c->setWeekCountData(aLocale, c->getType(), success);  // set the correct locale (this was an indirected calendar)
 
864
    }
 
865
    else
 
866
#endif /* UCONFIG_NO_SERVICE */
 
867
    {
 
868
        // a calendar was returned - we assume the factory did the right thing.
 
869
        c = (Calendar*)u;
 
870
    }
 
871
 
 
872
    // Now, reset calendar to default state:
 
873
    c->adoptTimeZone(zone); //  Set the correct time zone
 
874
    c->setTimeInMillis(getNow(), success); // let the new calendar have the current time.
 
875
 
 
876
    return c;
 
877
}
 
878
 
 
879
// -------------------------------------
 
880
 
 
881
Calendar* U_EXPORT2
 
882
Calendar::createInstance(const TimeZone& zone, const Locale& aLocale, UErrorCode& success)
 
883
{
 
884
    Calendar* c = createInstance(aLocale, success);
 
885
    if(U_SUCCESS(success) && c) {
 
886
        c->setTimeZone(zone);
 
887
    }
 
888
    return c; 
 
889
}
 
890
 
 
891
// -------------------------------------
 
892
 
 
893
UBool
 
894
Calendar::operator==(const Calendar& that) const
 
895
{
 
896
    UErrorCode status = U_ZERO_ERROR;
 
897
    return isEquivalentTo(that) &&
 
898
        getTimeInMillis(status) == that.getTimeInMillis(status) &&
 
899
        U_SUCCESS(status);
 
900
}
 
901
 
 
902
UBool 
 
903
Calendar::isEquivalentTo(const Calendar& other) const
 
904
{
 
905
    return getDynamicClassID() == other.getDynamicClassID() &&
 
906
        fLenient                == other.fLenient &&
 
907
        fFirstDayOfWeek         == other.fFirstDayOfWeek &&
 
908
        fMinimalDaysInFirstWeek == other.fMinimalDaysInFirstWeek &&
 
909
        *fZone                  == *other.fZone;
 
910
}
 
911
 
 
912
// -------------------------------------
 
913
 
 
914
UBool
 
915
Calendar::equals(const Calendar& when, UErrorCode& status) const
 
916
{
 
917
    return (this == &when ||
 
918
        getTime(status) == when.getTime(status));
 
919
}
 
920
 
 
921
// -------------------------------------
 
922
 
 
923
UBool
 
924
Calendar::before(const Calendar& when, UErrorCode& status) const
 
925
{
 
926
    return (this != &when &&
 
927
        getTimeInMillis(status) < when.getTimeInMillis(status));
 
928
}
 
929
 
 
930
// -------------------------------------
 
931
 
 
932
UBool
 
933
Calendar::after(const Calendar& when, UErrorCode& status) const
 
934
{
 
935
    return (this != &when &&
 
936
        getTimeInMillis(status) > when.getTimeInMillis(status));
 
937
}
 
938
 
 
939
// -------------------------------------
 
940
 
 
941
 
 
942
const Locale* U_EXPORT2
 
943
Calendar::getAvailableLocales(int32_t& count)
 
944
{
 
945
    return Locale::getAvailableLocales(count);
 
946
}
 
947
 
 
948
// -------------------------------------
 
949
 
 
950
StringEnumeration* U_EXPORT2 getKeywordValuesForLocale(const char* key,
 
951
                    const Locale& locale, UBool commonlyUsed, UErrorCode& status)
 
952
{
 
953
    // This is a wrapper over ucal_getKeywordValuesForLocale
 
954
    UEnumeration *uenum = ucal_getKeywordValuesForLocale(key, locale.getName(),
 
955
                                                        commonlyUsed, &status);
 
956
    if (U_FAILURE(status)) {
 
957
        uenum_close(uenum);
 
958
        return NULL;
 
959
    }
 
960
    return new UStringEnumeration(uenum);
 
961
}
 
962
 
 
963
// -------------------------------------
 
964
 
 
965
UDate U_EXPORT2
 
966
Calendar::getNow()
 
967
{
 
968
    return uprv_getUTCtime(); // return as milliseconds
 
969
}
 
970
 
 
971
// -------------------------------------
 
972
 
 
973
/**
 
974
* Gets this Calendar's current time as a long.
 
975
* @return the current time as UTC milliseconds from the epoch.
 
976
*/
 
977
double 
 
978
Calendar::getTimeInMillis(UErrorCode& status) const
 
979
{
 
980
    if(U_FAILURE(status)) 
 
981
        return 0.0;
 
982
 
 
983
    if ( ! fIsTimeSet) 
 
984
        ((Calendar*)this)->updateTime(status);
 
985
 
 
986
    /* Test for buffer overflows */
 
987
    if(U_FAILURE(status)) {
 
988
        return 0.0;
 
989
    }
 
990
    return fTime;
 
991
}
 
992
 
 
993
// -------------------------------------
 
994
 
 
995
/**
 
996
* Sets this Calendar's current time from the given long value.
 
997
* @param date the new time in UTC milliseconds from the epoch.
 
998
*/
 
999
void 
 
1000
Calendar::setTimeInMillis( double millis, UErrorCode& status ) {
 
1001
    if(U_FAILURE(status)) 
 
1002
        return;
 
1003
 
 
1004
    if (millis > MAX_MILLIS) {
 
1005
        millis = MAX_MILLIS;
 
1006
    } else if (millis < MIN_MILLIS) {
 
1007
        millis = MIN_MILLIS;
 
1008
    }
 
1009
 
 
1010
    fTime = millis;
 
1011
    fAreFieldsSet = fAreAllFieldsSet = FALSE;
 
1012
    fIsTimeSet = fAreFieldsVirtuallySet = TRUE;
 
1013
}
 
1014
 
 
1015
// -------------------------------------
 
1016
 
 
1017
int32_t
 
1018
Calendar::get(UCalendarDateFields field, UErrorCode& status) const
 
1019
{
 
1020
    // field values are only computed when actually requested; for more on when computation
 
1021
    // of various things happens, see the "data flow in Calendar" description at the top
 
1022
    // of this file
 
1023
    if (U_SUCCESS(status)) ((Calendar*)this)->complete(status); // Cast away const
 
1024
    return U_SUCCESS(status) ? fFields[field] : 0;
 
1025
}
 
1026
 
 
1027
// -------------------------------------
 
1028
 
 
1029
void
 
1030
Calendar::set(UCalendarDateFields field, int32_t value)
 
1031
{
 
1032
    if (fAreFieldsVirtuallySet) {
 
1033
        UErrorCode ec = U_ZERO_ERROR;
 
1034
        computeFields(ec);
 
1035
    }
 
1036
    fFields[field]     = value;
 
1037
    fStamp[field]     = fNextStamp++;
 
1038
    fIsSet[field]     = TRUE; // Remove later
 
1039
    fIsTimeSet = fAreFieldsSet = fAreFieldsVirtuallySet = FALSE;
 
1040
}
 
1041
 
 
1042
// -------------------------------------
 
1043
 
 
1044
void
 
1045
Calendar::set(int32_t year, int32_t month, int32_t date)
 
1046
{
 
1047
    set(UCAL_YEAR, year);
 
1048
    set(UCAL_MONTH, month);
 
1049
    set(UCAL_DATE, date);
 
1050
}
 
1051
 
 
1052
// -------------------------------------
 
1053
 
 
1054
void
 
1055
Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute)
 
1056
{
 
1057
    set(UCAL_YEAR, year);
 
1058
    set(UCAL_MONTH, month);
 
1059
    set(UCAL_DATE, date);
 
1060
    set(UCAL_HOUR_OF_DAY, hour);
 
1061
    set(UCAL_MINUTE, minute);
 
1062
}
 
1063
 
 
1064
// -------------------------------------
 
1065
 
 
1066
void
 
1067
Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute, int32_t second)
 
1068
{
 
1069
    set(UCAL_YEAR, year);
 
1070
    set(UCAL_MONTH, month);
 
1071
    set(UCAL_DATE, date);
 
1072
    set(UCAL_HOUR_OF_DAY, hour);
 
1073
    set(UCAL_MINUTE, minute);
 
1074
    set(UCAL_SECOND, second);
 
1075
}
 
1076
 
 
1077
// -------------------------------------
 
1078
 
 
1079
void
 
1080
Calendar::clear()
 
1081
{
 
1082
    for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
 
1083
        fFields[i]     = 0; // Must do this; other code depends on it
 
1084
        fStamp[i]     = kUnset;
 
1085
        fIsSet[i]     = FALSE; // Remove later
 
1086
    }
 
1087
    fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE;
 
1088
    // fTime is not 'cleared' - may be used if no fields are set.
 
1089
}
 
1090
 
 
1091
// -------------------------------------
 
1092
 
 
1093
void
 
1094
Calendar::clear(UCalendarDateFields field)
 
1095
{
 
1096
    if (fAreFieldsVirtuallySet) {
 
1097
        UErrorCode ec = U_ZERO_ERROR;
 
1098
        computeFields(ec);
 
1099
    }
 
1100
    fFields[field]         = 0;
 
1101
    fStamp[field]         = kUnset;
 
1102
    fIsSet[field]         = FALSE; // Remove later
 
1103
    fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = FALSE;
 
1104
}
 
1105
 
 
1106
// -------------------------------------
 
1107
 
 
1108
UBool
 
1109
Calendar::isSet(UCalendarDateFields field) const
 
1110
{
 
1111
    return fAreFieldsVirtuallySet || (fStamp[field] != kUnset);
 
1112
}
 
1113
 
 
1114
 
 
1115
int32_t Calendar::newestStamp(UCalendarDateFields first, UCalendarDateFields last, int32_t bestStampSoFar) const
 
1116
{
 
1117
    int32_t bestStamp = bestStampSoFar;
 
1118
    for (int32_t i=(int32_t)first; i<=(int32_t)last; ++i) {
 
1119
        if (fStamp[i] > bestStamp) {
 
1120
            bestStamp = fStamp[i];
 
1121
        }
 
1122
    }
 
1123
    return bestStamp;
 
1124
}
 
1125
 
 
1126
 
 
1127
// -------------------------------------
 
1128
 
 
1129
void
 
1130
Calendar::complete(UErrorCode& status)
 
1131
{
 
1132
    if (!fIsTimeSet) {
 
1133
        updateTime(status);
 
1134
        /* Test for buffer overflows */
 
1135
        if(U_FAILURE(status)) {
 
1136
            return;
 
1137
        }
 
1138
    }
 
1139
    if (!fAreFieldsSet) {
 
1140
        computeFields(status); // fills in unset fields
 
1141
        /* Test for buffer overflows */
 
1142
        if(U_FAILURE(status)) {
 
1143
            return;
 
1144
        }
 
1145
        fAreFieldsSet         = TRUE;
 
1146
        fAreAllFieldsSet     = TRUE;
 
1147
    }
 
1148
}
 
1149
 
 
1150
//-------------------------------------------------------------------------
 
1151
// Protected utility methods for use by subclasses.  These are very handy
 
1152
// for implementing add, roll, and computeFields.
 
1153
//-------------------------------------------------------------------------
 
1154
 
 
1155
/**
 
1156
* Adjust the specified field so that it is within
 
1157
* the allowable range for the date to which this calendar is set.
 
1158
* For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH}
 
1159
* field for a calendar set to April 31 would cause it to be set
 
1160
* to April 30.
 
1161
* <p>
 
1162
* <b>Subclassing:</b>
 
1163
* <br>
 
1164
* This utility method is intended for use by subclasses that need to implement
 
1165
* their own overrides of {@link #roll roll} and {@link #add add}.
 
1166
* <p>
 
1167
* <b>Note:</b>
 
1168
* <code>pinField</code> is implemented in terms of
 
1169
* {@link #getActualMinimum getActualMinimum}
 
1170
* and {@link #getActualMaximum getActualMaximum}.  If either of those methods uses
 
1171
* a slow, iterative algorithm for a particular field, it would be
 
1172
* unwise to attempt to call <code>pinField</code> for that field.  If you
 
1173
* really do need to do so, you should override this method to do
 
1174
* something more efficient for that field.
 
1175
* <p>
 
1176
* @param field The calendar field whose value should be pinned.
 
1177
*
 
1178
* @see #getActualMinimum
 
1179
* @see #getActualMaximum
 
1180
* @stable ICU 2.0
 
1181
*/
 
1182
void Calendar::pinField(UCalendarDateFields field, UErrorCode& status) {
 
1183
    int32_t max = getActualMaximum(field, status);
 
1184
    int32_t min = getActualMinimum(field, status);
 
1185
 
 
1186
    if (fFields[field] > max) {
 
1187
        set(field, max);
 
1188
    } else if (fFields[field] < min) {
 
1189
        set(field, min);
 
1190
    }
 
1191
}
 
1192
 
 
1193
 
 
1194
void Calendar::computeFields(UErrorCode &ec)
 
1195
{
 
1196
  if (U_FAILURE(ec)) {
 
1197
        return;
 
1198
    }
 
1199
    // Compute local wall millis
 
1200
    double localMillis = internalGetTime();
 
1201
    int32_t rawOffset, dstOffset;
 
1202
    getTimeZone().getOffset(localMillis, FALSE, rawOffset, dstOffset, ec);
 
1203
    localMillis += (rawOffset + dstOffset); 
 
1204
 
 
1205
    // Mark fields as set.  Do this before calling handleComputeFields().
 
1206
    uint32_t mask =   //fInternalSetMask;
 
1207
        (1 << UCAL_ERA) |
 
1208
        (1 << UCAL_YEAR) |
 
1209
        (1 << UCAL_MONTH) |
 
1210
        (1 << UCAL_DAY_OF_MONTH) | // = UCAL_DATE
 
1211
        (1 << UCAL_DAY_OF_YEAR) |
 
1212
        (1 << UCAL_EXTENDED_YEAR);  
 
1213
 
 
1214
    for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
 
1215
        if ((mask & 1) == 0) {
 
1216
            fStamp[i] = kInternallySet;
 
1217
            fIsSet[i] = TRUE; // Remove later
 
1218
        } else {
 
1219
            fStamp[i] = kUnset;
 
1220
            fIsSet[i] = FALSE; // Remove later
 
1221
        }
 
1222
        mask >>= 1;
 
1223
    }
 
1224
 
 
1225
    // We used to check for and correct extreme millis values (near
 
1226
    // Long.MIN_VALUE or Long.MAX_VALUE) here.  Such values would cause
 
1227
    // overflows from positive to negative (or vice versa) and had to
 
1228
    // be manually tweaked.  We no longer need to do this because we
 
1229
    // have limited the range of supported dates to those that have a
 
1230
    // Julian day that fits into an int.  This allows us to implement a
 
1231
    // JULIAN_DAY field and also removes some inelegant code. - Liu
 
1232
    // 11/6/00
 
1233
 
 
1234
    int32_t days =  (int32_t)ClockMath::floorDivide(localMillis, (double)kOneDay);
 
1235
 
 
1236
    internalSet(UCAL_JULIAN_DAY,days + kEpochStartAsJulianDay);
 
1237
 
 
1238
#if defined (U_DEBUG_CAL)
 
1239
    //fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n",
 
1240
    //__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
 
1241
#endif  
 
1242
 
 
1243
    computeGregorianAndDOWFields(fFields[UCAL_JULIAN_DAY], ec);
 
1244
 
 
1245
    // Call framework method to have subclass compute its fields.
 
1246
    // These must include, at a minimum, MONTH, DAY_OF_MONTH,
 
1247
    // EXTENDED_YEAR, YEAR, DAY_OF_YEAR.  This method will call internalSet(),
 
1248
    // which will update stamp[].
 
1249
    handleComputeFields(fFields[UCAL_JULIAN_DAY], ec);
 
1250
 
 
1251
    // Compute week-related fields, based on the subclass-computed
 
1252
    // fields computed by handleComputeFields().
 
1253
    computeWeekFields(ec);
 
1254
 
 
1255
    // Compute time-related fields.  These are indepent of the date and
 
1256
    // of the subclass algorithm.  They depend only on the local zone
 
1257
    // wall milliseconds in day.
 
1258
    int32_t millisInDay =  (int32_t) (localMillis - (days * kOneDay));
 
1259
    fFields[UCAL_MILLISECONDS_IN_DAY] = millisInDay;
 
1260
    fFields[UCAL_MILLISECOND] = millisInDay % 1000;
 
1261
    millisInDay /= 1000;
 
1262
    fFields[UCAL_SECOND] = millisInDay % 60;
 
1263
    millisInDay /= 60;
 
1264
    fFields[UCAL_MINUTE] = millisInDay % 60;
 
1265
    millisInDay /= 60;
 
1266
    fFields[UCAL_HOUR_OF_DAY] = millisInDay;
 
1267
    fFields[UCAL_AM_PM] = millisInDay / 12; // Assume AM == 0
 
1268
    fFields[UCAL_HOUR] = millisInDay % 12;
 
1269
    fFields[UCAL_ZONE_OFFSET] = rawOffset;
 
1270
    fFields[UCAL_DST_OFFSET] = dstOffset;
 
1271
}
 
1272
 
 
1273
uint8_t Calendar::julianDayToDayOfWeek(double julian)
 
1274
{
 
1275
    // If julian is negative, then julian%7 will be negative, so we adjust
 
1276
    // accordingly.  We add 1 because Julian day 0 is Monday.
 
1277
    int8_t dayOfWeek = (int8_t) uprv_fmod(julian + 1, 7);
 
1278
 
 
1279
    uint8_t result = (uint8_t)(dayOfWeek + ((dayOfWeek < 0) ? (7+UCAL_SUNDAY ) : UCAL_SUNDAY));
 
1280
    return result;
 
1281
}
 
1282
 
 
1283
/**
 
1284
* Compute the Gregorian calendar year, month, and day of month from
 
1285
* the given Julian day.  These values are not stored in fields, but in
 
1286
* member variables gregorianXxx.  Also compute the DAY_OF_WEEK and
 
1287
* DOW_LOCAL fields.
 
1288
*/
 
1289
void Calendar::computeGregorianAndDOWFields(int32_t julianDay, UErrorCode &ec)
 
1290
{
 
1291
    computeGregorianFields(julianDay, ec);
 
1292
 
 
1293
    // Compute day of week: JD 0 = Monday
 
1294
    int32_t dow = julianDayToDayOfWeek(julianDay);
 
1295
    internalSet(UCAL_DAY_OF_WEEK,dow);
 
1296
 
 
1297
    // Calculate 1-based localized day of week
 
1298
    int32_t dowLocal = dow - getFirstDayOfWeek() + 1;
 
1299
    if (dowLocal < 1) {
 
1300
        dowLocal += 7;
 
1301
    }
 
1302
    internalSet(UCAL_DOW_LOCAL,dowLocal);
 
1303
    fFields[UCAL_DOW_LOCAL] = dowLocal;
 
1304
}
 
1305
 
 
1306
/**
 
1307
* Compute the Gregorian calendar year, month, and day of month from the
 
1308
* Julian day.  These values are not stored in fields, but in member
 
1309
* variables gregorianXxx.  They are used for time zone computations and by
 
1310
* subclasses that are Gregorian derivatives.  Subclasses may call this
 
1311
* method to perform a Gregorian calendar millis->fields computation.
 
1312
*/
 
1313
void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode & /* ec */) {
 
1314
    int32_t gregorianDayOfWeekUnused;
 
1315
    Grego::dayToFields(julianDay - kEpochStartAsJulianDay, fGregorianYear, fGregorianMonth, fGregorianDayOfMonth, gregorianDayOfWeekUnused, fGregorianDayOfYear);
 
1316
}
 
1317
 
 
1318
/**
 
1319
* Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH,
 
1320
* DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR,
 
1321
* DAY_OF_WEEK, and DAY_OF_YEAR.  The latter fields are computed by the
 
1322
* subclass based on the calendar system.
 
1323
*
 
1324
* <p>The YEAR_WOY field is computed simplistically.  It is equal to YEAR
 
1325
* most of the time, but at the year boundary it may be adjusted to YEAR-1
 
1326
* or YEAR+1 to reflect the overlap of a week into an adjacent year.  In
 
1327
* this case, a simple increment or decrement is performed on YEAR, even
 
1328
* though this may yield an invalid YEAR value.  For instance, if the YEAR
 
1329
* is part of a calendar system with an N-year cycle field CYCLE, then
 
1330
* incrementing the YEAR may involve incrementing CYCLE and setting YEAR
 
1331
* back to 0 or 1.  This is not handled by this code, and in fact cannot be
 
1332
* simply handled without having subclasses define an entire parallel set of
 
1333
* fields for fields larger than or equal to a year.  This additional
 
1334
* complexity is not warranted, since the intention of the YEAR_WOY field is
 
1335
* to support ISO 8601 notation, so it will typically be used with a
 
1336
* proleptic Gregorian calendar, which has no field larger than a year.
 
1337
*/
 
1338
void Calendar::computeWeekFields(UErrorCode &ec) {
 
1339
    if(U_FAILURE(ec)) { 
 
1340
        return;
 
1341
    }
 
1342
    int32_t eyear = fFields[UCAL_EXTENDED_YEAR];
 
1343
    int32_t dayOfWeek = fFields[UCAL_DAY_OF_WEEK];
 
1344
    int32_t dayOfYear = fFields[UCAL_DAY_OF_YEAR];
 
1345
 
 
1346
    // WEEK_OF_YEAR start
 
1347
    // Compute the week of the year.  For the Gregorian calendar, valid week
 
1348
    // numbers run from 1 to 52 or 53, depending on the year, the first day
 
1349
    // of the week, and the minimal days in the first week.  For other
 
1350
    // calendars, the valid range may be different -- it depends on the year
 
1351
    // length.  Days at the start of the year may fall into the last week of
 
1352
    // the previous year; days at the end of the year may fall into the
 
1353
    // first week of the next year.  ASSUME that the year length is less than
 
1354
    // 7000 days.
 
1355
    int32_t yearOfWeekOfYear = eyear;
 
1356
    int32_t relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; // 0..6
 
1357
    int32_t relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; // 0..6
 
1358
    int32_t woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53
 
1359
    if ((7 - relDowJan1) >= getMinimalDaysInFirstWeek()) {
 
1360
        ++woy;
 
1361
    }
 
1362
 
 
1363
    // Adjust for weeks at the year end that overlap into the previous or
 
1364
    // next calendar year.
 
1365
    if (woy == 0) {
 
1366
        // We are the last week of the previous year.
 
1367
        // Check to see if we are in the last week; if so, we need
 
1368
        // to handle the case in which we are the first week of the
 
1369
        // next year.
 
1370
 
 
1371
        int32_t prevDoy = dayOfYear + handleGetYearLength(eyear - 1);
 
1372
        woy = weekNumber(prevDoy, dayOfWeek);
 
1373
        yearOfWeekOfYear--;
 
1374
    } else {
 
1375
        int32_t lastDoy = handleGetYearLength(eyear);
 
1376
        // Fast check: For it to be week 1 of the next year, the DOY
 
1377
        // must be on or after L-5, where L is yearLength(), then it
 
1378
        // cannot possibly be week 1 of the next year:
 
1379
        //          L-5                  L
 
1380
        // doy: 359 360 361 362 363 364 365 001
 
1381
        // dow:      1   2   3   4   5   6   7
 
1382
        if (dayOfYear >= (lastDoy - 5)) {
 
1383
            int32_t lastRelDow = (relDow + lastDoy - dayOfYear) % 7;
 
1384
            if (lastRelDow < 0) {
 
1385
                lastRelDow += 7;
 
1386
            }
 
1387
            if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) &&
 
1388
                ((dayOfYear + 7 - relDow) > lastDoy)) {
 
1389
                    woy = 1;
 
1390
                    yearOfWeekOfYear++;
 
1391
                }
 
1392
        }
 
1393
    }
 
1394
    fFields[UCAL_WEEK_OF_YEAR] = woy;
 
1395
    fFields[UCAL_YEAR_WOY] = yearOfWeekOfYear;
 
1396
    // WEEK_OF_YEAR end
 
1397
 
 
1398
    int32_t dayOfMonth = fFields[UCAL_DAY_OF_MONTH];
 
1399
    fFields[UCAL_WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek);
 
1400
    fFields[UCAL_DAY_OF_WEEK_IN_MONTH] = (dayOfMonth-1) / 7 + 1;
 
1401
#if defined (U_DEBUG_CAL)
 
1402
    if(fFields[UCAL_DAY_OF_WEEK_IN_MONTH]==0) fprintf(stderr, "%s:%d: DOWIM %d on %g\n", 
 
1403
        __FILE__, __LINE__,fFields[UCAL_DAY_OF_WEEK_IN_MONTH], fTime);
 
1404
#endif
 
1405
}
 
1406
 
 
1407
 
 
1408
int32_t Calendar::weekNumber(int32_t desiredDay, int32_t dayOfPeriod, int32_t dayOfWeek)
 
1409
{
 
1410
    // Determine the day of the week of the first day of the period
 
1411
    // in question (either a year or a month).  Zero represents the
 
1412
    // first day of the week on this calendar.
 
1413
    int32_t periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % 7;
 
1414
    if (periodStartDayOfWeek < 0) periodStartDayOfWeek += 7;
 
1415
 
 
1416
    // Compute the week number.  Initially, ignore the first week, which
 
1417
    // may be fractional (or may not be).  We add periodStartDayOfWeek in
 
1418
    // order to fill out the first week, if it is fractional.
 
1419
    int32_t weekNo = (desiredDay + periodStartDayOfWeek - 1)/7;
 
1420
 
 
1421
    // If the first week is long enough, then count it.  If
 
1422
    // the minimal days in the first week is one, or if the period start
 
1423
    // is zero, we always increment weekNo.
 
1424
    if ((7 - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo;
 
1425
 
 
1426
    return weekNo;
 
1427
}
 
1428
 
 
1429
void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode &/* status */)
 
1430
{
 
1431
    internalSet(UCAL_MONTH, getGregorianMonth());
 
1432
    internalSet(UCAL_DAY_OF_MONTH, getGregorianDayOfMonth());
 
1433
    internalSet(UCAL_DAY_OF_YEAR, getGregorianDayOfYear());
 
1434
    int32_t eyear = getGregorianYear();
 
1435
    internalSet(UCAL_EXTENDED_YEAR, eyear);
 
1436
    int32_t era = GregorianCalendar::AD;
 
1437
    if (eyear < 1) {
 
1438
        era = GregorianCalendar::BC;
 
1439
        eyear = 1 - eyear;
 
1440
    }
 
1441
    internalSet(UCAL_ERA, era);
 
1442
    internalSet(UCAL_YEAR, eyear);
 
1443
}
 
1444
// -------------------------------------
 
1445
 
 
1446
 
 
1447
void Calendar::roll(EDateFields field, int32_t amount, UErrorCode& status) 
 
1448
{
 
1449
    roll((UCalendarDateFields)field, amount, status);
 
1450
}
 
1451
 
 
1452
void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status)
 
1453
{
 
1454
    if (amount == 0) {
 
1455
        return; // Nothing to do
 
1456
    }
 
1457
 
 
1458
    complete(status);
 
1459
 
 
1460
    if(U_FAILURE(status)) {
 
1461
        return;
 
1462
    }
 
1463
    switch (field) {
 
1464
    case UCAL_DAY_OF_MONTH:
 
1465
    case UCAL_AM_PM:
 
1466
    case UCAL_MINUTE:
 
1467
    case UCAL_SECOND:
 
1468
    case UCAL_MILLISECOND:
 
1469
    case UCAL_MILLISECONDS_IN_DAY:
 
1470
    case UCAL_ERA:
 
1471
        // These are the standard roll instructions.  These work for all
 
1472
        // simple cases, that is, cases in which the limits are fixed, such
 
1473
        // as the hour, the day of the month, and the era.
 
1474
        {
 
1475
            int32_t min = getActualMinimum(field,status);
 
1476
            int32_t max = getActualMaximum(field,status);
 
1477
            int32_t gap = max - min + 1;
 
1478
 
 
1479
            int32_t value = internalGet(field) + amount;
 
1480
            value = (value - min) % gap;
 
1481
            if (value < 0) {
 
1482
                value += gap;
 
1483
            }
 
1484
            value += min;
 
1485
 
 
1486
            set(field, value);
 
1487
            return;
 
1488
        }
 
1489
 
 
1490
    case UCAL_HOUR:
 
1491
    case UCAL_HOUR_OF_DAY:
 
1492
        // Rolling the hour is difficult on the ONSET and CEASE days of
 
1493
        // daylight savings.  For example, if the change occurs at
 
1494
        // 2 AM, we have the following progression:
 
1495
        // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst
 
1496
        // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std
 
1497
        // To get around this problem we don't use fields; we manipulate
 
1498
        // the time in millis directly.
 
1499
        {
 
1500
            // Assume min == 0 in calculations below
 
1501
            double start = getTimeInMillis(status);
 
1502
            int32_t oldHour = internalGet(field);
 
1503
            int32_t max = getMaximum(field);
 
1504
            int32_t newHour = (oldHour + amount) % (max + 1);
 
1505
            if (newHour < 0) {
 
1506
                newHour += max + 1;
 
1507
            }
 
1508
            setTimeInMillis(start + kOneHour * (newHour - oldHour),status);
 
1509
            return;
 
1510
        }
 
1511
 
 
1512
    case UCAL_MONTH:
 
1513
        // Rolling the month involves both pinning the final value
 
1514
        // and adjusting the DAY_OF_MONTH if necessary.  We only adjust the
 
1515
        // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
 
1516
        // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
 
1517
        {
 
1518
            int32_t max = getActualMaximum(UCAL_MONTH, status);
 
1519
            int32_t mon = (internalGet(UCAL_MONTH) + amount) % (max+1);
 
1520
 
 
1521
            if (mon < 0) {
 
1522
                mon += (max + 1);
 
1523
            }
 
1524
            set(UCAL_MONTH, mon);
 
1525
 
 
1526
            // Keep the day of month in range.  We don't want to spill over
 
1527
            // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 ->
 
1528
            // mar3.
 
1529
            pinField(UCAL_DAY_OF_MONTH,status);
 
1530
            return;
 
1531
        }
 
1532
 
 
1533
    case UCAL_YEAR:
 
1534
    case UCAL_YEAR_WOY:
 
1535
    case UCAL_EXTENDED_YEAR:
 
1536
        // Rolling the year can involve pinning the DAY_OF_MONTH.
 
1537
        set(field, internalGet(field) + amount);
 
1538
        pinField(UCAL_MONTH,status);
 
1539
        pinField(UCAL_DAY_OF_MONTH,status);
 
1540
        return;
 
1541
 
 
1542
    case UCAL_WEEK_OF_MONTH:
 
1543
        {
 
1544
            // This is tricky, because during the roll we may have to shift
 
1545
            // to a different day of the week.  For example:
 
1546
 
 
1547
            //    s  m  t  w  r  f  s
 
1548
            //          1  2  3  4  5
 
1549
            //    6  7  8  9 10 11 12
 
1550
 
 
1551
            // When rolling from the 6th or 7th back one week, we go to the
 
1552
            // 1st (assuming that the first partial week counts).  The same
 
1553
            // thing happens at the end of the month.
 
1554
 
 
1555
            // The other tricky thing is that we have to figure out whether
 
1556
            // the first partial week actually counts or not, based on the
 
1557
            // minimal first days in the week.  And we have to use the
 
1558
            // correct first day of the week to delineate the week
 
1559
            // boundaries.
 
1560
 
 
1561
            // Here's our algorithm.  First, we find the real boundaries of
 
1562
            // the month.  Then we discard the first partial week if it
 
1563
            // doesn't count in this locale.  Then we fill in the ends with
 
1564
            // phantom days, so that the first partial week and the last
 
1565
            // partial week are full weeks.  We then have a nice square
 
1566
            // block of weeks.  We do the usual rolling within this block,
 
1567
            // as is done elsewhere in this method.  If we wind up on one of
 
1568
            // the phantom days that we added, we recognize this and pin to
 
1569
            // the first or the last day of the month.  Easy, eh?
 
1570
 
 
1571
            // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
 
1572
            // in this locale.  We have dow in 0..6.
 
1573
            int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
 
1574
            if (dow < 0) dow += 7;
 
1575
 
 
1576
            // Find the day of the week (normalized for locale) for the first
 
1577
            // of the month.
 
1578
            int32_t fdm = (dow - internalGet(UCAL_DAY_OF_MONTH) + 1) % 7;
 
1579
            if (fdm < 0) fdm += 7;
 
1580
 
 
1581
            // Get the first day of the first full week of the month,
 
1582
            // including phantom days, if any.  Figure out if the first week
 
1583
            // counts or not; if it counts, then fill in phantom days.  If
 
1584
            // not, advance to the first real full week (skip the partial week).
 
1585
            int32_t start;
 
1586
            if ((7 - fdm) < getMinimalDaysInFirstWeek())
 
1587
                start = 8 - fdm; // Skip the first partial week
 
1588
            else
 
1589
                start = 1 - fdm; // This may be zero or negative
 
1590
 
 
1591
            // Get the day of the week (normalized for locale) for the last
 
1592
            // day of the month.
 
1593
            int32_t monthLen = getActualMaximum(UCAL_DAY_OF_MONTH, status);
 
1594
            int32_t ldm = (monthLen - internalGet(UCAL_DAY_OF_MONTH) + dow) % 7;
 
1595
            // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.
 
1596
 
 
1597
            // Get the limit day for the blocked-off rectangular month; that
 
1598
            // is, the day which is one past the last day of the month,
 
1599
            // after the month has already been filled in with phantom days
 
1600
            // to fill out the last week.  This day has a normalized DOW of 0.
 
1601
            int32_t limit = monthLen + 7 - ldm;
 
1602
 
 
1603
            // Now roll between start and (limit - 1).
 
1604
            int32_t gap = limit - start;
 
1605
            int32_t day_of_month = (internalGet(UCAL_DAY_OF_MONTH) + amount*7 -
 
1606
                start) % gap;
 
1607
            if (day_of_month < 0) day_of_month += gap;
 
1608
            day_of_month += start;
 
1609
 
 
1610
            // Finally, pin to the real start and end of the month.
 
1611
            if (day_of_month < 1) day_of_month = 1;
 
1612
            if (day_of_month > monthLen) day_of_month = monthLen;
 
1613
 
 
1614
            // Set the DAY_OF_MONTH.  We rely on the fact that this field
 
1615
            // takes precedence over everything else (since all other fields
 
1616
            // are also set at this point).  If this fact changes (if the
 
1617
            // disambiguation algorithm changes) then we will have to unset
 
1618
            // the appropriate fields here so that DAY_OF_MONTH is attended
 
1619
            // to.
 
1620
            set(UCAL_DAY_OF_MONTH, day_of_month);
 
1621
            return;
 
1622
        }
 
1623
    case UCAL_WEEK_OF_YEAR:
 
1624
        {
 
1625
            // This follows the outline of WEEK_OF_MONTH, except it applies
 
1626
            // to the whole year.  Please see the comment for WEEK_OF_MONTH
 
1627
            // for general notes.
 
1628
 
 
1629
            // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
 
1630
            // in this locale.  We have dow in 0..6.
 
1631
            int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
 
1632
            if (dow < 0) dow += 7;
 
1633
 
 
1634
            // Find the day of the week (normalized for locale) for the first
 
1635
            // of the year.
 
1636
            int32_t fdy = (dow - internalGet(UCAL_DAY_OF_YEAR) + 1) % 7;
 
1637
            if (fdy < 0) fdy += 7;
 
1638
 
 
1639
            // Get the first day of the first full week of the year,
 
1640
            // including phantom days, if any.  Figure out if the first week
 
1641
            // counts or not; if it counts, then fill in phantom days.  If
 
1642
            // not, advance to the first real full week (skip the partial week).
 
1643
            int32_t start;
 
1644
            if ((7 - fdy) < getMinimalDaysInFirstWeek())
 
1645
                start = 8 - fdy; // Skip the first partial week
 
1646
            else
 
1647
                start = 1 - fdy; // This may be zero or negative
 
1648
 
 
1649
            // Get the day of the week (normalized for locale) for the last
 
1650
            // day of the year.
 
1651
            int32_t yearLen = getActualMaximum(UCAL_DAY_OF_YEAR,status);
 
1652
            int32_t ldy = (yearLen - internalGet(UCAL_DAY_OF_YEAR) + dow) % 7;
 
1653
            // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here.
 
1654
 
 
1655
            // Get the limit day for the blocked-off rectangular year; that
 
1656
            // is, the day which is one past the last day of the year,
 
1657
            // after the year has already been filled in with phantom days
 
1658
            // to fill out the last week.  This day has a normalized DOW of 0.
 
1659
            int32_t limit = yearLen + 7 - ldy;
 
1660
 
 
1661
            // Now roll between start and (limit - 1).
 
1662
            int32_t gap = limit - start;
 
1663
            int32_t day_of_year = (internalGet(UCAL_DAY_OF_YEAR) + amount*7 -
 
1664
                start) % gap;
 
1665
            if (day_of_year < 0) day_of_year += gap;
 
1666
            day_of_year += start;
 
1667
 
 
1668
            // Finally, pin to the real start and end of the month.
 
1669
            if (day_of_year < 1) day_of_year = 1;
 
1670
            if (day_of_year > yearLen) day_of_year = yearLen;
 
1671
 
 
1672
            // Make sure that the year and day of year are attended to by
 
1673
            // clearing other fields which would normally take precedence.
 
1674
            // If the disambiguation algorithm is changed, this section will
 
1675
            // have to be updated as well.
 
1676
            set(UCAL_DAY_OF_YEAR, day_of_year);
 
1677
            clear(UCAL_MONTH);
 
1678
            return;
 
1679
        }
 
1680
    case UCAL_DAY_OF_YEAR:
 
1681
        {
 
1682
            // Roll the day of year using millis.  Compute the millis for
 
1683
            // the start of the year, and get the length of the year.
 
1684
            double delta = amount * kOneDay; // Scale up from days to millis
 
1685
            double min2 = internalGet(UCAL_DAY_OF_YEAR)-1;
 
1686
            min2 *= kOneDay;
 
1687
            min2 = internalGetTime() - min2;
 
1688
 
 
1689
            //      double min2 = internalGetTime() - (internalGet(UCAL_DAY_OF_YEAR) - 1.0) * kOneDay;
 
1690
            double newtime;
 
1691
 
 
1692
            double yearLength = getActualMaximum(UCAL_DAY_OF_YEAR,status);
 
1693
            double oneYear = yearLength;
 
1694
            oneYear *= kOneDay;
 
1695
            newtime = uprv_fmod((internalGetTime() + delta - min2), oneYear);
 
1696
            if (newtime < 0) newtime += oneYear;
 
1697
            setTimeInMillis(newtime + min2, status);
 
1698
            return;
 
1699
        }
 
1700
    case UCAL_DAY_OF_WEEK:
 
1701
    case UCAL_DOW_LOCAL:
 
1702
        {
 
1703
            // Roll the day of week using millis.  Compute the millis for
 
1704
            // the start of the week, using the first day of week setting.
 
1705
            // Restrict the millis to [start, start+7days).
 
1706
            double delta = amount * kOneDay; // Scale up from days to millis
 
1707
            // Compute the number of days before the current day in this
 
1708
            // week.  This will be a value 0..6.
 
1709
            int32_t leadDays = internalGet(field);
 
1710
            leadDays -= (field == UCAL_DAY_OF_WEEK) ? getFirstDayOfWeek() : 1;
 
1711
            if (leadDays < 0) leadDays += 7;
 
1712
            double min2 = internalGetTime() - leadDays * kOneDay;
 
1713
            double newtime = uprv_fmod((internalGetTime() + delta - min2), kOneWeek);
 
1714
            if (newtime < 0) newtime += kOneWeek;
 
1715
            setTimeInMillis(newtime + min2, status);
 
1716
            return;
 
1717
        }
 
1718
    case UCAL_DAY_OF_WEEK_IN_MONTH:
 
1719
        {
 
1720
            // Roll the day of week in the month using millis.  Determine
 
1721
            // the first day of the week in the month, and then the last,
 
1722
            // and then roll within that range.
 
1723
            double delta = amount * kOneWeek; // Scale up from weeks to millis
 
1724
            // Find the number of same days of the week before this one
 
1725
            // in this month.
 
1726
            int32_t preWeeks = (internalGet(UCAL_DAY_OF_MONTH) - 1) / 7;
 
1727
            // Find the number of same days of the week after this one
 
1728
            // in this month.
 
1729
            int32_t postWeeks = (getActualMaximum(UCAL_DAY_OF_MONTH,status) -
 
1730
                internalGet(UCAL_DAY_OF_MONTH)) / 7;
 
1731
            // From these compute the min and gap millis for rolling.
 
1732
            double min2 = internalGetTime() - preWeeks * kOneWeek;
 
1733
            double gap2 = kOneWeek * (preWeeks + postWeeks + 1); // Must add 1!
 
1734
            // Roll within this range
 
1735
            double newtime = uprv_fmod((internalGetTime() + delta - min2), gap2);
 
1736
            if (newtime < 0) newtime += gap2;
 
1737
            setTimeInMillis(newtime + min2, status);
 
1738
            return;
 
1739
        }
 
1740
    case UCAL_JULIAN_DAY:
 
1741
        set(field, internalGet(field) + amount);
 
1742
        return;
 
1743
    default:
 
1744
        // Other fields cannot be rolled by this method
 
1745
#if defined (U_DEBUG_CAL)
 
1746
        fprintf(stderr, "%s:%d: ILLEGAL ARG because of roll on non-rollable field %s\n", 
 
1747
            __FILE__, __LINE__,fldName(field));
 
1748
#endif
 
1749
        status = U_ILLEGAL_ARGUMENT_ERROR;
 
1750
    }
 
1751
}
 
1752
 
 
1753
void Calendar::add(EDateFields field, int32_t amount, UErrorCode& status)
 
1754
{
 
1755
    Calendar::add((UCalendarDateFields)field, amount, status);
 
1756
}
 
1757
 
 
1758
// -------------------------------------
 
1759
void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status)
 
1760
{
 
1761
    if (amount == 0) {
 
1762
        return;   // Do nothing!
 
1763
    }
 
1764
 
 
1765
    // We handle most fields in the same way.  The algorithm is to add
 
1766
    // a computed amount of millis to the current millis.  The only
 
1767
    // wrinkle is with DST -- for some fields, like the DAY_OF_MONTH,
 
1768
    // we don't want the HOUR to shift due to changes in DST.  If the
 
1769
    // result of the add operation is to move from DST to Standard, or
 
1770
    // vice versa, we need to adjust by an hour forward or back,
 
1771
    // respectively.  For such fields we set keepHourInvariant to TRUE.
 
1772
 
 
1773
    // We only adjust the DST for fields larger than an hour.  For
 
1774
    // fields smaller than an hour, we cannot adjust for DST without
 
1775
    // causing problems.  for instance, if you add one hour to April 5,
 
1776
    // 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an
 
1777
    // illegal value), but then the adjustment sees the change and
 
1778
    // compensates by subtracting an hour.  As a result the time
 
1779
    // doesn't advance at all.
 
1780
 
 
1781
    // For some fields larger than a day, such as a UCAL_MONTH, we pin the
 
1782
    // UCAL_DAY_OF_MONTH.  This allows <March 31>.add(UCAL_MONTH, 1) to be
 
1783
    // <April 30>, rather than <April 31> => <May 1>.
 
1784
 
 
1785
    double delta = amount; // delta in ms
 
1786
    UBool keepHourInvariant = TRUE;
 
1787
 
 
1788
    switch (field) {
 
1789
    case UCAL_ERA:
 
1790
        set(field, get(field, status) + amount);
 
1791
        pinField(UCAL_ERA, status);
 
1792
        return;
 
1793
 
 
1794
    case UCAL_YEAR:
 
1795
    case UCAL_EXTENDED_YEAR:
 
1796
    case UCAL_YEAR_WOY:
 
1797
    case UCAL_MONTH:
 
1798
        set(field, get(field, status) + amount);
 
1799
        pinField(UCAL_DAY_OF_MONTH, status);
 
1800
        return;
 
1801
 
 
1802
    case UCAL_WEEK_OF_YEAR:
 
1803
    case UCAL_WEEK_OF_MONTH:
 
1804
    case UCAL_DAY_OF_WEEK_IN_MONTH:
 
1805
        delta *= kOneWeek;
 
1806
        break;
 
1807
 
 
1808
    case UCAL_AM_PM:
 
1809
        delta *= 12 * kOneHour;
 
1810
        break;
 
1811
 
 
1812
    case UCAL_DAY_OF_MONTH:
 
1813
    case UCAL_DAY_OF_YEAR:
 
1814
    case UCAL_DAY_OF_WEEK:
 
1815
    case UCAL_DOW_LOCAL:
 
1816
    case UCAL_JULIAN_DAY:
 
1817
        delta *= kOneDay;
 
1818
        break;
 
1819
 
 
1820
    case UCAL_HOUR_OF_DAY:
 
1821
    case UCAL_HOUR:
 
1822
        delta *= kOneHour;
 
1823
        keepHourInvariant = FALSE;
 
1824
        break;
 
1825
 
 
1826
    case UCAL_MINUTE:
 
1827
        delta *= kOneMinute;
 
1828
        keepHourInvariant = FALSE;
 
1829
        break;
 
1830
 
 
1831
    case UCAL_SECOND:
 
1832
        delta *= kOneSecond;
 
1833
        keepHourInvariant = FALSE;
 
1834
        break;
 
1835
 
 
1836
    case UCAL_MILLISECOND:
 
1837
    case UCAL_MILLISECONDS_IN_DAY:
 
1838
        keepHourInvariant = FALSE;
 
1839
        break;
 
1840
 
 
1841
    default:
 
1842
#if defined (U_DEBUG_CAL)
 
1843
        fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s not addable",
 
1844
            __FILE__, __LINE__, fldName(field));
 
1845
#endif
 
1846
        status = U_ILLEGAL_ARGUMENT_ERROR;
 
1847
        return;
 
1848
        //  throw new IllegalArgumentException("Calendar.add(" + fieldName(field) +
 
1849
        //                                     ") not supported");
 
1850
    }
 
1851
 
 
1852
    // In order to keep the hour invariant (for fields where this is
 
1853
    // appropriate), record the DST_OFFSET before and after the add()
 
1854
    // operation.  If it has changed, then adjust the millis to
 
1855
    // compensate.
 
1856
    int32_t dst = 0;
 
1857
    int32_t hour = 0;
 
1858
    if (keepHourInvariant) {
 
1859
        dst = get(UCAL_DST_OFFSET, status);
 
1860
        hour = internalGet(UCAL_HOUR_OF_DAY);
 
1861
    }
 
1862
 
 
1863
    setTimeInMillis(getTimeInMillis(status) + delta, status);
 
1864
 
 
1865
    if (keepHourInvariant) {
 
1866
        dst -= get(UCAL_DST_OFFSET, status);
 
1867
        if (dst != 0) {
 
1868
            // We have done an hour-invariant adjustment but the
 
1869
            // DST offset has altered.  We adjust millis to keep
 
1870
            // the hour constant.  In cases such as midnight after
 
1871
            // a DST change which occurs at midnight, there is the
 
1872
            // danger of adjusting into a different day.  To avoid
 
1873
            // this we make the adjustment only if it actually
 
1874
            // maintains the hour.
 
1875
            double t = internalGetTime();
 
1876
            setTimeInMillis(t + dst, status);
 
1877
            if (get(UCAL_HOUR_OF_DAY, status) != hour) {
 
1878
                setTimeInMillis(t, status);
 
1879
            }
 
1880
        }
 
1881
    } 
 
1882
}
 
1883
 
 
1884
// -------------------------------------
 
1885
int32_t Calendar::fieldDifference(UDate when, EDateFields field, UErrorCode& status) {
 
1886
    return fieldDifference(when, (UCalendarDateFields) field, status);
 
1887
}
 
1888
 
 
1889
int32_t Calendar::fieldDifference(UDate targetMs, UCalendarDateFields field, UErrorCode& ec) {
 
1890
    if (U_FAILURE(ec)) return 0;
 
1891
    int32_t min = 0;
 
1892
    double startMs = getTimeInMillis(ec);
 
1893
    // Always add from the start millis.  This accomodates
 
1894
    // operations like adding years from February 29, 2000 up to
 
1895
    // February 29, 2004.  If 1, 1, 1, 1 is added to the year
 
1896
    // field, the DOM gets pinned to 28 and stays there, giving an
 
1897
    // incorrect DOM difference of 1.  We have to add 1, reset, 2,
 
1898
    // reset, 3, reset, 4.
 
1899
    if (startMs < targetMs) {
 
1900
        int32_t max = 1;
 
1901
        // Find a value that is too large
 
1902
        while (U_SUCCESS(ec)) {
 
1903
            setTimeInMillis(startMs, ec);
 
1904
            add(field, max, ec);
 
1905
            double ms = getTimeInMillis(ec);
 
1906
            if (ms == targetMs) {
 
1907
                return max;
 
1908
            } else if (ms > targetMs) {
 
1909
                break;
 
1910
            } else {
 
1911
                max <<= 1;
 
1912
                if (max < 0) {
 
1913
                    // Field difference too large to fit into int32_t
 
1914
#if defined (U_DEBUG_CAL)
 
1915
                    fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
 
1916
                        __FILE__, __LINE__, fldName(field));
 
1917
#endif
 
1918
                    ec = U_ILLEGAL_ARGUMENT_ERROR;
 
1919
                }
 
1920
            }
 
1921
        }
 
1922
        // Do a binary search
 
1923
        while ((max - min) > 1 && U_SUCCESS(ec)) {
 
1924
            int32_t t = (min + max) / 2;
 
1925
            setTimeInMillis(startMs, ec);
 
1926
            add(field, t, ec);
 
1927
            double ms = getTimeInMillis(ec);
 
1928
            if (ms == targetMs) {
 
1929
                return t;
 
1930
            } else if (ms > targetMs) {
 
1931
                max = t;
 
1932
            } else {
 
1933
                min = t;
 
1934
            }
 
1935
        }
 
1936
    } else if (startMs > targetMs) {
 
1937
        int32_t max = -1;
 
1938
        // Find a value that is too small
 
1939
        while (U_SUCCESS(ec)) {
 
1940
            setTimeInMillis(startMs, ec);
 
1941
            add(field, max, ec);
 
1942
            double ms = getTimeInMillis(ec);
 
1943
            if (ms == targetMs) {
 
1944
                return max;
 
1945
            } else if (ms < targetMs) {
 
1946
                break;
 
1947
            } else {
 
1948
                max <<= 1;
 
1949
                if (max == 0) {
 
1950
                    // Field difference too large to fit into int32_t
 
1951
#if defined (U_DEBUG_CAL)
 
1952
                    fprintf(stderr, "%s:%d: ILLEGAL ARG because field %s's max too large for int32_t\n",
 
1953
                        __FILE__, __LINE__, fldName(field));
 
1954
#endif
 
1955
                    ec = U_ILLEGAL_ARGUMENT_ERROR;
 
1956
                }
 
1957
            }
 
1958
        }
 
1959
        // Do a binary search
 
1960
        while ((min - max) > 1 && U_SUCCESS(ec)) {
 
1961
            int32_t t = (min + max) / 2;
 
1962
            setTimeInMillis(startMs, ec);
 
1963
            add(field, t, ec);
 
1964
            double ms = getTimeInMillis(ec);
 
1965
            if (ms == targetMs) {
 
1966
                return t;
 
1967
            } else if (ms < targetMs) {
 
1968
                max = t;
 
1969
            } else {
 
1970
                min = t;
 
1971
            }
 
1972
        }
 
1973
    }
 
1974
    // Set calendar to end point
 
1975
    setTimeInMillis(startMs, ec);
 
1976
    add(field, min, ec);
 
1977
 
 
1978
    /* Test for buffer overflows */
 
1979
    if(U_FAILURE(ec)) {
 
1980
        return 0;
 
1981
    }
 
1982
    return min;
 
1983
}
 
1984
 
 
1985
// -------------------------------------
 
1986
 
 
1987
void
 
1988
Calendar::adoptTimeZone(TimeZone* zone)
 
1989
{
 
1990
    // Do nothing if passed-in zone is NULL
 
1991
    if (zone == NULL) return;
 
1992
 
 
1993
    // fZone should always be non-null
 
1994
    if (fZone != NULL) delete fZone;
 
1995
    fZone = zone;
 
1996
 
 
1997
    // if the zone changes, we need to recompute the time fields
 
1998
    fAreFieldsSet = FALSE;
 
1999
}
 
2000
 
 
2001
// -------------------------------------
 
2002
void
 
2003
Calendar::setTimeZone(const TimeZone& zone)
 
2004
{
 
2005
    adoptTimeZone(zone.clone());
 
2006
}
 
2007
 
 
2008
// -------------------------------------
 
2009
 
 
2010
const TimeZone&
 
2011
Calendar::getTimeZone() const
 
2012
{
 
2013
    return *fZone;
 
2014
}
 
2015
 
 
2016
// -------------------------------------
 
2017
 
 
2018
TimeZone*
 
2019
Calendar::orphanTimeZone()
 
2020
{
 
2021
    TimeZone *z = fZone;
 
2022
    // we let go of the time zone; the new time zone is the system default time zone
 
2023
    fZone = TimeZone::createDefault();
 
2024
    return z;
 
2025
}
 
2026
 
 
2027
// -------------------------------------
 
2028
 
 
2029
void
 
2030
Calendar::setLenient(UBool lenient)
 
2031
{
 
2032
    fLenient = lenient;
 
2033
}
 
2034
 
 
2035
// -------------------------------------
 
2036
 
 
2037
UBool
 
2038
Calendar::isLenient() const
 
2039
{
 
2040
    return fLenient;
 
2041
}
 
2042
 
 
2043
// -------------------------------------
 
2044
 
 
2045
void
 
2046
Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value)
 
2047
{
 
2048
    if (fFirstDayOfWeek != value &&
 
2049
        value >= UCAL_SUNDAY && value <= UCAL_SATURDAY) {
 
2050
            fFirstDayOfWeek = value;
 
2051
            fAreFieldsSet = FALSE;
 
2052
        }
 
2053
}
 
2054
 
 
2055
// -------------------------------------
 
2056
 
 
2057
Calendar::EDaysOfWeek
 
2058
Calendar::getFirstDayOfWeek() const
 
2059
{
 
2060
    return (Calendar::EDaysOfWeek)fFirstDayOfWeek;
 
2061
}
 
2062
 
 
2063
UCalendarDaysOfWeek
 
2064
Calendar::getFirstDayOfWeek(UErrorCode & /*status*/) const
 
2065
{
 
2066
    return fFirstDayOfWeek;
 
2067
}
 
2068
// -------------------------------------
 
2069
 
 
2070
void
 
2071
Calendar::setMinimalDaysInFirstWeek(uint8_t value)
 
2072
{
 
2073
    // Values less than 1 have the same effect as 1; values greater
 
2074
    // than 7 have the same effect as 7. However, we normalize values
 
2075
    // so operator== and so forth work.
 
2076
    if (value < 1) {
 
2077
        value = 1;
 
2078
    } else if (value > 7) {
 
2079
        value = 7;
 
2080
    }
 
2081
    if (fMinimalDaysInFirstWeek != value) {
 
2082
        fMinimalDaysInFirstWeek = value;
 
2083
        fAreFieldsSet = FALSE;
 
2084
    }
 
2085
}
 
2086
 
 
2087
// -------------------------------------
 
2088
 
 
2089
uint8_t
 
2090
Calendar::getMinimalDaysInFirstWeek() const
 
2091
{
 
2092
    return fMinimalDaysInFirstWeek;
 
2093
}
 
2094
 
 
2095
// ------------------------------------- limits
 
2096
 
 
2097
int32_t 
 
2098
Calendar::getMinimum(EDateFields field) const {
 
2099
    return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MINIMUM);
 
2100
}
 
2101
 
 
2102
int32_t
 
2103
Calendar::getMinimum(UCalendarDateFields field) const
 
2104
{
 
2105
    return getLimit(field,UCAL_LIMIT_MINIMUM);
 
2106
}
 
2107
 
 
2108
// -------------------------------------
 
2109
int32_t
 
2110
Calendar::getMaximum(EDateFields field) const
 
2111
{
 
2112
    return getLimit((UCalendarDateFields) field,UCAL_LIMIT_MAXIMUM);
 
2113
}
 
2114
 
 
2115
int32_t
 
2116
Calendar::getMaximum(UCalendarDateFields field) const
 
2117
{
 
2118
    return getLimit(field,UCAL_LIMIT_MAXIMUM);
 
2119
}
 
2120
 
 
2121
// -------------------------------------
 
2122
int32_t
 
2123
Calendar::getGreatestMinimum(EDateFields field) const
 
2124
{
 
2125
    return getLimit((UCalendarDateFields)field,UCAL_LIMIT_GREATEST_MINIMUM);
 
2126
}
 
2127
 
 
2128
int32_t
 
2129
Calendar::getGreatestMinimum(UCalendarDateFields field) const
 
2130
{
 
2131
    return getLimit(field,UCAL_LIMIT_GREATEST_MINIMUM);
 
2132
}
 
2133
 
 
2134
// -------------------------------------
 
2135
int32_t
 
2136
Calendar::getLeastMaximum(EDateFields field) const
 
2137
{
 
2138
    return getLimit((UCalendarDateFields) field,UCAL_LIMIT_LEAST_MAXIMUM);
 
2139
}
 
2140
 
 
2141
int32_t
 
2142
Calendar::getLeastMaximum(UCalendarDateFields field) const
 
2143
{
 
2144
    return getLimit( field,UCAL_LIMIT_LEAST_MAXIMUM);
 
2145
}
 
2146
 
 
2147
// -------------------------------------
 
2148
int32_t 
 
2149
Calendar::getActualMinimum(EDateFields field, UErrorCode& status) const
 
2150
{
 
2151
    return getActualMinimum((UCalendarDateFields) field, status);
 
2152
}
 
2153
 
 
2154
int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) const {
 
2155
    switch (field) {
 
2156
    case UCAL_DAY_OF_WEEK:
 
2157
    case UCAL_AM_PM:
 
2158
    case UCAL_HOUR:
 
2159
    case UCAL_HOUR_OF_DAY:
 
2160
    case UCAL_MINUTE:
 
2161
    case UCAL_SECOND:
 
2162
    case UCAL_MILLISECOND:
 
2163
    case UCAL_ZONE_OFFSET:
 
2164
    case UCAL_DST_OFFSET:
 
2165
    case UCAL_DOW_LOCAL:
 
2166
    case UCAL_JULIAN_DAY:
 
2167
    case UCAL_MILLISECONDS_IN_DAY:
 
2168
    case UCAL_IS_LEAP_MONTH:
 
2169
        return kCalendarLimits[field][limitType];
 
2170
 
 
2171
    case UCAL_WEEK_OF_MONTH:
 
2172
        {
 
2173
            int32_t limit;
 
2174
            if (limitType == UCAL_LIMIT_MINIMUM) {
 
2175
                limit = getMinimalDaysInFirstWeek() == 1 ? 1 : 0;
 
2176
            } else if (limitType == UCAL_LIMIT_GREATEST_MINIMUM) {
 
2177
                limit = 1;
 
2178
            } else {
 
2179
                int32_t minDaysInFirst = getMinimalDaysInFirstWeek();
 
2180
                int32_t daysInMonth = handleGetLimit(UCAL_DAY_OF_MONTH, limitType);
 
2181
                if (limitType == UCAL_LIMIT_LEAST_MAXIMUM) {
 
2182
                    limit = (daysInMonth + (7 - minDaysInFirst)) / 7;
 
2183
                } else { // limitType == UCAL_LIMIT_MAXIMUM
 
2184
                    limit = (daysInMonth + 6 + (7 - minDaysInFirst)) / 7;
 
2185
                }
 
2186
            }
 
2187
            return limit;
 
2188
        }
 
2189
    default:
 
2190
        return handleGetLimit(field, limitType);
 
2191
    }
 
2192
}
 
2193
 
 
2194
 
 
2195
int32_t
 
2196
Calendar::getActualMinimum(UCalendarDateFields field, UErrorCode& status) const
 
2197
{
 
2198
    int32_t fieldValue = getGreatestMinimum(field);
 
2199
    int32_t endValue = getMinimum(field);
 
2200
 
 
2201
    // if we know that the minimum value is always the same, just return it
 
2202
    if (fieldValue == endValue) {
 
2203
        return fieldValue;
 
2204
    }
 
2205
 
 
2206
    // clone the calendar so we don't mess with the real one, and set it to
 
2207
    // accept anything for the field values
 
2208
    Calendar *work = (Calendar*)this->clone();
 
2209
    if (work == NULL) {
 
2210
        status = U_MEMORY_ALLOCATION_ERROR;
 
2211
        return 0;
 
2212
    }
 
2213
    work->setLenient(TRUE);
 
2214
 
 
2215
    // now try each value from getLeastMaximum() to getMaximum() one by one until
 
2216
    // we get a value that normalizes to another value.  The last value that
 
2217
    // normalizes to itself is the actual minimum for the current date
 
2218
    int32_t result = fieldValue;
 
2219
 
 
2220
    do {
 
2221
        work->set(field, fieldValue);
 
2222
        if (work->get(field, status) != fieldValue) {
 
2223
            break;
 
2224
        } 
 
2225
        else {
 
2226
            result = fieldValue;
 
2227
            fieldValue--;
 
2228
        }
 
2229
    } while (fieldValue >= endValue);
 
2230
 
 
2231
    delete work;
 
2232
 
 
2233
    /* Test for buffer overflows */
 
2234
    if(U_FAILURE(status)) {
 
2235
        return 0;
 
2236
    }
 
2237
    return result;
 
2238
}
 
2239
 
 
2240
// -------------------------------------
 
2241
 
 
2242
 
 
2243
 
 
2244
/**
 
2245
* Ensure that each field is within its valid range by calling {@link
 
2246
* #validateField(int)} on each field that has been set.  This method
 
2247
* should only be called if this calendar is not lenient.
 
2248
* @see #isLenient
 
2249
* @see #validateField(int)
 
2250
*/
 
2251
void Calendar::validateFields(UErrorCode &status) {
 
2252
    for (int32_t field = 0; U_SUCCESS(status) && (field < UCAL_FIELD_COUNT); field++) {
 
2253
        if (isSet((UCalendarDateFields)field)) {
 
2254
            validateField((UCalendarDateFields)field, status);
 
2255
        }
 
2256
    }
 
2257
}
 
2258
 
 
2259
/**
 
2260
* Validate a single field of this calendar.  Subclasses should
 
2261
* override this method to validate any calendar-specific fields.
 
2262
* Generic fields can be handled by
 
2263
* <code>Calendar.validateField()</code>.
 
2264
* @see #validateField(int, int, int)
 
2265
*/
 
2266
void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) {
 
2267
    int32_t y;
 
2268
    switch (field) {
 
2269
    case UCAL_DAY_OF_MONTH:
 
2270
        y = handleGetExtendedYear();
 
2271
        validateField(field, 1, handleGetMonthLength(y, internalGet(UCAL_MONTH)), status);
 
2272
        break;
 
2273
    case UCAL_DAY_OF_YEAR:
 
2274
        y = handleGetExtendedYear();
 
2275
        validateField(field, 1, handleGetYearLength(y), status);
 
2276
        break;
 
2277
    case UCAL_DAY_OF_WEEK_IN_MONTH:
 
2278
        if (internalGet(field) == 0) {
 
2279
#if defined (U_DEBUG_CAL)
 
2280
            fprintf(stderr, "%s:%d: ILLEGAL ARG because DOW in month cannot be 0\n", 
 
2281
                __FILE__, __LINE__);
 
2282
#endif
 
2283
            status = U_ILLEGAL_ARGUMENT_ERROR; // "DAY_OF_WEEK_IN_MONTH cannot be zero"
 
2284
            return;
 
2285
        }
 
2286
        validateField(field, getMinimum(field), getMaximum(field), status);
 
2287
        break;
 
2288
    default:
 
2289
        validateField(field, getMinimum(field), getMaximum(field), status);
 
2290
        break;
 
2291
    }
 
2292
}
 
2293
 
 
2294
/**
 
2295
* Validate a single field of this calendar given its minimum and
 
2296
* maximum allowed value.  If the field is out of range, throw a
 
2297
* descriptive <code>IllegalArgumentException</code>.  Subclasses may
 
2298
* use this method in their implementation of {@link
 
2299
* #validateField(int)}.
 
2300
*/
 
2301
void Calendar::validateField(UCalendarDateFields field, int32_t min, int32_t max, UErrorCode& status)
 
2302
{
 
2303
    int32_t value = fFields[field];
 
2304
    if (value < min || value > max) {
 
2305
#if defined (U_DEBUG_CAL)
 
2306
        fprintf(stderr, "%s:%d: ILLEGAL ARG because of field %s out of range %d..%d  at %d\n", 
 
2307
            __FILE__, __LINE__,fldName(field),min,max,value);
 
2308
#endif
 
2309
        status = U_ILLEGAL_ARGUMENT_ERROR;
 
2310
        return;
 
2311
    }
 
2312
}
 
2313
 
 
2314
// -------------------------
 
2315
 
 
2316
const UFieldResolutionTable* Calendar::getFieldResolutionTable() const {
 
2317
    return kDatePrecedence;
 
2318
}
 
2319
 
 
2320
 
 
2321
UCalendarDateFields Calendar::newerField(UCalendarDateFields defaultField, UCalendarDateFields alternateField) const
 
2322
{
 
2323
    if (fStamp[alternateField] > fStamp[defaultField]) {
 
2324
        return alternateField;
 
2325
    }
 
2326
    return defaultField;
 
2327
}
 
2328
 
 
2329
UCalendarDateFields Calendar::resolveFields(const UFieldResolutionTable* precedenceTable) {
 
2330
    int32_t bestField = UCAL_FIELD_COUNT;
 
2331
    for (int32_t g=0; precedenceTable[g][0][0] != -1 && (bestField == UCAL_FIELD_COUNT); ++g) {
 
2332
        int32_t bestStamp = kUnset;
 
2333
        for (int32_t l=0; precedenceTable[g][l][0] != -1; ++l) {
 
2334
            int32_t lineStamp = kUnset;
 
2335
            // Skip over first entry if it is negative
 
2336
            for (int32_t i=((precedenceTable[g][l][0]>=kResolveRemap)?1:0); precedenceTable[g][l][i]!=-1; ++i) {
 
2337
                int32_t s = fStamp[precedenceTable[g][l][i]];
 
2338
                // If any field is unset then don't use this line
 
2339
                if (s == kUnset) {
 
2340
                    goto linesInGroup;
 
2341
                } else if(s > lineStamp) {
 
2342
                    lineStamp = s;
 
2343
                }
 
2344
            }
 
2345
            // Record new maximum stamp & field no.
 
2346
            if (lineStamp > bestStamp) {
 
2347
                bestStamp = lineStamp;
 
2348
                bestField = precedenceTable[g][l][0]; // First field refers to entire line
 
2349
            }
 
2350
linesInGroup:
 
2351
            ;
 
2352
        }
 
2353
    }
 
2354
    return (UCalendarDateFields)( (bestField>=kResolveRemap)?(bestField&(kResolveRemap-1)):bestField  );
 
2355
}
 
2356
 
 
2357
const UFieldResolutionTable Calendar::kDatePrecedence[] =
 
2358
 
2359
    {
 
2360
        { UCAL_DAY_OF_MONTH, kResolveSTOP },
 
2361
        { UCAL_WEEK_OF_YEAR, UCAL_DAY_OF_WEEK, kResolveSTOP },
 
2362
        { UCAL_WEEK_OF_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
 
2363
        { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
 
2364
        { UCAL_WEEK_OF_YEAR, UCAL_DOW_LOCAL, kResolveSTOP },
 
2365
        { UCAL_WEEK_OF_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
 
2366
        { UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
 
2367
        { UCAL_DAY_OF_YEAR, kResolveSTOP },
 
2368
        { kResolveRemap | UCAL_DAY_OF_MONTH, UCAL_YEAR, kResolveSTOP },  // if YEAR is set over YEAR_WOY use DAY_OF_MONTH
 
2369
        { kResolveRemap | UCAL_WEEK_OF_YEAR, UCAL_YEAR_WOY, kResolveSTOP },  // if YEAR_WOY is set,  calc based on WEEK_OF_YEAR
 
2370
        { kResolveSTOP }
 
2371
    },
 
2372
    {
 
2373
        { UCAL_WEEK_OF_YEAR, kResolveSTOP },
 
2374
        { UCAL_WEEK_OF_MONTH, kResolveSTOP },
 
2375
        { UCAL_DAY_OF_WEEK_IN_MONTH, kResolveSTOP },
 
2376
        { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DAY_OF_WEEK, kResolveSTOP },
 
2377
        { kResolveRemap | UCAL_DAY_OF_WEEK_IN_MONTH, UCAL_DOW_LOCAL, kResolveSTOP },
 
2378
        { kResolveSTOP }
 
2379
    }, 
 
2380
    {{kResolveSTOP}}
 
2381
};
 
2382
 
 
2383
 
 
2384
const UFieldResolutionTable Calendar::kDOWPrecedence[] = 
 
2385
{
 
2386
    {
 
2387
        { UCAL_DAY_OF_WEEK,kResolveSTOP, kResolveSTOP },
 
2388
        { UCAL_DOW_LOCAL,kResolveSTOP, kResolveSTOP },
 
2389
        {kResolveSTOP}
 
2390
    },
 
2391
    {{kResolveSTOP}}
 
2392
};
 
2393
 
 
2394
// precedence for calculating a year
 
2395
const UFieldResolutionTable Calendar::kYearPrecedence[] = 
 
2396
{
 
2397
    {
 
2398
        { UCAL_YEAR, kResolveSTOP },
 
2399
        { UCAL_EXTENDED_YEAR, kResolveSTOP },
 
2400
        { UCAL_YEAR_WOY, UCAL_WEEK_OF_YEAR, kResolveSTOP },  // YEAR_WOY is useless without WEEK_OF_YEAR
 
2401
        { kResolveSTOP }
 
2402
    },
 
2403
    {{kResolveSTOP}}
 
2404
};
 
2405
 
 
2406
 
 
2407
// -------------------------
 
2408
 
 
2409
 
 
2410
void Calendar::computeTime(UErrorCode& status) {
 
2411
    if (!isLenient()) {
 
2412
        validateFields(status);
 
2413
        if (U_FAILURE(status)) {
 
2414
            return;
 
2415
        }
 
2416
    }
 
2417
 
 
2418
    // Compute the Julian day
 
2419
    int32_t julianDay = computeJulianDay();
 
2420
 
 
2421
    double millis = Grego::julianDayToMillis(julianDay);
 
2422
 
 
2423
#if defined (U_DEBUG_CAL)
 
2424
    //  int32_t julianInsanityCheck =  (int32_t)ClockMath::floorDivide(millis, kOneDay);
 
2425
    //  julianInsanityCheck += kEpochStartAsJulianDay;
 
2426
    //  if(1 || julianInsanityCheck != julianDay) {
 
2427
    //    fprintf(stderr, "%s:%d- D'oh- computed jules %d, to mills (%s)%.lf, recomputed %d\n",
 
2428
    //            __FILE__, __LINE__, julianDay, millis<0.0?"NEG":"", millis, julianInsanityCheck);
 
2429
    //  }
 
2430
#endif
 
2431
 
 
2432
    int32_t millisInDay;
 
2433
 
 
2434
    // We only use MILLISECONDS_IN_DAY if it has been set by the user.
 
2435
    // This makes it possible for the caller to set the calendar to a
 
2436
    // time and call clear(MONTH) to reset the MONTH to January.  This
 
2437
    // is legacy behavior.  Without this, clear(MONTH) has no effect,
 
2438
    // since the internally set JULIAN_DAY is used.
 
2439
    if (fStamp[UCAL_MILLISECONDS_IN_DAY] >= ((int32_t)kMinimumUserStamp) &&
 
2440
        newestStamp(UCAL_AM_PM, UCAL_MILLISECOND, kUnset) <= fStamp[UCAL_MILLISECONDS_IN_DAY]) {
 
2441
            millisInDay = internalGet(UCAL_MILLISECONDS_IN_DAY);
 
2442
        } else {
 
2443
            millisInDay = computeMillisInDay();
 
2444
        }
 
2445
 
 
2446
        // Compute the time zone offset and DST offset.  There are two potential
 
2447
        // ambiguities here.  We'll assume a 2:00 am (wall time) switchover time
 
2448
        // for discussion purposes here.
 
2449
        // 1. The transition into DST.  Here, a designated time of 2:00 am - 2:59 am
 
2450
        //    can be in standard or in DST depending.  However, 2:00 am is an invalid
 
2451
        //    representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
 
2452
        //    We assume standard time, that is, 2:30 am is interpreted as 3:30 am DST.
 
2453
        // 2. The transition out of DST.  Here, a designated time of 1:00 am - 1:59 am
 
2454
        //    can be in standard or DST.  Both are valid representations (the rep
 
2455
        //    jumps from 1:59:59 DST to 1:00:00 Std).
 
2456
        //    Again, we assume standard time, that is, 1:30 am is interpreted as 1:30 am Std.
 
2457
        // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
 
2458
        // or DST_OFFSET fields; then we use those fields.
 
2459
        if (fStamp[UCAL_ZONE_OFFSET] >= ((int32_t)kMinimumUserStamp) ||
 
2460
            fStamp[UCAL_DST_OFFSET] >= ((int32_t)kMinimumUserStamp)) {
 
2461
                millisInDay -= internalGet(UCAL_ZONE_OFFSET) + internalGet(UCAL_DST_OFFSET);
 
2462
            } else {
 
2463
                millisInDay -= computeZoneOffset(millis, millisInDay,status);
 
2464
            }
 
2465
 
 
2466
            internalSetTime(millis + millisInDay);
 
2467
}
 
2468
 
 
2469
/**
 
2470
* Compute the milliseconds in the day from the fields.  This is a
 
2471
* value from 0 to 23:59:59.999 inclusive, unless fields are out of
 
2472
* range, in which case it can be an arbitrary value.  This value
 
2473
* reflects local zone wall time.
 
2474
* @stable ICU 2.0
 
2475
*/
 
2476
int32_t Calendar::computeMillisInDay() {
 
2477
  // Do the time portion of the conversion.
 
2478
 
 
2479
    int32_t millisInDay = 0;
 
2480
 
 
2481
    // Find the best set of fields specifying the time of day.  There
 
2482
    // are only two possibilities here; the HOUR_OF_DAY or the
 
2483
    // AM_PM and the HOUR.
 
2484
    int32_t hourOfDayStamp = fStamp[UCAL_HOUR_OF_DAY];
 
2485
    int32_t hourStamp = (fStamp[UCAL_HOUR] > fStamp[UCAL_AM_PM])?fStamp[UCAL_HOUR]:fStamp[UCAL_AM_PM];
 
2486
    int32_t bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp;
 
2487
 
 
2488
    // Hours
 
2489
    if (bestStamp != kUnset) {
 
2490
        if (bestStamp == hourOfDayStamp) {
 
2491
            // Don't normalize here; let overflow bump into the next period.
 
2492
            // This is consistent with how we handle other fields.
 
2493
            millisInDay += internalGet(UCAL_HOUR_OF_DAY);
 
2494
        } else {
 
2495
            // Don't normalize here; let overflow bump into the next period.
 
2496
            // This is consistent with how we handle other fields.
 
2497
            millisInDay += internalGet(UCAL_HOUR);
 
2498
            millisInDay += 12 * internalGet(UCAL_AM_PM); // Default works for unset AM_PM
 
2499
        }
 
2500
    }
 
2501
 
 
2502
    // We use the fact that unset == 0; we start with millisInDay
 
2503
    // == HOUR_OF_DAY.
 
2504
    millisInDay *= 60;
 
2505
    millisInDay += internalGet(UCAL_MINUTE); // now have minutes
 
2506
    millisInDay *= 60;
 
2507
    millisInDay += internalGet(UCAL_SECOND); // now have seconds
 
2508
    millisInDay *= 1000;
 
2509
    millisInDay += internalGet(UCAL_MILLISECOND); // now have millis
 
2510
 
 
2511
    return millisInDay;
 
2512
}
 
2513
 
 
2514
/**
 
2515
* This method can assume EXTENDED_YEAR has been set.
 
2516
* @param millis milliseconds of the date fields
 
2517
* @param millisInDay milliseconds of the time fields; may be out
 
2518
* or range.
 
2519
* @stable ICU 2.0
 
2520
*/
 
2521
int32_t Calendar::computeZoneOffset(double millis, int32_t millisInDay, UErrorCode &ec) {
 
2522
    int32_t rawOffset, dstOffset;
 
2523
    getTimeZone().getOffset(millis+millisInDay, TRUE, rawOffset, dstOffset, ec);
 
2524
    return rawOffset + dstOffset;
 
2525
    // Note: Because we pass in wall millisInDay, rather than
 
2526
    // standard millisInDay, we interpret "1:00 am" on the day
 
2527
    // of cessation of DST as "1:00 am Std" (assuming the time
 
2528
    // of cessation is 2:00 am).
 
2529
}
 
2530
 
 
2531
int32_t Calendar::computeJulianDay() 
 
2532
{
 
2533
    // We want to see if any of the date fields is newer than the
 
2534
    // JULIAN_DAY.  If not, then we use JULIAN_DAY.  If so, then we do
 
2535
    // the normal resolution.  We only use JULIAN_DAY if it has been
 
2536
    // set by the user.  This makes it possible for the caller to set
 
2537
    // the calendar to a time and call clear(MONTH) to reset the MONTH
 
2538
    // to January.  This is legacy behavior.  Without this,
 
2539
    // clear(MONTH) has no effect, since the internally set JULIAN_DAY
 
2540
    // is used.
 
2541
    if (fStamp[UCAL_JULIAN_DAY] >= (int32_t)kMinimumUserStamp) {
 
2542
        int32_t bestStamp = newestStamp(UCAL_ERA, UCAL_DAY_OF_WEEK_IN_MONTH, kUnset);
 
2543
        bestStamp = newestStamp(UCAL_YEAR_WOY, UCAL_EXTENDED_YEAR, bestStamp);
 
2544
        if (bestStamp <= fStamp[UCAL_JULIAN_DAY]) {
 
2545
            return internalGet(UCAL_JULIAN_DAY);
 
2546
        }
 
2547
    }
 
2548
 
 
2549
    UCalendarDateFields bestField = resolveFields(getFieldResolutionTable());
 
2550
    if (bestField == UCAL_FIELD_COUNT) {
 
2551
        bestField = UCAL_DAY_OF_MONTH;
 
2552
    }
 
2553
 
 
2554
    return handleComputeJulianDay(bestField);
 
2555
}
 
2556
 
 
2557
// -------------------------------------------
 
2558
 
 
2559
int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField)  {
 
2560
    UBool useMonth = (bestField == UCAL_DAY_OF_MONTH ||
 
2561
        bestField == UCAL_WEEK_OF_MONTH ||
 
2562
        bestField == UCAL_DAY_OF_WEEK_IN_MONTH);
 
2563
    int32_t year;
 
2564
 
 
2565
    if (bestField == UCAL_WEEK_OF_YEAR) {
 
2566
        year = internalGet(UCAL_YEAR_WOY, handleGetExtendedYear());
 
2567
        internalSet(UCAL_EXTENDED_YEAR, year);
 
2568
    } else {
 
2569
        year = handleGetExtendedYear();
 
2570
        internalSet(UCAL_EXTENDED_YEAR, year);
 
2571
    }
 
2572
 
 
2573
#if defined (U_DEBUG_CAL) 
 
2574
    fprintf(stderr, "%s:%d: bestField= %s - y=%d\n", __FILE__, __LINE__, fldName(bestField), year);
 
2575
#endif 
 
2576
 
 
2577
    // Get the Julian day of the day BEFORE the start of this year.
 
2578
    // If useMonth is true, get the day before the start of the month.
 
2579
 
 
2580
    // give calendar subclass a chance to have a default 'first' month
 
2581
    int32_t month;
 
2582
 
 
2583
    if(isSet(UCAL_MONTH)) {
 
2584
        month = internalGet(UCAL_MONTH);
 
2585
    } else {
 
2586
        month = getDefaultMonthInYear(year);
 
2587
    }
 
2588
 
 
2589
    int32_t julianDay = handleComputeMonthStart(year, useMonth ? month : 0, useMonth);
 
2590
 
 
2591
    if (bestField == UCAL_DAY_OF_MONTH) {
 
2592
 
 
2593
        // give calendar subclass a chance to have a default 'first' dom
 
2594
        int32_t dayOfMonth;
 
2595
        if(isSet(UCAL_DAY_OF_MONTH)) {
 
2596
            dayOfMonth = internalGet(UCAL_DAY_OF_MONTH,1);
 
2597
        } else {
 
2598
            dayOfMonth = getDefaultDayInMonth(year, month);
 
2599
        }
 
2600
        return julianDay + dayOfMonth;
 
2601
    }
 
2602
 
 
2603
    if (bestField == UCAL_DAY_OF_YEAR) {
 
2604
        return julianDay + internalGet(UCAL_DAY_OF_YEAR);
 
2605
    }
 
2606
 
 
2607
    int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw
 
2608
 
 
2609
    // At this point julianDay is the 0-based day BEFORE the first day of
 
2610
    // January 1, year 1 of the given calendar.  If julianDay == 0, it
 
2611
    // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
 
2612
    // or Gregorian). (or it is before the month we are in, if useMonth is True)
 
2613
 
 
2614
    // At this point we need to process the WEEK_OF_MONTH or
 
2615
    // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
 
2616
    // First, perform initial shared computations.  These locate the
 
2617
    // first week of the period.
 
2618
 
 
2619
    // Get the 0-based localized DOW of day one of the month or year.
 
2620
    // Valid range 0..6.
 
2621
    int32_t first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek;
 
2622
    if (first < 0) {
 
2623
        first += 7;
 
2624
    }
 
2625
 
 
2626
    int32_t dowLocal = getLocalDOW();
 
2627
 
 
2628
    // Find the first target DOW (dowLocal) in the month or year.
 
2629
    // Actually, it may be just before the first of the month or year.
 
2630
    // It will be an integer from -5..7.
 
2631
    int32_t date = 1 - first + dowLocal;
 
2632
 
 
2633
    if (bestField == UCAL_DAY_OF_WEEK_IN_MONTH) {
 
2634
        // Adjust the target DOW to be in the month or year.
 
2635
        if (date < 1) {
 
2636
            date += 7;
 
2637
        }
 
2638
 
 
2639
        // The only trickiness occurs if the day-of-week-in-month is
 
2640
        // negative.
 
2641
        int32_t dim = internalGet(UCAL_DAY_OF_WEEK_IN_MONTH, 1);
 
2642
        if (dim >= 0) {
 
2643
            date += 7*(dim - 1);
 
2644
 
 
2645
        } else {
 
2646
            // Move date to the last of this day-of-week in this month,
 
2647
            // then back up as needed.  If dim==-1, we don't back up at
 
2648
            // all.  If dim==-2, we back up once, etc.  Don't back up
 
2649
            // past the first of the given day-of-week in this month.
 
2650
            // Note that we handle -2, -3, etc. correctly, even though
 
2651
            // values < -1 are technically disallowed.
 
2652
            int32_t m = internalGet(UCAL_MONTH, UCAL_JANUARY);
 
2653
            int32_t monthLength = handleGetMonthLength(year, m);
 
2654
            date += ((monthLength - date) / 7 + dim + 1) * 7;
 
2655
        }
 
2656
    } else {
 
2657
#if defined (U_DEBUG_CAL) 
 
2658
        fprintf(stderr, "%s:%d - bf= %s\n", __FILE__, __LINE__, fldName(bestField));
 
2659
#endif 
 
2660
 
 
2661
        if(bestField == UCAL_WEEK_OF_YEAR) {  // ------------------------------------- WOY -------------
 
2662
            if(!isSet(UCAL_YEAR_WOY) ||  // YWOY not set at all or
 
2663
                ( (resolveFields(kYearPrecedence) != UCAL_YEAR_WOY) // YWOY doesn't have precedence
 
2664
                && (fStamp[UCAL_YEAR_WOY]!=kInternallySet) ) ) // (excluding where all fields are internally set - then YWOY is used)
 
2665
            {
 
2666
                // need to be sure to stay in 'real' year.
 
2667
                int32_t woy = internalGet(bestField);
 
2668
 
 
2669
                int32_t nextJulianDay = handleComputeMonthStart(year+1, 0, FALSE); // jd of day before jan 1
 
2670
                int32_t nextFirst = julianDayToDayOfWeek(nextJulianDay + 1) - firstDayOfWeek; 
 
2671
 
 
2672
                if (nextFirst < 0) { // 0..6 ldow of Jan 1
 
2673
                    nextFirst += 7;
 
2674
                }
 
2675
 
 
2676
                if(woy==1) {  // FIRST WEEK ---------------------------------
 
2677
#if defined (U_DEBUG_CAL) 
 
2678
                    fprintf(stderr, "%s:%d - woy=%d, yp=%d, nj(%d)=%d, nf=%d", __FILE__, __LINE__, 
 
2679
                        internalGet(bestField), resolveFields(kYearPrecedence), year+1, 
 
2680
                        nextJulianDay, nextFirst);
 
2681
 
 
2682
                    fprintf(stderr, " next: %d DFW,  min=%d   \n", (7-nextFirst), getMinimalDaysInFirstWeek() );
 
2683
#endif 
 
2684
 
 
2685
                    // nextFirst is now the localized DOW of Jan 1  of y-woy+1
 
2686
                    if((nextFirst > 0) &&   // Jan 1 starts on FDOW
 
2687
                        (7-nextFirst) >= getMinimalDaysInFirstWeek()) // or enough days in the week
 
2688
                    {
 
2689
                        // Jan 1 of (yearWoy+1) is in yearWoy+1 - recalculate JD to next year
 
2690
#if defined (U_DEBUG_CAL) 
 
2691
                        fprintf(stderr, "%s:%d - was going to move JD from %d to %d [d%d]\n", __FILE__, __LINE__, 
 
2692
                            julianDay, nextJulianDay, (nextJulianDay-julianDay));
 
2693
#endif 
 
2694
                        julianDay = nextJulianDay;
 
2695
 
 
2696
                        // recalculate 'first' [0-based local dow of jan 1]
 
2697
                        first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek;
 
2698
                        if (first < 0) {
 
2699
                            first += 7;
 
2700
                        }
 
2701
                        // recalculate date.
 
2702
                        date = 1 - first + dowLocal;
 
2703
                    }
 
2704
                } else if(woy>=getLeastMaximum(bestField)) {          
 
2705
                    // could be in the last week- find out if this JD would overstep
 
2706
                    int32_t testDate = date;
 
2707
                    if ((7 - first) < getMinimalDaysInFirstWeek()) {
 
2708
                        testDate += 7;
 
2709
                    }
 
2710
 
 
2711
                    // Now adjust for the week number.
 
2712
                    testDate += 7 * (woy - 1);
 
2713
 
 
2714
#if defined (U_DEBUG_CAL) 
 
2715
                    fprintf(stderr, "%s:%d - y=%d, y-1=%d doy%d, njd%d (C.F. %d)\n",
 
2716
                        __FILE__, __LINE__, year, year-1, testDate, julianDay+testDate, nextJulianDay);
 
2717
#endif
 
2718
                    if(julianDay+testDate > nextJulianDay) { // is it past Dec 31?  (nextJulianDay is day BEFORE year+1's  Jan 1)
 
2719
                        // Fire up the calculating engines.. retry YWOY = (year-1)
 
2720
                        julianDay = handleComputeMonthStart(year-1, 0, FALSE); // jd before Jan 1 of previous year
 
2721
                        first = julianDayToDayOfWeek(julianDay + 1) - firstDayOfWeek; // 0 based local dow   of first week
 
2722
 
 
2723
                        if(first < 0) { // 0..6
 
2724
                            first += 7;
 
2725
                        }
 
2726
                        date = 1 - first + dowLocal;
 
2727
 
 
2728
#if defined (U_DEBUG_CAL) 
 
2729
                        fprintf(stderr, "%s:%d - date now %d, jd%d, ywoy%d\n",
 
2730
                            __FILE__, __LINE__, date, julianDay, year-1);
 
2731
#endif
 
2732
 
 
2733
 
 
2734
                    } /* correction needed */
 
2735
                } /* leastmaximum */
 
2736
            } /* resolvefields(year) != year_woy */
 
2737
        } /* bestfield != week_of_year */
 
2738
 
 
2739
        // assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR)
 
2740
        // Adjust for minimal days in first week
 
2741
        if ((7 - first) < getMinimalDaysInFirstWeek()) {
 
2742
            date += 7;
 
2743
        }
 
2744
 
 
2745
        // Now adjust for the week number.
 
2746
        date += 7 * (internalGet(bestField) - 1);
 
2747
    }
 
2748
 
 
2749
    return julianDay + date;
 
2750
}
 
2751
 
 
2752
int32_t
 
2753
Calendar::getDefaultMonthInYear(int32_t /*eyear*/) 
 
2754
{
 
2755
    return 0;
 
2756
}
 
2757
 
 
2758
int32_t
 
2759
Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/) 
 
2760
{
 
2761
    return 1;
 
2762
}
 
2763
 
 
2764
 
 
2765
int32_t Calendar::getLocalDOW()
 
2766
{
 
2767
  // Get zero-based localized DOW, valid range 0..6.  This is the DOW
 
2768
    // we are looking for.
 
2769
    int32_t dowLocal = 0;
 
2770
    switch (resolveFields(kDOWPrecedence)) {
 
2771
    case UCAL_DAY_OF_WEEK:
 
2772
        dowLocal = internalGet(UCAL_DAY_OF_WEEK) - fFirstDayOfWeek;
 
2773
        break;
 
2774
    case UCAL_DOW_LOCAL:
 
2775
        dowLocal = internalGet(UCAL_DOW_LOCAL) - 1;
 
2776
        break;
 
2777
    default:
 
2778
        break;
 
2779
    }
 
2780
    dowLocal = dowLocal % 7;
 
2781
    if (dowLocal < 0) {
 
2782
        dowLocal += 7;
 
2783
    }
 
2784
    return dowLocal;
 
2785
}
 
2786
 
 
2787
int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy)
 
2788
{
 
2789
    // We have UCAL_YEAR_WOY and UCAL_WEEK_OF_YEAR - from those, determine 
 
2790
    // what year we fall in, so that other code can set it properly.
 
2791
    // (code borrowed from computeWeekFields and handleComputeJulianDay)
 
2792
    //return yearWoy;
 
2793
 
 
2794
    // First, we need a reliable DOW.
 
2795
    UCalendarDateFields bestField = resolveFields(kDatePrecedence); // !! Note: if subclasses have a different table, they should override handleGetExtendedYearFromWeekFields 
 
2796
 
 
2797
    // Now, a local DOW
 
2798
    int32_t dowLocal = getLocalDOW(); // 0..6
 
2799
    int32_t firstDayOfWeek = getFirstDayOfWeek(); // Localized fdw
 
2800
    int32_t jan1Start = handleComputeMonthStart(yearWoy, 0, FALSE);
 
2801
    int32_t nextJan1Start = handleComputeMonthStart(yearWoy+1, 0, FALSE); // next year's Jan1 start
 
2802
 
 
2803
    // At this point julianDay is the 0-based day BEFORE the first day of
 
2804
    // January 1, year 1 of the given calendar.  If julianDay == 0, it
 
2805
    // specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
 
2806
    // or Gregorian). (or it is before the month we are in, if useMonth is True)
 
2807
 
 
2808
    // At this point we need to process the WEEK_OF_MONTH or
 
2809
    // WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
 
2810
    // First, perform initial shared computations.  These locate the
 
2811
    // first week of the period.
 
2812
 
 
2813
    // Get the 0-based localized DOW of day one of the month or year.
 
2814
    // Valid range 0..6.
 
2815
    int32_t first = julianDayToDayOfWeek(jan1Start + 1) - firstDayOfWeek;
 
2816
    if (first < 0) {
 
2817
        first += 7;
 
2818
    }
 
2819
    int32_t nextFirst = julianDayToDayOfWeek(nextJan1Start + 1) - firstDayOfWeek;
 
2820
    if (nextFirst < 0) {
 
2821
        nextFirst += 7;
 
2822
    }
 
2823
 
 
2824
    int32_t minDays = getMinimalDaysInFirstWeek();
 
2825
    UBool jan1InPrevYear = FALSE;  // January 1st in the year of WOY is the 1st week?  (i.e. first week is < minimal )
 
2826
    //UBool nextJan1InPrevYear = FALSE; // January 1st of Year of WOY + 1 is in the first week? 
 
2827
 
 
2828
    if((7 - first) < minDays) { 
 
2829
        jan1InPrevYear = TRUE;
 
2830
    }
 
2831
 
 
2832
    //   if((7 - nextFirst) < minDays) {
 
2833
    //     nextJan1InPrevYear = TRUE;
 
2834
    //   }
 
2835
 
 
2836
    switch(bestField) {
 
2837
    case UCAL_WEEK_OF_YEAR:
 
2838
        if(woy == 1) {
 
2839
            if(jan1InPrevYear == TRUE) {
 
2840
                // the first week of January is in the previous year
 
2841
                // therefore WOY1 is always solidly within yearWoy
 
2842
                return yearWoy;
 
2843
            } else {
 
2844
                // First WOY is split between two years
 
2845
                if( dowLocal < first) { // we are prior to Jan 1
 
2846
                    return yearWoy-1; // previous year
 
2847
                } else {
 
2848
                    return yearWoy; // in this year
 
2849
                }
 
2850
            }
 
2851
        } else if(woy >= getLeastMaximum(bestField)) {  
 
2852
            // we _might_ be in the last week.. 
 
2853
            int32_t jd =  // Calculate JD of our target day:
 
2854
                jan1Start +  // JD of Jan 1
 
2855
                (7-first) + //  days in the first week (Jan 1.. )
 
2856
                (woy-1)*7 + // add the weeks of the year
 
2857
                dowLocal;   // the local dow (0..6) of last week
 
2858
            if(jan1InPrevYear==FALSE) {
 
2859
                jd -= 7; // woy already includes Jan 1's week.
 
2860
            }
 
2861
 
 
2862
            if( (jd+1) >= nextJan1Start ) {
 
2863
                // we are in week 52 or 53 etc. - actual year is yearWoy+1
 
2864
                return yearWoy+1;
 
2865
            } else {
 
2866
                // still in yearWoy;
 
2867
                return yearWoy;
 
2868
            }
 
2869
        } else {
 
2870
            // we're not possibly in the last week -must be ywoy
 
2871
            return yearWoy;
 
2872
        }
 
2873
        break;
 
2874
 
 
2875
    case UCAL_DATE:
 
2876
        if((internalGet(UCAL_MONTH)==0) &&
 
2877
            (woy >= getLeastMaximum(UCAL_WEEK_OF_YEAR))) {
 
2878
                return yearWoy+1; // month 0, late woy = in the next year
 
2879
            } else if(woy==1) {
 
2880
                //if(nextJan1InPrevYear) {
 
2881
                if(internalGet(UCAL_MONTH)==0) {
 
2882
                    return yearWoy;
 
2883
                } else {
 
2884
                    return yearWoy-1;
 
2885
                }
 
2886
                //}
 
2887
            }
 
2888
 
 
2889
            //(internalGet(UCAL_DATE) <= (7-first)) /* && in minDow  */ ) {
 
2890
            //within 1st week and in this month.. 
 
2891
            //return yearWoy+1;
 
2892
            return yearWoy;
 
2893
            break;
 
2894
 
 
2895
    default: // assume the year is appropriate
 
2896
        return yearWoy;
 
2897
        break;
 
2898
    }
 
2899
 
 
2900
#if defined (U_DEBUG_CAL) 
 
2901
    fprintf(stderr, "%s:%d - forgot a return on field %s\n", __FILE__, __LINE__, fldName(bestField));
 
2902
#endif 
 
2903
 
 
2904
    return yearWoy;
 
2905
}
 
2906
 
 
2907
int32_t Calendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const
 
2908
{
 
2909
    return handleComputeMonthStart(extendedYear, month+1, TRUE) -
 
2910
        handleComputeMonthStart(extendedYear, month, TRUE);
 
2911
}
 
2912
 
 
2913
int32_t Calendar::handleGetYearLength(int32_t eyear) const  {
 
2914
    return handleComputeMonthStart(eyear+1, 0, FALSE) -
 
2915
        handleComputeMonthStart(eyear, 0, FALSE);
 
2916
}
 
2917
 
 
2918
int32_t
 
2919
Calendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const
 
2920
{
 
2921
    int32_t result;
 
2922
    switch (field) {
 
2923
    case UCAL_DATE:
 
2924
        {
 
2925
            if(U_FAILURE(status)) return 0;
 
2926
            Calendar *cal = clone();
 
2927
            if(!cal) { status = U_MEMORY_ALLOCATION_ERROR; return 0; }
 
2928
            cal->prepareGetActual(field,FALSE,status);
 
2929
            result = handleGetMonthLength(cal->get(UCAL_EXTENDED_YEAR, status), cal->get(UCAL_MONTH, status));
 
2930
            delete cal;
 
2931
        }
 
2932
        break;
 
2933
 
 
2934
    case UCAL_DAY_OF_YEAR:
 
2935
        {
 
2936
            if(U_FAILURE(status)) return 0;
 
2937
            Calendar *cal = clone();
 
2938
            if(!cal) { status = U_MEMORY_ALLOCATION_ERROR; return 0; }
 
2939
            cal->prepareGetActual(field,FALSE,status);
 
2940
            result = handleGetYearLength(cal->get(UCAL_EXTENDED_YEAR, status));
 
2941
            delete cal;
 
2942
        }
 
2943
        break;
 
2944
 
 
2945
    case UCAL_DAY_OF_WEEK:
 
2946
    case UCAL_AM_PM:
 
2947
    case UCAL_HOUR:
 
2948
    case UCAL_HOUR_OF_DAY:
 
2949
    case UCAL_MINUTE:
 
2950
    case UCAL_SECOND:
 
2951
    case UCAL_MILLISECOND:
 
2952
    case UCAL_ZONE_OFFSET:
 
2953
    case UCAL_DST_OFFSET:
 
2954
    case UCAL_DOW_LOCAL:
 
2955
    case UCAL_JULIAN_DAY:
 
2956
    case UCAL_MILLISECONDS_IN_DAY:
 
2957
        // These fields all have fixed minima/maxima
 
2958
        result = getMaximum(field);
 
2959
        break;
 
2960
 
 
2961
    default:
 
2962
        // For all other fields, do it the hard way....
 
2963
        result = getActualHelper(field, getLeastMaximum(field), getMaximum(field),status);
 
2964
        break;
 
2965
    }
 
2966
    return result;
 
2967
}
 
2968
 
 
2969
 
 
2970
/**
 
2971
* Prepare this calendar for computing the actual minimum or maximum.
 
2972
* This method modifies this calendar's fields; it is called on a
 
2973
* temporary calendar.
 
2974
*
 
2975
* <p>Rationale: The semantics of getActualXxx() is to return the
 
2976
* maximum or minimum value that the given field can take, taking into
 
2977
* account other relevant fields.  In general these other fields are
 
2978
* larger fields.  For example, when computing the actual maximum
 
2979
* DATE, the current value of DATE itself is ignored,
 
2980
* as is the value of any field smaller.
 
2981
*
 
2982
* <p>The time fields all have fixed minima and maxima, so we don't
 
2983
* need to worry about them.  This also lets us set the
 
2984
* MILLISECONDS_IN_DAY to zero to erase any effects the time fields
 
2985
* might have when computing date fields.
 
2986
*
 
2987
* <p>DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and
 
2988
* WEEK_OF_YEAR fields to ensure that they are computed correctly.
 
2989
* @internal
 
2990
*/
 
2991
void Calendar::prepareGetActual(UCalendarDateFields field, UBool isMinimum, UErrorCode &status)
 
2992
{
 
2993
    set(UCAL_MILLISECONDS_IN_DAY, 0);
 
2994
 
 
2995
    switch (field) {
 
2996
    case UCAL_YEAR:
 
2997
    case UCAL_EXTENDED_YEAR:
 
2998
        set(UCAL_DAY_OF_YEAR, getGreatestMinimum(UCAL_DAY_OF_YEAR));
 
2999
        break;
 
3000
 
 
3001
    case UCAL_YEAR_WOY:
 
3002
        set(UCAL_WEEK_OF_YEAR, getGreatestMinimum(UCAL_WEEK_OF_YEAR));
 
3003
 
 
3004
    case UCAL_MONTH:
 
3005
        set(UCAL_DATE, getGreatestMinimum(UCAL_DATE));
 
3006
        break;
 
3007
 
 
3008
    case UCAL_DAY_OF_WEEK_IN_MONTH:
 
3009
        // For dowim, the maximum occurs for the DOW of the first of the
 
3010
        // month.
 
3011
        set(UCAL_DATE, 1);
 
3012
        set(UCAL_DAY_OF_WEEK, get(UCAL_DAY_OF_WEEK, status)); // Make this user set
 
3013
        break;
 
3014
 
 
3015
    case UCAL_WEEK_OF_MONTH:
 
3016
    case UCAL_WEEK_OF_YEAR:
 
3017
        // If we're counting weeks, set the day of the week to either the
 
3018
        // first or last localized DOW.  We know the last week of a month
 
3019
        // or year will contain the first day of the week, and that the
 
3020
        // first week will contain the last DOW.
 
3021
        {
 
3022
            int32_t dow = fFirstDayOfWeek;
 
3023
            if (isMinimum) {
 
3024
                dow = (dow + 6) % 7; // set to last DOW
 
3025
                if (dow < UCAL_SUNDAY) {
 
3026
                    dow += 7;
 
3027
                }
 
3028
            }
 
3029
#if defined (U_DEBUG_CAL) 
 
3030
            fprintf(stderr, "prepareGetActualHelper(WOM/WOY) - dow=%d\n", dow);
 
3031
#endif
 
3032
            set(UCAL_DAY_OF_WEEK, dow);
 
3033
        }
 
3034
        break;
 
3035
    default:
 
3036
        break;
 
3037
    }
 
3038
 
 
3039
    // Do this last to give it the newest time stamp
 
3040
    set(field, getGreatestMinimum(field));
 
3041
}
 
3042
 
 
3043
int32_t Calendar::getActualHelper(UCalendarDateFields field, int32_t startValue, int32_t endValue, UErrorCode &status) const
 
3044
{
 
3045
#if defined (U_DEBUG_CAL) 
 
3046
    fprintf(stderr, "getActualHelper(%d,%d .. %d, %s)\n", field, startValue, endValue, u_errorName(status));
 
3047
#endif
 
3048
    if (startValue == endValue) {
 
3049
        // if we know that the maximum value is always the same, just return it
 
3050
        return startValue;
 
3051
    }
 
3052
 
 
3053
    int32_t delta = (endValue > startValue) ? 1 : -1;
 
3054
 
 
3055
    // clone the calendar so we don't mess with the real one, and set it to
 
3056
    // accept anything for the field values
 
3057
    if(U_FAILURE(status)) return startValue;
 
3058
    Calendar *work = clone();
 
3059
    if(!work) { status = U_MEMORY_ALLOCATION_ERROR; return startValue; }
 
3060
    work->setLenient(TRUE);
 
3061
    work->prepareGetActual(field, delta < 0, status);
 
3062
 
 
3063
    // now try each value from the start to the end one by one until
 
3064
    // we get a value that normalizes to another value.  The last value that
 
3065
    // normalizes to itself is the actual maximum for the current date
 
3066
    work->set(field, startValue);
 
3067
 
 
3068
    // prepareGetActual sets the first day of week in the same week with
 
3069
    // the first day of a month.  Unlike WEEK_OF_YEAR, week number for the
 
3070
    // week which contains days from both previous and current month is
 
3071
    // not unique.  For example, last several days in the previous month
 
3072
    // is week 5, and the rest of week is week 1.
 
3073
    int32_t result = startValue;
 
3074
    if (work->get(field, status) != startValue
 
3075
        && field != UCAL_WEEK_OF_MONTH && delta > 0 || U_FAILURE(status)) {
 
3076
#if defined (U_DEBUG_CAL) 
 
3077
        fprintf(stderr, "getActualHelper(fld %d) - got  %d (not %d) - %s\n", field, work->get(field,status), startValue, u_errorName(status));
 
3078
#endif
 
3079
    } else {
 
3080
        do {
 
3081
            startValue += delta;
 
3082
            work->add(field, delta, status);
 
3083
            if (work->get(field, status) != startValue || U_FAILURE(status)) {
 
3084
#if defined (U_DEBUG_CAL)
 
3085
                fprintf(stderr, "getActualHelper(fld %d) - got  %d (not %d), BREAK - %s\n", field, work->get(field,status), startValue, u_errorName(status));
 
3086
#endif
 
3087
                break;
 
3088
            }
 
3089
            result = startValue;
 
3090
        } while (startValue != endValue);
 
3091
    }
 
3092
    delete work;
 
3093
#if defined (U_DEBUG_CAL) 
 
3094
    fprintf(stderr, "getActualHelper(%d) = %d\n", field, result);
 
3095
#endif
 
3096
    return result;
 
3097
}
 
3098
 
 
3099
 
 
3100
 
 
3101
 
 
3102
// -------------------------------------
 
3103
 
 
3104
void
 
3105
Calendar::setWeekCountData(const Locale& desiredLocale, const char *type, UErrorCode& status)
 
3106
{
 
3107
    // Read the week count data from the resource bundle.  This should
 
3108
    // have the form:
 
3109
    //
 
3110
    //   DateTimeElements:intvector {
 
3111
    //      1,    // first day of week
 
3112
    //      1     // min days in week
 
3113
    //   }
 
3114
    //   Both have a range of 1..7
 
3115
 
 
3116
 
 
3117
    if (U_FAILURE(status)) return;
 
3118
 
 
3119
    fFirstDayOfWeek = UCAL_SUNDAY;
 
3120
    fMinimalDaysInFirstWeek = 1;
 
3121
 
 
3122
    CalendarData calData(desiredLocale, type, status);
 
3123
    // If the resource data doesn't seem to be present at all, then use last-resort
 
3124
    // hard-coded data.
 
3125
    UResourceBundle *dateTimeElements = calData.getByKey(gDateTimeElements, status);
 
3126
 
 
3127
    if (U_FAILURE(status))
 
3128
    {
 
3129
#if defined (U_DEBUG_CALDATA)
 
3130
        fprintf(stderr, " Failure loading dateTimeElements = %s\n", u_errorName(status));
 
3131
#endif
 
3132
        status = U_USING_FALLBACK_WARNING;
 
3133
        return;
 
3134
    }
 
3135
 
 
3136
    U_LOCALE_BASED(locBased, *this);
 
3137
    locBased.setLocaleIDs(ures_getLocaleByType(dateTimeElements, ULOC_VALID_LOCALE, &status),
 
3138
        ures_getLocaleByType(dateTimeElements, ULOC_ACTUAL_LOCALE, &status));
 
3139
    if (U_SUCCESS(status)) {
 
3140
#if defined (U_DEBUG_CAL)
 
3141
        fprintf(stderr, " Valid=%s, Actual=%s\n", validLocale, actualLocale);
 
3142
#endif
 
3143
        int32_t arrLen;
 
3144
        const int32_t *dateTimeElementsArr = ures_getIntVector(dateTimeElements, &arrLen, &status);
 
3145
 
 
3146
        if(U_SUCCESS(status) && arrLen == 2
 
3147
            && 1 <= dateTimeElementsArr[0] && dateTimeElementsArr[0] <= 7
 
3148
            && 1 <= dateTimeElementsArr[1] && dateTimeElementsArr[1] <= 7)
 
3149
        {
 
3150
            fFirstDayOfWeek = (UCalendarDaysOfWeek)dateTimeElementsArr[0];
 
3151
            fMinimalDaysInFirstWeek = (uint8_t)dateTimeElementsArr[1];
 
3152
        }
 
3153
        else {
 
3154
            status = U_INVALID_FORMAT_ERROR;
 
3155
        }
 
3156
    }
 
3157
 
 
3158
    // do NOT close dateTimeElements
 
3159
}
 
3160
 
 
3161
/**
 
3162
* Recompute the time and update the status fields isTimeSet
 
3163
* and areFieldsSet.  Callers should check isTimeSet and only
 
3164
* call this method if isTimeSet is false.
 
3165
*/
 
3166
void 
 
3167
Calendar::updateTime(UErrorCode& status) 
 
3168
{
 
3169
    computeTime(status);
 
3170
    if(U_FAILURE(status))
 
3171
        return;
 
3172
 
 
3173
    // If we are lenient, we need to recompute the fields to normalize
 
3174
    // the values.  Also, if we haven't set all the fields yet (i.e.,
 
3175
    // in a newly-created object), we need to fill in the fields. [LIU]
 
3176
    if (isLenient() || ! fAreAllFieldsSet) 
 
3177
        fAreFieldsSet = FALSE;
 
3178
 
 
3179
    fIsTimeSet = TRUE;
 
3180
    fAreFieldsVirtuallySet = FALSE;
 
3181
}
 
3182
 
 
3183
Locale 
 
3184
Calendar::getLocale(ULocDataLocaleType type, UErrorCode& status) const {
 
3185
    U_LOCALE_BASED(locBased, *this);
 
3186
    return locBased.getLocale(type, status);
 
3187
}
 
3188
 
 
3189
const char *
 
3190
Calendar::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const {
 
3191
    U_LOCALE_BASED(locBased, *this);
 
3192
    return locBased.getLocaleID(type, status);
 
3193
}
 
3194
 
 
3195
// Deprecated function. This doesn't need to be inline.
 
3196
void
 
3197
Calendar::internalSet(EDateFields field, int32_t value)
 
3198
{
 
3199
    internalSet((UCalendarDateFields) field, value);
 
3200
}
 
3201
 
 
3202
U_NAMESPACE_END
 
3203
 
 
3204
#endif /* #if !UCONFIG_NO_FORMATTING */
 
3205
 
 
3206
 
 
3207
//eof