Bug Summary

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