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