Bug Summary

File:root/firefox-clang/intl/icu/source/i18n/calendar.cpp
Warning:line 962, column 19
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name calendar.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -resource-dir /usr/lib/llvm-21/lib/clang/21 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -D DEBUG=1 -D U_I18N_IMPLEMENTATION -D _LIBCPP_DISABLE_DEPRECATION_WARNINGS -D U_USING_ICU_NAMESPACE=0 -D U_NO_DEFAULT_INCLUDE_UTF_HEADERS=1 -D U_HIDE_OBSOLETE_UTF_OLD_H=1 -D UCONFIG_NO_LEGACY_CONVERSION -D UCONFIG_NO_TRANSLITERATION -D UCONFIG_NO_REGULAR_EXPRESSIONS -D UCONFIG_NO_BREAK_ITERATION -D UCONFIG_NO_IDNA -D UCONFIG_NO_MF2 -D U_CHARSET_IS_UTF8 -D UNISTR_FROM_CHAR_EXPLICIT=explicit -D UNISTR_FROM_STRING_EXPLICIT=explicit -D U_ENABLE_DYLOAD=0 -D U_DEBUG=1 -I /root/firefox-clang/config/external/icu/i18n -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -I /root/firefox-clang/intl/icu/source/common -I /root/firefox-clang/mfbt/double-conversion -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-comma -Wno-implicit-const-int-float-conversion -Wno-macro-redefined -Wno-microsoft-include -Wno-tautological-unsigned-enum-zero-compare -Wno-unreachable-code-loop-increment -Wno-unreachable-code-return -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-06-27-100320-3286336-1 -x c++ /root/firefox-clang/intl/icu/source/i18n/calendar.cpp

/root/firefox-clang/intl/icu/source/i18n/calendar.cpp

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

/root/firefox-clang/intl/icu/source/common/unifiedcache.h

1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4******************************************************************************
5* Copyright (C) 2015, International Business Machines Corporation and
6* others. All Rights Reserved.
7******************************************************************************
8*
9* File UNIFIEDCACHE.H - The ICU Unified cache.
10******************************************************************************
11*/
12
13#ifndef __UNIFIED_CACHE_H__
14#define __UNIFIED_CACHE_H__
15
16#include "utypeinfo.h" // for 'typeid' to work
17
18#include "unicode/uobject.h"
19#include "unicode/locid.h"
20#include "sharedobject.h"
21#include "unicode/unistr.h"
22#include "cstring.h"
23#include "ustr_imp.h"
24
25struct UHashtable;
26struct UHashElement;
27
28U_NAMESPACE_BEGINnamespace icu_77 {
29
30class UnifiedCache;
31
32/**
33 * A base class for all cache keys.
34 */
35class U_COMMON_API CacheKeyBase : public UObject {
36 public:
37 CacheKeyBase() : fCreationStatus(U_ZERO_ERROR), fIsPrimary(false) {}
38
39 /**
40 * Copy constructor. Needed to support cloning.
41 */
42 CacheKeyBase(const CacheKeyBase &other)
43 : UObject(other), fCreationStatus(other.fCreationStatus), fIsPrimary(false) { }
44 virtual ~CacheKeyBase();
45
46 /**
47 * Returns the hash code for this object.
48 */
49 virtual int32_t hashCode() const = 0;
50
51 /**
52 * Clones this object polymorphically. Caller owns returned value.
53 */
54 virtual CacheKeyBase *clone() const = 0;
55
56 /**
57 * Create a new object for this key. Called by cache on cache miss.
58 * createObject must add a reference to the object it returns. Note
59 * that getting an object from the cache and returning it without calling
60 * removeRef on it satisfies this requirement. It can also return nullptr
61 * and set status to an error.
62 *
63 * @param creationContext the context in which the object is being
64 * created. May be nullptr.
65 * @param status Implementations can return a failure here.
66 * In addition, implementations may return a
67 * non nullptr object and set a warning status.
68 */
69 virtual const SharedObject *createObject(
70 const void *creationContext, UErrorCode &status) const = 0;
71
72 /**
73 * Writes a description of this key to buffer and returns buffer. Written
74 * description is nullptr terminated.
75 */
76 virtual char *writeDescription(char *buffer, int32_t bufSize) const = 0;
77
78 friend inline bool operator==(const CacheKeyBase& lhs,
79 const CacheKeyBase& rhs) {
80 return lhs.equals(rhs);
81 }
82
83 friend inline bool operator!=(const CacheKeyBase& lhs,
84 const CacheKeyBase& rhs) {
85 return !lhs.equals(rhs);
86 }
87
88 protected:
89 virtual bool equals(const CacheKeyBase& other) const = 0;
90
91 private:
92 mutable UErrorCode fCreationStatus;
93 mutable UBool fIsPrimary;
94 friend class UnifiedCache;
95};
96
97
98
99/**
100 * Templated version of CacheKeyBase.
101 * A key of type LocaleCacheKey<T> maps to a value of type T.
102 */
103template<typename T>
104class CacheKey : public CacheKeyBase {
105 public:
106 virtual ~CacheKey() { }
107 /**
108 * The template parameter, T, determines the hash code returned.
109 */
110 virtual int32_t hashCode() const override {
111 const char *s = typeid(T).name();
112 return ustr_hashCharsNustr_hashCharsN_77(s, static_cast<int32_t>(uprv_strlen(s):: strlen(s)));
113 }
114
115 /**
116 * Use the value type, T, as the description.
117 */
118 virtual char *writeDescription(char *buffer, int32_t bufLen) const override {
119 const char *s = typeid(T).name();
120 uprv_strncpy(buffer, s, bufLen):: strncpy(buffer, s, bufLen);
121 buffer[bufLen - 1] = 0;
122 return buffer;
123 }
124
125 protected:
126 /**
127 * Two objects are equal if they are of the same type.
128 */
129 virtual bool equals(const CacheKeyBase &other) const override {
130 return this == &other || typeid(*this) == typeid(other);
131 }
132};
133
134/**
135 * Cache key based on locale.
136 * A key of type LocaleCacheKey<T> maps to a value of type T.
137 */
138template<typename T>
139class LocaleCacheKey : public CacheKey<T> {
140 protected:
141 Locale fLoc;
142 virtual bool equals(const CacheKeyBase &other) const override {
143 if (!CacheKey<T>::equals(other)) {
144 return false;
145 }
146 // We know this and other are of same class because equals() on
147 // CacheKey returned true.
148 return operator==(static_cast<const LocaleCacheKey<T> &>(other));
149 }
150 public:
151 LocaleCacheKey(const Locale &loc) : fLoc(loc) {}
152 LocaleCacheKey(const LocaleCacheKey<T> &other)
153 : CacheKey<T>(other), fLoc(other.fLoc) { }
154 virtual ~LocaleCacheKey() { }
155 virtual int32_t hashCode() const override {
156 return (int32_t)(37u * (uint32_t)CacheKey<T>::hashCode() + (uint32_t)fLoc.hashCode());
157 }
158 inline bool operator == (const LocaleCacheKey<T> &other) const {
159 return fLoc == other.fLoc;
160 }
161 virtual CacheKeyBase *clone() const override {
162 return new LocaleCacheKey<T>(*this);
163 }
164 virtual const T *createObject(
165 const void *creationContext, UErrorCode &status) const override;
166 /**
167 * Use the locale id as the description.
168 */
169 virtual char *writeDescription(char *buffer, int32_t bufLen) const override {
170 const char *s = fLoc.getName();
171 uprv_strncpy(buffer, s, bufLen):: strncpy(buffer, s, bufLen);
172 buffer[bufLen - 1] = 0;
173 return buffer;
174 }
175
176};
177
178/**
179 * The unified cache. A singleton type.
180 * Design doc here:
181 * https://docs.google.com/document/d/1RwGQJs4N4tawNbf809iYDRCvXoMKqDJihxzYt1ysmd8/edit?usp=sharing
182 */
183class U_COMMON_API UnifiedCache : public UnifiedCacheBase {
184 public:
185 /**
186 * @internal
187 * Do not call directly. Instead use UnifiedCache::getInstance() as
188 * there should be only one UnifiedCache in an application.
189 */
190 UnifiedCache(UErrorCode &status);
191
192 /**
193 * Return a pointer to the global cache instance.
194 */
195 static UnifiedCache *getInstance(UErrorCode &status);
196
197 /**
198 * Fetches a value from the cache by key. Equivalent to
199 * get(key, nullptr, ptr, status);
200 */
201 template<typename T>
202 void get(
203 const CacheKey<T>& key,
204 const T *&ptr,
205 UErrorCode &status) const {
206 get(key, nullptr, ptr, status);
6
Calling 'UnifiedCache::get'
16
Returning from 'UnifiedCache::get'
207 }
17
Returning without writing to 'ptr'
208
209 /**
210 * Fetches value from the cache by key.
211 *
212 * @param key the cache key.
213 * @param creationContext passed verbatim to createObject method of key
214 * @param ptr On entry, ptr must be nullptr or be included if
215 * the reference count of the object it points
216 * to. On exit, ptr points to the fetched object
217 * from the cache or is left unchanged on
218 * failure. Caller must call removeRef on ptr
219 * if set to a non nullptr value.
220 * @param status Any error returned here. May be set to a
221 * warning value even if ptr is set.
222 */
223 template<typename T>
224 void get(
225 const CacheKey<T>& key,
226 const void *creationContext,
227 const T *&ptr,
228 UErrorCode &status) const {
229 if (U_FAILURE(status)) {
7
Taking false branch
230 return;
231 }
232 UErrorCode creationStatus = U_ZERO_ERROR;
233 const SharedObject *value = nullptr;
234 _get(key, value, creationContext, creationStatus);
235 const T *tvalue = (const T *) value;
236 if (U_SUCCESS(creationStatus)) {
8
Taking true branch
237 SharedObject::copyPtr(tvalue, ptr);
9
Calling 'SharedObject::copyPtr'
13
Returning from 'SharedObject::copyPtr'
238 }
239 SharedObject::clearPtr(tvalue);
240 // Take care not to overwrite a warning status passed in with
241 // another warning or U_ZERO_ERROR.
242 if (status == U_ZERO_ERROR || U_FAILURE(creationStatus)) {
14
Assuming 'status' is equal to U_ZERO_ERROR
243 status = creationStatus;
244 }
245 }
15
Returning without writing to 'ptr'
246
247#ifdef UNIFIED_CACHE_DEBUG
248 /**
249 * Dumps the contents of this cache to standard error. Used for testing of
250 * cache only.
251 */
252 void dumpContents() const;
253#endif
254
255 /**
256 * Convenience method to get a value of type T from cache for a
257 * particular locale with creationContext == nullptr.
258 * @param loc the locale
259 * @param ptr On entry, must be nullptr or included in the ref count
260 * of the object to which it points.
261 * On exit, fetched value stored here or is left
262 * unchanged on failure. Caller must call removeRef on
263 * ptr if set to a non nullptr value.
264 * @param status Any error returned here. May be set to a
265 * warning value even if ptr is set.
266 */
267 template<typename T>
268 static void getByLocale(
269 const Locale &loc, const T *&ptr, UErrorCode &status) {
270 const UnifiedCache *cache = getInstance(status);
271 if (U_FAILURE(status)) {
4
Taking false branch
272 return;
273 }
274 cache->get(LocaleCacheKey<T>(loc), ptr, status);
5
Calling 'UnifiedCache::get'
18
Returning from 'UnifiedCache::get'
275 }
19
Returning without writing to 'ptr'
276
277#ifdef UNIFIED_CACHE_DEBUG
278 /**
279 * Dumps the cache contents to stderr. For testing only.
280 */
281 static void dump();
282#endif
283
284 /**
285 * Returns the number of keys in this cache. For testing only.
286 */
287 int32_t keyCount() const;
288
289 /**
290 * Removes any values from cache that are not referenced outside
291 * the cache.
292 */
293 void flush() const;
294
295 /**
296 * Configures at what point eviction of unused entries will begin.
297 * Eviction is triggered whenever the number of evictable keys exceeds
298 * BOTH count AND (number of in-use items) * (percentageOfInUseItems / 100).
299 * Once the number of unused entries drops below one of these,
300 * eviction ceases. Because eviction happens incrementally,
301 * the actual unused entry count may exceed both these numbers
302 * from time to time.
303 *
304 * A cache entry is defined as unused if it is not essential to guarantee
305 * that for a given key X, the cache returns the same reference to the
306 * same value as long as the client already holds a reference to that
307 * value.
308 *
309 * If this method is never called, the default settings are 1000 and 100%.
310 *
311 * Although this method is thread-safe, it is designed to be called at
312 * application startup. If it is called in the middle of execution, it
313 * will have no immediate effect on the cache. However over time, the
314 * cache will perform eviction slices in an attempt to honor the new
315 * settings.
316 *
317 * If a client already holds references to many different unique values
318 * in the cache such that the number of those unique values far exceeds
319 * "count" then the cache may not be able to maintain this maximum.
320 * However, if this happens, the cache still guarantees that the number of
321 * unused entries will remain only a small percentage of the total cache
322 * size.
323 *
324 * If the parameters passed are negative, setEvctionPolicy sets status to
325 * U_ILLEGAL_ARGUMENT_ERROR.
326 */
327 void setEvictionPolicy(
328 int32_t count, int32_t percentageOfInUseItems, UErrorCode &status);
329
330
331 /**
332 * Returns how many entries have been auto evicted during the lifetime
333 * of this cache. This only includes auto evicted entries, not
334 * entries evicted because of a call to flush().
335 */
336 int64_t autoEvictedCount() const;
337
338 /**
339 * Returns the unused entry count in this cache. For testing only,
340 * Regular clients will not need this.
341 */
342 int32_t unusedCount() const;
343
344 virtual void handleUnreferencedObject() const override;
345 virtual ~UnifiedCache();
346
347 private:
348 UHashtable *fHashtable;
349 mutable int32_t fEvictPos;
350 mutable int32_t fNumValuesTotal;
351 mutable int32_t fNumValuesInUse;
352 int32_t fMaxUnused;
353 int32_t fMaxPercentageOfInUse;
354 mutable int64_t fAutoEvictedCount;
355 SharedObject *fNoValue;
356
357 UnifiedCache(const UnifiedCache &other) = delete;
358 UnifiedCache &operator=(const UnifiedCache &other) = delete;
359
360 /**
361 * Flushes the contents of the cache. If cache values hold references to other
362 * cache values then _flush should be called in a loop until it returns false.
363 *
364 * On entry, gCacheMutex must be held.
365 * On exit, those values with are evictable are flushed.
366 *
367 * @param all if false flush evictable items only, which are those with no external
368 * references, plus those that can be safely recreated.<br>
369 * if true, flush all elements. Any values (sharedObjects) with remaining
370 * hard (external) references are not deleted, but are detached from
371 * the cache, so that a subsequent removeRefs can delete them.
372 * _flush is not thread safe when all is true.
373 * @return true if any value in cache was flushed or false otherwise.
374 */
375 UBool _flush(UBool all) const;
376
377 /**
378 * Gets value out of cache.
379 * On entry. gCacheMutex must not be held. value must be nullptr. status
380 * must be U_ZERO_ERROR.
381 * On exit. value and status set to what is in cache at key or on cache
382 * miss the key's createObject() is called and value and status are set to
383 * the result of that. In this latter case, best effort is made to add the
384 * value and status to the cache. If createObject() fails to create a value,
385 * fNoValue is stored in cache, and value is set to nullptr. Caller must call
386 * removeRef on value if non nullptr.
387 */
388 void _get(
389 const CacheKeyBase &key,
390 const SharedObject *&value,
391 const void *creationContext,
392 UErrorCode &status) const;
393
394 /**
395 * Attempts to fetch value and status for key from cache.
396 * On entry, gCacheMutex must not be held value must be nullptr and status must
397 * be U_ZERO_ERROR.
398 * On exit, either returns false (In this
399 * case caller should try to create the object) or returns true with value
400 * pointing to the fetched value and status set to fetched status. When
401 * false is returned status may be set to failure if an in progress hash
402 * entry could not be made but value will remain unchanged. When true is
403 * returned, caller must call removeRef() on value.
404 */
405 UBool _poll(
406 const CacheKeyBase &key,
407 const SharedObject *&value,
408 UErrorCode &status) const;
409
410 /**
411 * Places a new value and creationStatus in the cache for the given key.
412 * On entry, gCacheMutex must be held. key must not exist in the cache.
413 * On exit, value and creation status placed under key. Soft reference added
414 * to value on successful add. On error sets status.
415 */
416 void _putNew(
417 const CacheKeyBase &key,
418 const SharedObject *value,
419 const UErrorCode creationStatus,
420 UErrorCode &status) const;
421
422 /**
423 * Places value and status at key if there is no value at key or if cache
424 * entry for key is in progress. Otherwise, it leaves the current value and
425 * status there.
426 *
427 * On entry. gCacheMutex must not be held. Value must be
428 * included in the reference count of the object to which it points.
429 *
430 * On exit, value and status are changed to what was already in the cache if
431 * something was there and not in progress. Otherwise, value and status are left
432 * unchanged in which case they are placed in the cache on a best-effort basis.
433 * Caller must call removeRef() on value.
434 */
435 void _putIfAbsentAndGet(
436 const CacheKeyBase &key,
437 const SharedObject *&value,
438 UErrorCode &status) const;
439
440 /**
441 * Returns the next element in the cache round robin style.
442 * Returns nullptr if the cache is empty.
443 * On entry, gCacheMutex must be held.
444 */
445 const UHashElement *_nextElement() const;
446
447 /**
448 * Return the number of cache items that would need to be evicted
449 * to bring usage into conformance with eviction policy.
450 *
451 * An item corresponds to an entry in the hash table, a hash table element.
452 *
453 * On entry, gCacheMutex must be held.
454 */
455 int32_t _computeCountOfItemsToEvict() const;
456
457 /**
458 * Run an eviction slice.
459 * On entry, gCacheMutex must be held.
460 * _runEvictionSlice runs a slice of the evict pipeline by examining the next
461 * 10 entries in the cache round robin style evicting them if they are eligible.
462 */
463 void _runEvictionSlice() const;
464
465 /**
466 * Register a primary cache entry. A primary key is the first key to create
467 * a given SharedObject value. Subsequent keys whose create function
468 * produce references to an already existing SharedObject are not primary -
469 * they can be evicted and subsequently recreated.
470 *
471 * On entry, gCacheMutex must be held.
472 * On exit, items in use count incremented, entry is marked as a primary
473 * entry, and value registered with cache so that subsequent calls to
474 * addRef() and removeRef() on it correctly interact with the cache.
475 */
476 void _registerPrimary(const CacheKeyBase *theKey, const SharedObject *value) const;
477
478 /**
479 * Store a value and creation error status in given hash entry.
480 * On entry, gCacheMutex must be held. Hash entry element must be in progress.
481 * value must be non nullptr.
482 * On Exit, soft reference added to value. value and status stored in hash
483 * entry. Soft reference removed from previous stored value. Waiting
484 * threads notified.
485 */
486 void _put(
487 const UHashElement *element,
488 const SharedObject *value,
489 const UErrorCode status) const;
490 /**
491 * Remove a soft reference, and delete the SharedObject if no references remain.
492 * To be used from within the UnifiedCache implementation only.
493 * gCacheMutex must be held by caller.
494 * @param value the SharedObject to be acted on.
495 */
496 void removeSoftRef(const SharedObject *value) const;
497
498 /**
499 * Increment the hard reference count of the given SharedObject.
500 * gCacheMutex must be held by the caller.
501 * Update numValuesEvictable on transitions between zero and one reference.
502 *
503 * @param value The SharedObject to be referenced.
504 * @return the hard reference count after the addition.
505 */
506 int32_t addHardRef(const SharedObject *value) const;
507
508 /**
509 * Decrement the hard reference count of the given SharedObject.
510 * gCacheMutex must be held by the caller.
511 * Update numValuesEvictable on transitions between one and zero reference.
512 *
513 * @param value The SharedObject to be referenced.
514 * @return the hard reference count after the removal.
515 */
516 int32_t removeHardRef(const SharedObject *value) const;
517
518
519#ifdef UNIFIED_CACHE_DEBUG
520 void _dumpContents() const;
521#endif
522
523 /**
524 * Fetch value and error code from a particular hash entry.
525 * On entry, gCacheMutex must be held. value must be either nullptr or must be
526 * included in the ref count of the object to which it points.
527 * On exit, value and status set to what is in the hash entry. Caller must
528 * eventually call removeRef on value.
529 * If hash entry is in progress, value will be set to gNoValue and status will
530 * be set to U_ZERO_ERROR.
531 */
532 void _fetch(const UHashElement *element, const SharedObject *&value,
533 UErrorCode &status) const;
534
535 /**
536 * Determine if given hash entry is in progress.
537 * On entry, gCacheMutex must be held.
538 */
539 UBool _inProgress(const UHashElement *element) const;
540
541 /**
542 * Determine if given hash entry is in progress.
543 * On entry, gCacheMutex must be held.
544 */
545 UBool _inProgress(const SharedObject *theValue, UErrorCode creationStatus) const;
546
547 /**
548 * Determine if given hash entry is eligible for eviction.
549 * On entry, gCacheMutex must be held.
550 */
551 UBool _isEvictable(const UHashElement *element) const;
552};
553
554U_NAMESPACE_END}
555
556#endif

/root/firefox-clang/intl/icu/source/common/sharedobject.h

1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4******************************************************************************
5* Copyright (C) 2015-2016, International Business Machines
6* Corporation and others. All Rights Reserved.
7******************************************************************************
8* sharedobject.h
9*/
10
11#ifndef __SHAREDOBJECT_H__
12#define __SHAREDOBJECT_H__
13
14
15#include "unicode/uobject.h"
16#include "umutex.h"
17
18U_NAMESPACE_BEGINnamespace icu_77 {
19
20class SharedObject;
21
22/**
23 * Base class for unified cache exposing enough methods to SharedObject
24 * instances to allow their addRef() and removeRef() methods to
25 * update cache metrics. No other part of ICU, except for SharedObject,
26 * should directly call the methods of this base class.
27 */
28class U_COMMON_API UnifiedCacheBase : public UObject {
29public:
30 UnifiedCacheBase() { }
31
32 /**
33 * Notify the cache implementation that an object was seen transitioning to
34 * zero hard references. The cache may use this to keep track the number of
35 * unreferenced SharedObjects, and to trigger evictions.
36 */
37 virtual void handleUnreferencedObject() const = 0;
38
39 virtual ~UnifiedCacheBase();
40private:
41 UnifiedCacheBase(const UnifiedCacheBase &) = delete;
42 UnifiedCacheBase &operator=(const UnifiedCacheBase &) = delete;
43};
44
45/**
46 * Base class for shared, reference-counted, auto-deleted objects.
47 * Subclasses can be immutable.
48 * If they are mutable, then they must implement their copy constructor
49 * so that copyOnWrite() works.
50 *
51 * Either stack-allocate, use LocalPointer, or use addRef()/removeRef().
52 * Sharing requires reference-counting.
53 */
54class U_COMMON_API SharedObject : public UObject {
55public:
56 /** Initializes totalRefCount, softRefCount to 0. */
57 SharedObject() :
58 softRefCount(0),
59 hardRefCount(0),
60 cachePtr(nullptr) {}
61
62 /** Initializes totalRefCount, softRefCount to 0. */
63 SharedObject(const SharedObject &other) :
64 UObject(other),
65 softRefCount(0),
66 hardRefCount(0),
67 cachePtr(nullptr) {}
68
69 virtual ~SharedObject();
70
71 /**
72 * Increments the number of hard references to this object. Thread-safe.
73 * Not for use from within the Unified Cache implementation.
74 */
75 void addRef() const;
76
77 /**
78 * Decrements the number of hard references to this object, and
79 * arrange for possible cache-eviction and/or deletion if ref
80 * count goes to zero. Thread-safe.
81 *
82 * Not for use from within the UnifiedCache implementation.
83 */
84 void removeRef() const;
85
86 /**
87 * Returns the number of hard references for this object.
88 * Uses a memory barrier.
89 */
90 int32_t getRefCount() const;
91
92 /**
93 * If noHardReferences() == true then this object has no hard references.
94 * Must be called only from within the internals of UnifiedCache.
95 */
96 inline UBool noHardReferences() const { return getRefCount() == 0; }
97
98 /**
99 * If hasHardReferences() == true then this object has hard references.
100 * Must be called only from within the internals of UnifiedCache.
101 */
102 inline UBool hasHardReferences() const { return getRefCount() != 0; }
103
104 /**
105 * Deletes this object if it has no references.
106 * Available for non-cached SharedObjects only. Ownership of cached objects
107 * is with the UnifiedCache, which is solely responsible for eviction and deletion.
108 */
109 void deleteIfZeroRefCount() const;
110
111
112 /**
113 * Returns a writable version of ptr.
114 * If there is exactly one owner, then ptr itself is returned as a
115 * non-const pointer.
116 * If there are multiple owners, then ptr is replaced with a
117 * copy-constructed clone,
118 * and that is returned.
119 * Returns nullptr if cloning failed.
120 *
121 * T must be a subclass of SharedObject.
122 */
123 template<typename T>
124 static T *copyOnWrite(const T *&ptr) {
125 const T *p = ptr;
126 if(p->getRefCount() <= 1) { return const_cast<T *>(p); }
127 T *p2 = new T(*p);
128 if(p2 == nullptr) { return nullptr; }
129 p->removeRef();
130 ptr = p2;
131 p2->addRef();
132 return p2;
133 }
134
135 /**
136 * Makes dest an owner of the object pointed to by src while adjusting
137 * reference counts and deleting the previous object dest pointed to
138 * if necessary. Before this call is made, dest must either be nullptr or
139 * be included in the reference count of the object it points to.
140 *
141 * T must be a subclass of SharedObject.
142 */
143 template<typename T>
144 static void copyPtr(const T *src, const T *&dest) {
145 if(src != dest) {
10
Assuming 'src' is equal to 'dest'
11
Taking false branch
146 if(dest != nullptr) { dest->removeRef(); }
147 dest = src;
148 if(src != nullptr) { src->addRef(); }
149 }
150 }
12
Returning without writing to 'dest'
151
152 /**
153 * Equivalent to copyPtr(nullptr, dest).
154 */
155 template<typename T>
156 static void clearPtr(const T *&ptr) {
157 if (ptr != nullptr) {
158 ptr->removeRef();
159 ptr = nullptr;
160 }
161 }
162
163private:
164 /**
165 * The number of references from the UnifiedCache, which is
166 * the number of times that the sharedObject is stored as a hash table value.
167 * For use by UnifiedCache implementation code only.
168 * All access is synchronized by UnifiedCache's gCacheMutex
169 */
170 mutable int32_t softRefCount;
171 friend class UnifiedCache;
172
173 /**
174 * Reference count, excluding references from within the UnifiedCache implementation.
175 */
176 mutable u_atomic_int32_t hardRefCount;
177
178 mutable const UnifiedCacheBase *cachePtr;
179
180};
181
182U_NAMESPACE_END}
183
184#endif