// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* * Copyright (C) 1997-2016, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* * * File CALENDAR.CPP * * Modification History: * * Date Name Description * 02/03/97 clhuang Creation. * 04/22/97 aliu Cleaned up, fixed memory leak, made * setWeekCountData() more robust. * Moved platform code to TPlatformUtilities. * 05/01/97 aliu Made equals(), before(), after() arguments const. * 05/20/97 aliu Changed logic of when to compute fields and time * to fix bugs. * 08/12/97 aliu Added equivalentTo. Misc other fixes. * 07/28/98 stephen Sync up with JDK 1.2 * 09/02/98 stephen Sync with JDK 1.2 8/31 build (getActualMin/Max) * 03/17/99 stephen Changed adoptTimeZone() - now fAreFieldsSet is * set to false to force update of time. ******************************************************************************* */ #include "utypeinfo.h" // for 'typeid' to work #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING #include "unicode/gregocal.h" #include "unicode/basictz.h" #include "unicode/simpletz.h" #include "unicode/rbtz.h" #include "unicode/vtzone.h" #include "gregoimp.h" #include "buddhcal.h" #include "taiwncal.h" #include "japancal.h" #include "islamcal.h" #include "hebrwcal.h" #include "persncal.h" #include "indiancal.h" #include "iso8601cal.h" #include "chnsecal.h" #include "coptccal.h" #include "dangical.h" #include "ethpccal.h" #include "unicode/calendar.h" #include "cpputils.h" #include "servloc.h" #include "ucln_in.h" #include "cstring.h" #include "locbased.h" #include "uresimp.h" #include "ustrenum.h" #include "uassert.h" #include "olsontz.h" #include "sharedcalendar.h" #include "unifiedcache.h" #include "ulocimp.h" #include "bytesinkutil.h" #include "charstr.h" #if !UCONFIG_NO_SERVICE static icu::ICULocaleService* gService = …; static icu::UInitOnce gServiceInitOnce { … }; // INTERNAL - for cleanup U_CDECL_BEGIN static UBool calendar_cleanup() { … } U_CDECL_END #endif // ------------------------------------------ // // Registration // //------------------------------------------- //#define U_DEBUG_CALSVC 1 // #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) /** * fldName was removed as a duplicate implementation. * use udbg_ services instead, * which depend on include files and library from ../tools/toolutil, the following circular link: * CPPFLAGS+=-I$(top_srcdir)/tools/toolutil * LIBS+=$(LIBICUTOOLUTIL) */ #include "udbgutil.h" #include <stdio.h> /** * convert a UCalendarDateFields into a string - for debugging * @param f field enum * @return static string to the field name * @internal */ const char* fldName(UCalendarDateFields f) { return udbg_enumName(UDBG_UCalendarDateFields, (int32_t)f); } #if UCAL_DEBUG_DUMP // from CalendarTest::calToStr - but doesn't modify contents. void ucal_dump(const Calendar &cal) { cal.dump(); } void Calendar::dump() const { int i; fprintf(stderr, "@calendar=%s, timeset=%c, fieldset=%c, allfields=%c, virtualset=%c, t=%.2f", getType(), fIsTimeSet?'y':'n', fAreFieldsSet?'y':'n', fAreAllFieldsSet?'y':'n', fAreFieldsVirtuallySet?'y':'n', fTime); // can add more things here: DST, zone, etc. fprintf(stderr, "\n"); for(i = 0;i<UCAL_FIELD_COUNT;i++) { int n; const char *f = fldName((UCalendarDateFields)i); fprintf(stderr, " %25s: %-11ld", f, fFields[i]); if(fStamp[i] == kUnset) { fprintf(stderr, " (unset) "); } else if(fStamp[i] == kInternallySet) { fprintf(stderr, " (internally set) "); //} else if(fStamp[i] == kInternalDefault) { // fprintf(stderr, " (internal default) "); } else { fprintf(stderr, " %%%d ", fStamp[i]); } fprintf(stderr, "\n"); } } U_CFUNC void ucal_dump(UCalendar* cal) { ucal_dump( *((Calendar*)cal) ); } #endif #endif /* Max value for stamp allowable before recalculation */ #define STAMP_MAX … static const char * const gCalTypes[] = …; // Must be in the order of gCalTypes above ECalType; U_NAMESPACE_BEGIN SharedCalendar::~SharedCalendar() { … } template<> U_I18N_API const SharedCalendar *LocaleCacheKey<SharedCalendar>::createObject( const void * /*unusedCreationContext*/, UErrorCode &status) const { … } static ECalType getCalendarType(const char *s) { … } #if !UCONFIG_NO_SERVICE // Only used with service registration. static UBool isStandardSupportedKeyword(const char *keyword, UErrorCode& status) { … } #endif static ECalType getCalendarTypeForLocale(const char *locid) { … } static Calendar *createStandardCalendar(ECalType calType, const Locale &loc, UErrorCode& status) { … } #if !UCONFIG_NO_SERVICE // ------------------------------------- /** * a Calendar Factory which creates the "basic" calendar types, that is, those * shipped with ICU. */ class BasicCalendarFactory : public LocaleKeyFactory { … }; BasicCalendarFactory::~BasicCalendarFactory() { … } /** * A factory which looks up the DefaultCalendar resource to determine which class of calendar to use */ class DefaultCalendarFactory : public ICUResourceBundleFactory { … }; DefaultCalendarFactory::~DefaultCalendarFactory() { … } // ------------------------------------- class CalendarService : public ICULocaleService { … }; CalendarService::~CalendarService() { … } // ------------------------------------- static inline UBool isCalendarServiceUsed() { … } // ------------------------------------- static void U_CALLCONV initCalendarService(UErrorCode &status) { … } static ICULocaleService* getCalendarService(UErrorCode &status) { … } URegistryKey Calendar::registerFactory(ICUServiceFactory* toAdopt, UErrorCode& status) { … } UBool Calendar::unregister(URegistryKey key, UErrorCode& status) { … } #endif /* UCONFIG_NO_SERVICE */ // ------------------------------------- static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = …; // Resource bundle tags read by this class static const char gCalendar[] = …; static const char gMonthNames[] = …; static const char gGregorian[] = …; // Data flow in Calendar // --------------------- // The current time is represented in two ways by Calendar: as UTC // milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local // fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the // millis from the fields, and vice versa. The data needed to do this // conversion is encapsulated by a TimeZone object owned by the Calendar. // The data provided by the TimeZone object may also be overridden if the // user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class // keeps track of what information was most recently set by the caller, and // uses that to compute any other information as needed. // If the user sets the fields using set(), the data flow is as follows. // This is implemented by the Calendar subclass's computeTime() method. // During this process, certain fields may be ignored. The disambiguation // algorithm for resolving which fields to pay attention to is described // above. // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.) // | // | Using Calendar-specific algorithm // V // local standard millis // | // | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET // V // UTC millis (in time data member) // If the user sets the UTC millis using setTime(), the data flow is as // follows. This is implemented by the Calendar subclass's computeFields() // method. // UTC millis (in time data member) // | // | Using TimeZone getOffset() // V // local standard millis // | // | Using Calendar-specific algorithm // V // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.) // In general, a round trip from fields, through local and UTC millis, and // back out to fields is made when necessary. This is implemented by the // complete() method. Resolving a partial set of fields into a UTC millis // value allows all remaining fields to be generated from that value. If // the Calendar is lenient, the fields are also renormalized to standard // ranges when they are regenerated. // ------------------------------------- Calendar::Calendar(UErrorCode& success) : … { … } // ------------------------------------- Calendar::Calendar(TimeZone* zone, const Locale& aLocale, UErrorCode& success) : … { … } // ------------------------------------- Calendar::Calendar(const TimeZone& zone, const Locale& aLocale, UErrorCode& success) : … { … } // ------------------------------------- Calendar::~Calendar() { … } // ------------------------------------- Calendar::Calendar(const Calendar &source) : … { … } // ------------------------------------- Calendar & Calendar::operator=(const Calendar &right) { … } // ------------------------------------- Calendar* U_EXPORT2 Calendar::createInstance(UErrorCode& success) { … } // ------------------------------------- Calendar* U_EXPORT2 Calendar::createInstance(const TimeZone& zone, UErrorCode& success) { … } // ------------------------------------- Calendar* U_EXPORT2 Calendar::createInstance(const Locale& aLocale, UErrorCode& success) { … } // ------------------------------------- Adopting // Note: this is the bottleneck that actually calls the service routines. Calendar * U_EXPORT2 Calendar::makeInstance(const Locale& aLocale, UErrorCode& success) { … } Calendar* U_EXPORT2 Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success) { … } // ------------------------------------- Calendar* U_EXPORT2 Calendar::createInstance(const TimeZone& zone, const Locale& aLocale, UErrorCode& success) { … } // ------------------------------------- void U_EXPORT2 Calendar::getCalendarTypeFromLocale( const Locale &aLocale, char *typeBuffer, int32_t typeBufferSize, UErrorCode &success) { … } bool Calendar::operator==(const Calendar& that) const { … } UBool Calendar::isEquivalentTo(const Calendar& other) const { … } // ------------------------------------- UBool Calendar::equals(const Calendar& when, UErrorCode& status) const { … } // ------------------------------------- UBool Calendar::before(const Calendar& when, UErrorCode& status) const { … } // ------------------------------------- UBool Calendar::after(const Calendar& when, UErrorCode& status) const { … } // ------------------------------------- const Locale* U_EXPORT2 Calendar::getAvailableLocales(int32_t& count) { … } // ------------------------------------- StringEnumeration* U_EXPORT2 Calendar::getKeywordValuesForLocale(const char* key, const Locale& locale, UBool commonlyUsed, UErrorCode& status) { … } // ------------------------------------- UDate U_EXPORT2 Calendar::getNow() { … } // ------------------------------------- /** * Gets this Calendar's current time as a long. * @return the current time as UTC milliseconds from the epoch. */ double Calendar::getTimeInMillis(UErrorCode& status) const { … } // ------------------------------------- /** * Sets this Calendar's current time from the given long value. * A status of U_ILLEGAL_ARGUMENT_ERROR is set when millis is * outside the range permitted by a Calendar object when not in lenient mode. * when in lenient mode the out of range values are pinned to their respective min/max. * @param date the new time in UTC milliseconds from the epoch. */ void Calendar::setTimeInMillis( double millis, UErrorCode& status ) { … } // ------------------------------------- int32_t Calendar::get(UCalendarDateFields field, UErrorCode& status) const { … } // ------------------------------------- void Calendar::set(UCalendarDateFields field, int32_t value) { … } // ------------------------------------- void Calendar::set(int32_t year, int32_t month, int32_t date) { … } // ------------------------------------- void Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute) { … } // ------------------------------------- void Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute, int32_t second) { … } // ------------------------------------- int32_t Calendar::getRelatedYear(UErrorCode &status) const { … } // ------------------------------------- void Calendar::setRelatedYear(int32_t year) { … } // ------------------------------------- void Calendar::clear() { … } // ------------------------------------- void Calendar::clear(UCalendarDateFields field) { … } // ------------------------------------- UBool Calendar::isSet(UCalendarDateFields field) const { … } int32_t Calendar::newestStamp(UCalendarDateFields first, UCalendarDateFields last, int32_t bestStampSoFar) const { … } // ------------------------------------- void Calendar::complete(UErrorCode& status) { … } //------------------------------------------------------------------------- // Protected utility methods for use by subclasses. These are very handy // for implementing add, roll, and computeFields. //------------------------------------------------------------------------- /** * Adjust the specified field so that it is within * the allowable range for the date to which this calendar is set. * For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH} * field for a calendar set to April 31 would cause it to be set * to April 30. * <p> * <b>Subclassing:</b> * <br> * This utility method is intended for use by subclasses that need to implement * their own overrides of {@link #roll roll} and {@link #add add}. * <p> * <b>Note:</b> * <code>pinField</code> is implemented in terms of * {@link #getActualMinimum getActualMinimum} * and {@link #getActualMaximum getActualMaximum}. If either of those methods uses * a slow, iterative algorithm for a particular field, it would be * unwise to attempt to call <code>pinField</code> for that field. If you * really do need to do so, you should override this method to do * something more efficient for that field. * <p> * @param field The calendar field whose value should be pinned. * * @see #getActualMinimum * @see #getActualMaximum * @stable ICU 2.0 */ void Calendar::pinField(UCalendarDateFields field, UErrorCode& status) { … } void Calendar::computeFields(UErrorCode &ec) { … } uint8_t Calendar::julianDayToDayOfWeek(double julian) { … } /** * Compute the Gregorian calendar year, month, and day of month from * the given Julian day. These values are not stored in fields, but in * member variables gregorianXxx. Also compute the DAY_OF_WEEK and * DOW_LOCAL fields. */ void Calendar::computeGregorianAndDOWFields(int32_t julianDay, UErrorCode &ec) { … } /** * Compute the Gregorian calendar year, month, and day of month from the * Julian day. These values are not stored in fields, but in member * variables gregorianXxx. They are used for time zone computations and by * subclasses that are Gregorian derivatives. Subclasses may call this * method to perform a Gregorian calendar millis->fields computation. */ void Calendar::computeGregorianFields(int32_t julianDay, UErrorCode& ec) { … } /** * Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH, * DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR, * DAY_OF_WEEK, and DAY_OF_YEAR. The latter fields are computed by the * subclass based on the calendar system. * * <p>The YEAR_WOY field is computed simplistically. It is equal to YEAR * most of the time, but at the year boundary it may be adjusted to YEAR-1 * or YEAR+1 to reflect the overlap of a week into an adjacent year. In * this case, a simple increment or decrement is performed on YEAR, even * though this may yield an invalid YEAR value. For instance, if the YEAR * is part of a calendar system with an N-year cycle field CYCLE, then * incrementing the YEAR may involve incrementing CYCLE and setting YEAR * back to 0 or 1. This is not handled by this code, and in fact cannot be * simply handled without having subclasses define an entire parallel set of * fields for fields larger than or equal to a year. This additional * complexity is not warranted, since the intention of the YEAR_WOY field is * to support ISO 8601 notation, so it will typically be used with a * proleptic Gregorian calendar, which has no field larger than a year. */ void Calendar::computeWeekFields(UErrorCode &ec) { … } int32_t Calendar::weekNumber(int32_t desiredDay, int32_t dayOfPeriod, int32_t dayOfWeek) { … } void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode& status) { … } // ------------------------------------- void Calendar::roll(EDateFields field, int32_t amount, UErrorCode& status) { … } void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) UPRV_NO_SANITIZE_UNDEFINED { … } void Calendar::add(EDateFields field, int32_t amount, UErrorCode& status) { … } // ------------------------------------- void Calendar::add(UCalendarDateFields field, int32_t amount, UErrorCode& status) { … } // ------------------------------------- int32_t Calendar::fieldDifference(UDate when, EDateFields field, UErrorCode& status) { … } int32_t Calendar::fieldDifference(UDate targetMs, UCalendarDateFields field, UErrorCode& ec) { … } // ------------------------------------- void Calendar::adoptTimeZone(TimeZone* zone) { … } // ------------------------------------- void Calendar::setTimeZone(const TimeZone& zone) { … } // ------------------------------------- const TimeZone& Calendar::getTimeZone() const { … } // ------------------------------------- TimeZone* Calendar::orphanTimeZone() { … } // ------------------------------------- void Calendar::setLenient(UBool lenient) { … } // ------------------------------------- UBool Calendar::isLenient() const { … } // ------------------------------------- void Calendar::setRepeatedWallTimeOption(UCalendarWallTimeOption option) { … } // ------------------------------------- UCalendarWallTimeOption Calendar::getRepeatedWallTimeOption() const { … } // ------------------------------------- void Calendar::setSkippedWallTimeOption(UCalendarWallTimeOption option) { … } // ------------------------------------- UCalendarWallTimeOption Calendar::getSkippedWallTimeOption() const { … } // ------------------------------------- void Calendar::setFirstDayOfWeek(UCalendarDaysOfWeek value) UPRV_NO_SANITIZE_UNDEFINED { … } // ------------------------------------- Calendar::EDaysOfWeek Calendar::getFirstDayOfWeek() const { … } UCalendarDaysOfWeek Calendar::getFirstDayOfWeek(UErrorCode & /*status*/) const { … } // ------------------------------------- void Calendar::setMinimalDaysInFirstWeek(uint8_t value) { … } // ------------------------------------- uint8_t Calendar::getMinimalDaysInFirstWeek() const { … } // ------------------------------------- // weekend functions, just dummy implementations for now (for API freeze) UCalendarWeekdayType Calendar::getDayOfWeekType(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const { … } int32_t Calendar::getWeekendTransition(UCalendarDaysOfWeek dayOfWeek, UErrorCode &status) const { … } UBool Calendar::isWeekend(UDate date, UErrorCode &status) const { … } UBool Calendar::isWeekend() const { … } // ------------------------------------- limits int32_t Calendar::getMinimum(EDateFields field) const { … } int32_t Calendar::getMinimum(UCalendarDateFields field) const { … } // ------------------------------------- int32_t Calendar::getMaximum(EDateFields field) const { … } int32_t Calendar::getMaximum(UCalendarDateFields field) const { … } // ------------------------------------- int32_t Calendar::getGreatestMinimum(EDateFields field) const { … } int32_t Calendar::getGreatestMinimum(UCalendarDateFields field) const { … } // ------------------------------------- int32_t Calendar::getLeastMaximum(EDateFields field) const { … } int32_t Calendar::getLeastMaximum(UCalendarDateFields field) const { … } // ------------------------------------- int32_t Calendar::getActualMinimum(EDateFields field, UErrorCode& status) const { … } int32_t Calendar::getLimit(UCalendarDateFields field, ELimitType limitType) const { … } int32_t Calendar::getActualMinimum(UCalendarDateFields field, UErrorCode& status) const { … } // ------------------------------------- UBool Calendar::inDaylightTime(UErrorCode& status) const { … } bool Calendar::inTemporalLeapYear(UErrorCode& status) const { … } // ------------------------------------- static const char * const gTemporalMonthCodes[] = …; const char* Calendar::getTemporalMonthCode(UErrorCode& status) const { … } void Calendar::setTemporalMonthCode(const char* code, UErrorCode& status ) { … } // ------------------------------------- /** * Ensure that each field is within its valid range by calling {@link * #validateField(int)} on each field that has been set. This method * should only be called if this calendar is not lenient. * @see #isLenient * @see #validateField(int) */ void Calendar::validateFields(UErrorCode &status) { … } /** * Validate a single field of this calendar. Subclasses should * override this method to validate any calendar-specific fields. * Generic fields can be handled by * <code>Calendar.validateField()</code>. * @see #validateField(int, int, int) */ void Calendar::validateField(UCalendarDateFields field, UErrorCode &status) { … } /** * Validate a single field of this calendar given its minimum and * maximum allowed value. If the field is out of range, throw a * descriptive <code>IllegalArgumentException</code>. Subclasses may * use this method in their implementation of {@link * #validateField(int)}. */ void Calendar::validateField(UCalendarDateFields field, int32_t min, int32_t max, UErrorCode& status) { … } // ------------------------- const UFieldResolutionTable* Calendar::getFieldResolutionTable() const { … } UCalendarDateFields Calendar::newerField(UCalendarDateFields defaultField, UCalendarDateFields alternateField) const { … } UCalendarDateFields Calendar::resolveFields(const UFieldResolutionTable* precedenceTable) const { … } const UFieldResolutionTable Calendar::kDatePrecedence[] = …; const UFieldResolutionTable Calendar::kMonthPrecedence[] = …; const UFieldResolutionTable Calendar::kDOWPrecedence[] = …; // precedence for calculating a year const UFieldResolutionTable Calendar::kYearPrecedence[] = …; // ------------------------- void Calendar::computeTime(UErrorCode& status) { … } /** * Find the previous zone transition near the given time. */ UBool Calendar::getImmediatePreviousZoneTransition(UDate base, UDate *transitionTime, UErrorCode& status) const { … } /** * Compute the milliseconds in the day from the fields. This is a * value from 0 to 23:59:59.999 inclusive, unless fields are out of * range, in which case it can be an arbitrary value. This value * reflects local zone wall time. * @stable ICU 2.0 */ double Calendar::computeMillisInDay() { … } /** * This method can assume EXTENDED_YEAR has been set. * @param millis milliseconds of the date fields * @param millisInDay milliseconds of the time fields; may be out * or range. * @stable ICU 2.0 */ int32_t Calendar::computeZoneOffset(double millis, double millisInDay, UErrorCode &ec) { … } int32_t Calendar::computeJulianDay() { … } // ------------------------------------------- int32_t Calendar::handleComputeJulianDay(UCalendarDateFields bestField) { … } int32_t Calendar::getDefaultMonthInYear(int32_t /*eyear*/) { … } int32_t Calendar::getDefaultDayInMonth(int32_t /*eyear*/, int32_t /*month*/) { … } int32_t Calendar::getLocalDOW() { … } int32_t Calendar::handleGetExtendedYearFromWeekFields(int32_t yearWoy, int32_t woy) { … } int32_t Calendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { … } int32_t Calendar::handleGetYearLength(int32_t eyear) const { … } int32_t Calendar::getActualMaximum(UCalendarDateFields field, UErrorCode& status) const { … } /** * Prepare this calendar for computing the actual minimum or maximum. * This method modifies this calendar's fields; it is called on a * temporary calendar. * * <p>Rationale: The semantics of getActualXxx() is to return the * maximum or minimum value that the given field can take, taking into * account other relevant fields. In general these other fields are * larger fields. For example, when computing the actual maximum * DATE, the current value of DATE itself is ignored, * as is the value of any field smaller. * * <p>The time fields all have fixed minima and maxima, so we don't * need to worry about them. This also lets us set the * MILLISECONDS_IN_DAY to zero to erase any effects the time fields * might have when computing date fields. * * <p>DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and * WEEK_OF_YEAR fields to ensure that they are computed correctly. * @internal */ void Calendar::prepareGetActual(UCalendarDateFields field, UBool isMinimum, UErrorCode &status) { … } int32_t Calendar::getActualHelper(UCalendarDateFields field, int32_t startValue, int32_t endValue, UErrorCode &status) const { … } // ------------------------------------- void Calendar::setWeekData(const Locale& desiredLocale, const char *type, UErrorCode& status) { … } /** * Recompute the time and update the status fields isTimeSet * and areFieldsSet. Callers should check isTimeSet and only * call this method if isTimeSet is false. */ void Calendar::updateTime(UErrorCode& status) { … } Locale Calendar::getLocale(ULocDataLocaleType type, UErrorCode& status) const { … } const char * Calendar::getLocaleID(ULocDataLocaleType type, UErrorCode& status) const { … } void Calendar::recalculateStamp() { … } // Deprecated function. This doesn't need to be inline. void Calendar::internalSet(EDateFields field, int32_t value) { … } int32_t Calendar::internalGetMonth() const { … } int32_t Calendar::internalGetMonth(int32_t defaultValue) const { … } BasicTimeZone* Calendar::getBasicTimeZone() const { … } U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ //eof