Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/intl/icu/source/i18n/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 146 kB image not shown  

Quelle  calendar.cpp   Sprache: C

 
// © 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 "charstr.h"

#if !UCONFIG_NO_SERVICE
static icu::ICULocaleService* gService = nullptr;
static icu::UInitOnce gServiceInitOnce {};

// INTERNAL - for cleanup
U_CDECL_BEGIN
static UBool calendar_cleanup() {
#if !UCONFIG_NO_SERVICE
    if (gService) {
        delete gService;
        gService = nullptr;
    }
    gServiceInitOnce.reset();
#endif
    return true;
}
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 10000

static const char * const gCalTypes[] = {
    "gregorian",
    "japanese",
    "buddhist",
    "roc",
    "persian",
    "islamic-civil",
    "islamic",
    "hebrew",
    "chinese",
    "indian",
    "coptic",
    "ethiopic",
    "ethiopic-amete-alem",
    "iso8601",
    "dangi",
    "islamic-umalqura",
    "islamic-tbla",
    "islamic-rgsa",
    nullptr
};

// Must be in the order of gCalTypes above
typedef enum ECalType {
    CALTYPE_UNKNOWN = -1,
    CALTYPE_GREGORIAN = 0,
    CALTYPE_JAPANESE,
    CALTYPE_BUDDHIST,
    CALTYPE_ROC,
    CALTYPE_PERSIAN,
    CALTYPE_ISLAMIC_CIVIL,
    CALTYPE_ISLAMIC,
    CALTYPE_HEBREW,
    CALTYPE_CHINESE,
    CALTYPE_INDIAN,
    CALTYPE_COPTIC,
    CALTYPE_ETHIOPIC,
    CALTYPE_ETHIOPIC_AMETE_ALEM,
    CALTYPE_ISO8601,
    CALTYPE_DANGI,
    CALTYPE_ISLAMIC_UMALQURA,
    CALTYPE_ISLAMIC_TBLA,
    CALTYPE_ISLAMIC_RGSA
} ECalType;

U_NAMESPACE_BEGIN

SharedCalendar::~SharedCalendar() {
    delete ptr;
}

template<> U_I18N_API
const SharedCalendar *LocaleCacheKey<SharedCalendar>::createObject(
        const void * /*unusedCreationContext*/, UErrorCode &status) const {
    if (U_FAILURE(status)) {
       return nullptr;
    }
    Calendar *calendar = Calendar::makeInstance(fLoc, status);
    if (U_FAILURE(status)) {
        return nullptr;
    }
    SharedCalendar *shared = new SharedCalendar(calendar);
    if (shared == nullptr) {
        delete calendar;
        status = U_MEMORY_ALLOCATION_ERROR;
        return nullptr;
    }
    shared->addRef();
    return shared;
}

static ECalType getCalendarType(const char *s) {
    for (int i = 0; gCalTypes[i] != nullptr; i++) {
        if (uprv_stricmp(s, gCalTypes[i]) == 0) {
            return static_cast<ECalType>(i);
        }
    }
    return CALTYPE_UNKNOWN;
}

#if !UCONFIG_NO_SERVICE
// Only used with service registration.
static UBool isStandardSupportedKeyword(const char *keyword, UErrorCode& status) {
    if(U_FAILURE(status)) {
        return false;
    }
    ECalType calType = getCalendarType(keyword);
    return (calType != CALTYPE_UNKNOWN);
}

#endif

static ECalType getCalendarTypeForLocale(const char *locid) {
    UErrorCode status = U_ZERO_ERROR;
    ECalType calType = CALTYPE_UNKNOWN;

    // Canonicalize, so that an old-style variant will be transformed to keywords.
    // e.g ja_JP_TRADITIONAL -> ja_JP@calendar=japanese
    // NOTE: Since ICU-20187, ja_JP_TRADITIONAL no longer canonicalizes, and
    // the Gregorian calendar is returned instead.
    CharString canonicalName = ulocimp_canonicalize(locid, status);
    if (U_FAILURE(status)) {
        return CALTYPE_GREGORIAN;
    }

    CharString calTypeBuf = ulocimp_getKeywordValue(canonicalName.data(), "calendar", status);
    if (U_SUCCESS(status)) {
        calType = getCalendarType(calTypeBuf.data());
        if (calType != CALTYPE_UNKNOWN) {
            return calType;
        }
    }
    status = U_ZERO_ERROR;

    // when calendar keyword is not available or not supported, read supplementalData
    // to get the default calendar type for the locale's region
    CharString region = ulocimp_getRegionForSupplementalData(canonicalName.data(), truestatus);
    if (U_FAILURE(status)) {
        return CALTYPE_GREGORIAN;
    }

    // Read preferred calendar values from supplementalData calendarPreference
    UResourceBundle *rb = ures_openDirect(nullptr, "supplementalData", &status);
    ures_getByKey(rb, "calendarPreferenceData", rb, &status);
    UResourceBundle *order = ures_getByKey(rb, region.data(), nullptr, &status);
    if (status == U_MISSING_RESOURCE_ERROR && rb != nullptr) {
        status = U_ZERO_ERROR;
        order = ures_getByKey(rb, "001", nullptr, &status);
    }

    calTypeBuf.clear();
    if (U_SUCCESS(status) && order != nullptr) {
        // the first calendar type is the default for the region
        int32_t len = 0;
        const char16_t *uCalType = ures_getStringByIndex(order, 0, &len, &status);
        calTypeBuf.appendInvariantChars(uCalType, len, status);
        calType = getCalendarType(calTypeBuf.data());
    }

    ures_close(order);
    ures_close(rb);

    if (calType == CALTYPE_UNKNOWN) {
        // final fallback
        calType = CALTYPE_GREGORIAN;
    }
    return calType;
}

static Calendar *createStandardCalendar(ECalType calType, const Locale &loc, UErrorCode&&nbsp;status) {
    if (U_FAILURE(status)) {
        return nullptr;
    }
    LocalPointer<Calendar> cal;

    switch (calType) {
        case CALTYPE_GREGORIAN:
            cal.adoptInsteadAndCheckErrorCode(new GregorianCalendar(loc, status), status);
            break;
        case CALTYPE_JAPANESE:
            cal.adoptInsteadAndCheckErrorCode(new JapaneseCalendar(loc, status), status);
            break;
        case CALTYPE_BUDDHIST:
            cal.adoptInsteadAndCheckErrorCode(new BuddhistCalendar(loc, status), status);
            break;
        case CALTYPE_ROC:
            cal.adoptInsteadAndCheckErrorCode(new TaiwanCalendar(loc, status), status);
            break;
        case CALTYPE_PERSIAN:
            cal.adoptInsteadAndCheckErrorCode(new PersianCalendar(loc, status), status);
            break;
        case CALTYPE_ISLAMIC_TBLA:
            cal.adoptInsteadAndCheckErrorCode(new IslamicTBLACalendar(loc, status), status);
            break;
        case CALTYPE_ISLAMIC_CIVIL:
            cal.adoptInsteadAndCheckErrorCode(new IslamicCivilCalendar(loc, status), status);
            break;
        case CALTYPE_ISLAMIC_RGSA:
            cal.adoptInsteadAndCheckErrorCode(new IslamicRGSACalendar(loc, status), status);
            break;
        case CALTYPE_ISLAMIC:
            cal.adoptInsteadAndCheckErrorCode(new IslamicCalendar(loc, status), status);
            break;
        case CALTYPE_ISLAMIC_UMALQURA:
            cal.adoptInsteadAndCheckErrorCode(new IslamicUmalquraCalendar(loc, status), status);
            break;
        case CALTYPE_HEBREW:
            cal.adoptInsteadAndCheckErrorCode(new HebrewCalendar(loc, status), status);
            break;
        case CALTYPE_CHINESE:
            cal.adoptInsteadAndCheckErrorCode(new ChineseCalendar(loc, status), status);
            break;
        case CALTYPE_INDIAN:
            cal.adoptInsteadAndCheckErrorCode(new IndianCalendar(loc, status), status);
            break;
        case CALTYPE_COPTIC:
            cal.adoptInsteadAndCheckErrorCode(new CopticCalendar(loc, status), status);
            break;
        case CALTYPE_ETHIOPIC:
            cal.adoptInsteadAndCheckErrorCode(new EthiopicCalendar(loc, status), status);
            break;
        case CALTYPE_ETHIOPIC_AMETE_ALEM:
            cal.adoptInsteadAndCheckErrorCode(new EthiopicAmeteAlemCalendar(loc, status), status);
            break;
        case CALTYPE_ISO8601:
            cal.adoptInsteadAndCheckErrorCode(new ISO8601Calendar(loc, status), status);
            break;
        case CALTYPE_DANGI:
            cal.adoptInsteadAndCheckErrorCode(new DangiCalendar(loc, status), status);
            break;
        default:
            status = U_UNSUPPORTED_ERROR;
    }
    return cal.orphan();
}


#if !UCONFIG_NO_SERVICE

// -------------------------------------

/**
* a Calendar Factory which creates the "basic" calendar types, that is, those
* shipped with ICU.
*/

class BasicCalendarFactory : public LocaleKeyFactory {
public:
    /**
    * @param calendarType static const string (caller owns storage - will be aliased) to calendar type
    */

    BasicCalendarFactory()
        : LocaleKeyFactory(LocaleKeyFactory::INVISIBLE) { }

    virtual ~BasicCalendarFactory();

protected:
    //virtual UBool isSupportedID( const UnicodeString& id, UErrorCode& status) const {
    //  if(U_FAILURE(status)) {
    //    return false;
    //  }
    //  char keyword[ULOC_FULLNAME_CAPACITY];
    //  getCalendarKeyword(id, keyword, (int32_t)sizeof(keyword));
    //  return isStandardSupportedKeyword(keyword, status);
    //}

    virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const override
    {
        if (U_SUCCESS(status)) {
            for(int32_t i=0;gCalTypes[i] != nullptr;i++) {
                UnicodeString id(static_cast<char16_t>(0x40)); /* '@' a variant character */
                id.append(UNICODE_STRING_SIMPLE("calendar="));
                id.append(UnicodeString(gCalTypes[i], -1, US_INV));
                result.put(id, (void*)this, status);
            }
        }
    }

    virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const override {
        if (U_FAILURE(status)) {
           return nullptr;
        }
#ifdef U_DEBUG_CALSVC
        if(dynamic_cast<const LocaleKey*>(&key) == nullptr) {
            fprintf(stderr, "::create - not a LocaleKey!\n");
        }
#endif
        const LocaleKey* lkey = dynamic_cast<const LocaleKey*>(&key);
        U_ASSERT(lkey != nullptr);
        Locale curLoc;  // current locale
        Locale canLoc;  // Canonical locale

        lkey->currentLocale(curLoc);
        lkey->canonicalLocale(canLoc);

        char keyword[ULOC_FULLNAME_CAPACITY];
        curLoc.getKeywordValue("calendar", keyword, static_cast<int32_t>(sizeof(keyword)), status);

#ifdef U_DEBUG_CALSVC
        fprintf(stderr, "BasicCalendarFactory::create() - cur %s, can %s\n", (const char*)curLoc.getName(), (const char*)canLoc.getName());
#endif

        if(!isStandardSupportedKeyword(keyword,status)) {  // Do we handle this type?
#ifdef U_DEBUG_CALSVC

            fprintf(stderr, "BasicCalendarFactory - not handling %s.[%s]\n", (const char*) curLoc.getName(), tmp );
#endif
            return nullptr;
        }

        return createStandardCalendar(getCalendarType(keyword), canLoc, status);
    }
};

BasicCalendarFactory::~BasicCalendarFactory() {}

/**
* A factory which looks up the DefaultCalendar resource to determine which class of calendar to use
*/


class DefaultCalendarFactory : public ICUResourceBundleFactory {
public:
    DefaultCalendarFactory() : ICUResourceBundleFactory() { }
    virtual ~DefaultCalendarFactory();
protected:
    virtual UObject* create(const ICUServiceKey& key, const ICUService* /*service*/, UErrorCode& status) const override {
        if (U_FAILURE(status)) {
           return nullptr;
        }

        const LocaleKey *lkey = dynamic_cast<const LocaleKey*>(&key);
        U_ASSERT(lkey != nullptr);
        Locale loc;
        lkey->currentLocale(loc);

        UnicodeString *ret = new UnicodeString();
        if (ret == nullptr) {
            status = U_MEMORY_ALLOCATION_ERROR;
        } else {
            ret->append(static_cast<char16_t>(0x40)); // '@' is a variant character
            ret->append(UNICODE_STRING("calendar=", 9));
            ret->append(UnicodeString(gCalTypes[getCalendarTypeForLocale(loc.getName())], -1, US_INV));
        }
        return ret;
    }
};

DefaultCalendarFactory::~DefaultCalendarFactory() {}

// -------------------------------------
class CalendarService : public ICULocaleService {
public:
    CalendarService()
        : ICULocaleService(UNICODE_STRING_SIMPLE("Calendar"))
    {
        UErrorCode status = U_ZERO_ERROR;
        registerFactory(new DefaultCalendarFactory(), status);
    }

    virtual ~CalendarService();

    virtual UObject* cloneInstance(UObject* instance) const override {
        UnicodeString *s = dynamic_cast<UnicodeString *>(instance);
        if(s != nullptr) {
            return s->clone();
        } else {
#ifdef U_DEBUG_CALSVC_F
            UErrorCode status2 = U_ZERO_ERROR;
            fprintf(stderr, "Cloning a %s calendar with tz=%ld\n", ((Calendar*)instance)->getType(), ((Calendar*)instance)->get(UCAL_ZONE_OFFSET, status2));
#endif
            return ((Calendar*)instance)->clone();
        }
    }

    virtual UObject* handleDefault(const ICUServiceKey& key, UnicodeString* /*actualID*/, UErrorCode& status) const override {
        if (U_FAILURE(status)) {
           return nullptr;
        }
        LocaleKey& lkey = static_cast<LocaleKey&>(const_cast<ICUServiceKey&>(key));
        //int32_t kind = lkey.kind();

        Locale loc;
        lkey.canonicalLocale(loc);

#ifdef U_DEBUG_CALSVC
        Locale loc2;
        lkey.currentLocale(loc2);
        fprintf(stderr, "CalSvc:handleDefault for currentLoc %s, canloc %s\n", (const char*)loc.getName(),  (const char*)loc2.getName());
#endif
        Calendar *nc =  new GregorianCalendar(loc, status);
        if (nc == nullptr) {
            status = U_MEMORY_ALLOCATION_ERROR;
            return nc;
        }

#ifdef U_DEBUG_CALSVC
        UErrorCode status2 = U_ZERO_ERROR;
        fprintf(stderr, "New default calendar has tz=%d\n", ((Calendar*)nc)->get(UCAL_ZONE_OFFSET, status2));
#endif
        return nc;
    }

    virtual UBool isDefault() const override {
        return countFactories() == 1;
    }
};

CalendarService::~CalendarService() {}

// -------------------------------------

static inline UBool
isCalendarServiceUsed() {
    return !gServiceInitOnce.isReset();
}

// -------------------------------------

static void U_CALLCONV
initCalendarService(UErrorCode &status)
{
#ifdef U_DEBUG_CALSVC
        fprintf(stderr, "Spinning up Calendar Service\n");
#endif
    if (U_FAILURE(status)) {
       return;
    }
    ucln_i18n_registerCleanup(UCLN_I18N_CALENDAR, calendar_cleanup);
    gService = new CalendarService();
    if (gService == nullptr) {
            status = U_MEMORY_ALLOCATION_ERROR;
        return;
        }
#ifdef U_DEBUG_CALSVC
        fprintf(stderr, "Registering classes..\n");
#endif

        // Register all basic instances.
    gService->registerFactory(new BasicCalendarFactory(),status);

#ifdef U_DEBUG_CALSVC
        fprintf(stderr, "Done..\n");
#endif

        if(U_FAILURE(status)) {
#ifdef U_DEBUG_CALSVC
            fprintf(stderr, "err (%s) registering classes, deleting service.....\n", u_errorName(status));
#endif
        delete gService;
        gService = nullptr;
    }
        }

static ICULocaleService*
getCalendarService(UErrorCode &status)
{
    umtx_initOnce(gServiceInitOnce, &initCalendarService, status);
    return gService;
}

URegistryKey Calendar::registerFactory(ICUServiceFactory* toAdopt, UErrorCode& status)
{
    return getCalendarService(status)->registerFactory(toAdopt, status);
}

UBool Calendar::unregister(URegistryKey key, UErrorCode& status) {
    return getCalendarService(status)->unregister(key, status);
}
#endif /* UCONFIG_NO_SERVICE */

// -------------------------------------

static const int32_t kCalendarLimits[UCAL_FIELD_COUNT][4] = {
    //    Minimum  Greatest min      Least max   Greatest max
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // ERA
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // YEAR
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // MONTH
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // WEEK_OF_YEAR
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // WEEK_OF_MONTH
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // DAY_OF_MONTH
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // DAY_OF_YEAR
    {           1,            1,             7,             7  }, // DAY_OF_WEEK
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // DAY_OF_WEEK_IN_MONTH
    {           0,            0,             1,             1  }, // AM_PM
    {           0,            0,            11,            11  }, // HOUR
    {           0,            0,            23,            23  }, // HOUR_OF_DAY
    {           0,            0,            59,            59  }, // MINUTE
    {           0,            0,            59,            59  }, // SECOND
    {           0,            0,           999,           999  }, // MILLISECOND
    {-24*kOneHour, -16*kOneHour,   12*kOneHour,   30*kOneHour  }, // ZONE_OFFSET
    { -1*kOneHour,  -1*kOneHour,    2*kOneHour,    2*kOneHour  }, // DST_OFFSET
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // YEAR_WOY
    {           1,            1,             7,             7  }, // DOW_LOCAL
    {/*N/A*/-1,       /*N/A*/-1,     /*N/A*/-1,       /*N/A*/-1}, // EXTENDED_YEAR
    { -0x7F000000,  -0x7F000000,    0x7F000000,    0x7F000000  }, // JULIAN_DAY
    {           0,            0, 24*kOneHour-1, 24*kOneHour-1  }, // MILLISECONDS_IN_DAY
    {           0,            0,             1,             1  }, // IS_LEAP_MONTH
    {           0,            0,            11,            11  }  // ORDINAL_MONTH
};

// Resource bundle tags read by this class
static const char gCalendar[] = "calendar";
static const char gMonthNames[] = "monthNames";
static const char gGregorian[] = "gregorian";

// 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)
:   UObject(),
fIsTimeSet(false),
fAreFieldsSet(false),
fAreAllFieldsSet(false),
fAreFieldsVirtuallySet(false),
fNextStamp(static_cast<int32_t>(kMinimumUserStamp)),
fTime(0),
fLenient(true),
fZone(nullptr),
fRepeatedWallTime(UCAL_WALLTIME_LAST),
fSkippedWallTime(UCAL_WALLTIME_LAST)
{
    validLocale[0] = 0;
    actualLocale[0] = 0;
    clear();
    if (U_FAILURE(success)) {
        return;
    }
    fZone = TimeZone::createDefault();
    if (fZone == nullptr) {
        success = U_MEMORY_ALLOCATION_ERROR;
    }
    setWeekData(Locale::getDefault(), nullptr, success);
}

// -------------------------------------

Calendar::Calendar(TimeZone* zone, const Locale& aLocale, UErrorCode& success)
:   UObject(),
fIsTimeSet(false),
fAreFieldsSet(false),
fAreAllFieldsSet(false),
fAreFieldsVirtuallySet(false),
fNextStamp(static_cast<int32_t>(kMinimumUserStamp)),
fTime(0),
fLenient(true),
fZone(nullptr),
fRepeatedWallTime(UCAL_WALLTIME_LAST),
fSkippedWallTime(UCAL_WALLTIME_LAST)
{
    validLocale[0] = 0;
    actualLocale[0] = 0;
    if (U_FAILURE(success)) {
        delete zone;
        return;
    }
    if (zone == nullptr) {
#if defined (U_DEBUG_CAL)
        fprintf(stderr, "%s:%d: ILLEGAL ARG because timezone cannot be 0\n",
            __FILE__, __LINE__);
#endif
        success = U_ILLEGAL_ARGUMENT_ERROR;
        return;
    }

    clear();
    fZone = zone;
    setWeekData(aLocale, nullptr, success);
}

// -------------------------------------

Calendar::Calendar(const TimeZone& zone, const Locale& aLocale, UErrorCode& ;success)
:   UObject(),
fIsTimeSet(false),
fAreFieldsSet(false),
fAreAllFieldsSet(false),
fAreFieldsVirtuallySet(false),
fNextStamp(static_cast<int32_t>(kMinimumUserStamp)),
fTime(0),
fLenient(true),
fZone(nullptr),
fRepeatedWallTime(UCAL_WALLTIME_LAST),
fSkippedWallTime(UCAL_WALLTIME_LAST)
{
    validLocale[0] = 0;
    actualLocale[0] = 0;
    if (U_FAILURE(success)) {
        return;
    }
    clear();
    fZone = zone.clone();
    if (fZone == nullptr) {
        success = U_MEMORY_ALLOCATION_ERROR;
    }
    setWeekData(aLocale, nullptr, success);
}

// -------------------------------------

Calendar::~Calendar()
{
    delete fZone;
}

// -------------------------------------

Calendar::Calendar(const Calendar &source)
:   UObject(source)
{
    fZone = nullptr;
    *this = source;
}

// -------------------------------------

Calendar &
Calendar::operator=(const Calendar &right)
{
    if (this != &right) {
        uprv_arrayCopy(right.fFields, fFields, UCAL_FIELD_COUNT);
        uprv_arrayCopy(right.fIsSet, fIsSet, UCAL_FIELD_COUNT);
        uprv_arrayCopy(right.fStamp, fStamp, UCAL_FIELD_COUNT);
        fTime                    = right.fTime;
        fIsTimeSet               = right.fIsTimeSet;
        fAreAllFieldsSet         = right.fAreAllFieldsSet;
        fAreFieldsSet            = right.fAreFieldsSet;
        fAreFieldsVirtuallySet   = right.fAreFieldsVirtuallySet;
        fLenient                 = right.fLenient;
        fRepeatedWallTime        = right.fRepeatedWallTime;
        fSkippedWallTime         = right.fSkippedWallTime;
        delete fZone;
        fZone = nullptr;
        if (right.fZone != nullptr) {
            fZone                = right.fZone->clone();
        }
        fFirstDayOfWeek          = right.fFirstDayOfWeek;
        fMinimalDaysInFirstWeek  = right.fMinimalDaysInFirstWeek;
        fWeekendOnset            = right.fWeekendOnset;
        fWeekendOnsetMillis      = right.fWeekendOnsetMillis;
        fWeekendCease            = right.fWeekendCease;
        fWeekendCeaseMillis      = right.fWeekendCeaseMillis;
        fNextStamp               = right.fNextStamp;
        uprv_strncpy(validLocale, right.validLocale, sizeof(validLocale));
        uprv_strncpy(actualLocale, right.actualLocale, sizeof(actualLocale));
        validLocale[sizeof(validLocale)-1] = 0;
        actualLocale[sizeof(validLocale)-1] = 0;
    }

    return *this;
}

// -------------------------------------

Calendar* U_EXPORT2
Calendar::createInstance(UErrorCode& success)
{
    return createInstance(TimeZone::createDefault(), Locale::getDefault(), success);
}

// -------------------------------------

Calendar* U_EXPORT2
Calendar::createInstance(const TimeZone& zone, UErrorCode& success)
{
    return createInstance(zone, Locale::getDefault(), success);
}

// -------------------------------------

Calendar* U_EXPORT2
Calendar::createInstance(const Locale& aLocale, UErrorCode& success)
{
    return createInstance(TimeZone::forLocaleOrDefault(aLocale), aLocale, success);
}

// ------------------------------------- Adopting

// Note: this is the bottleneck that actually calls the service routines.

Calendar * U_EXPORT2
Calendar::makeInstance(const Locale& aLocale, UErrorCode& success) {
    if (U_FAILURE(success)) {
        return nullptr;
    }

    Locale actualLoc;
    UObject* u = nullptr;

#if !UCONFIG_NO_SERVICE
    if (isCalendarServiceUsed()) {
        u = getCalendarService(success)->get(aLocale, LocaleKey::KIND_ANY, &actualLoc, success);
    }
    else
#endif
    {
        u = createStandardCalendar(getCalendarTypeForLocale(aLocale.getName()), aLocale, success);
    }
    Calendar* c = nullptr;

    if(U_FAILURE(success) || !u) {
        if(U_SUCCESS(success)) { // Propagate some kind of err
            success = U_INTERNAL_PROGRAM_ERROR;
        }
        return nullptr;
    }

#if !UCONFIG_NO_SERVICE
    const UnicodeString* str = dynamic_cast<const UnicodeString*>(u);
    if(str != nullptr) {
        // It's a unicode string telling us what type of calendar to load ("gregorian", etc)
        // Create a Locale over this string
        Locale l("");
        LocaleUtility::initLocaleFromName(*str, l);

#ifdef U_DEBUG_CALSVC
        fprintf(stderr, "Calendar::createInstance(%s), looking up [%s]\n", aLocale.getName(), l.getName());
#endif

        Locale actualLoc2;
        delete u;
        u = nullptr;

        // Don't overwrite actualLoc, since the actual loc from this call
        // may be something like "@calendar=gregorian" -- TODO investigate
        // further...
        c = (Calendar*)getCalendarService(success)->get(l, LocaleKey::KIND_ANY, &actualLoc2, success);

        if(U_FAILURE(success) || !c) {
            if(U_SUCCESS(success)) {
                success = U_INTERNAL_PROGRAM_ERROR; // Propagate some err
            }
            return nullptr;
        }

        str = dynamic_cast<const UnicodeString*>(c);
        if(str != nullptr) {
            // recursed! Second lookup returned a UnicodeString.
            // Perhaps DefaultCalendar{} was set to another locale.
#ifdef U_DEBUG_CALSVC
            char tmp[200];
            // Extract a char* out of it..
            int32_t len = str->length();
            int32_t actLen = sizeof(tmp)-1;
            if(len > actLen) {
                len = actLen;
            }
            str->extract(0,len,tmp);
            tmp[len]=0;

            fprintf(stderr, "err - recursed, 2nd lookup was unistring %s\n", tmp);
#endif
            success = U_MISSING_RESOURCE_ERROR;  // requested a calendar type which could NOT be found.
            delete c;
            return nullptr;
        }
#ifdef U_DEBUG_CALSVC
        fprintf(stderr, "%p: setting week count data to locale %s, actual locale %s\n", c, (const char*)aLocale.getName(), (const char *)actualLoc.getName());
#endif
        c->setWeekData(aLocale, c->getType(), success);  // set the correct locale (this was an indirect calendar)

        char keyword[ULOC_FULLNAME_CAPACITY] = "";
        UErrorCode tmpStatus = U_ZERO_ERROR;
        l.getKeywordValue("calendar", keyword, ULOC_FULLNAME_CAPACITY, tmpStatus);
        if (U_SUCCESS(tmpStatus) && uprv_strcmp(keyword, "iso8601") == 0) {
            c->setFirstDayOfWeek(UCAL_MONDAY);
            c->setMinimalDaysInFirstWeek(4);
        }
    }
    else
#endif /* UCONFIG_NO_SERVICE */
    {
        // a calendar was returned - we assume the factory did the right thing.
        c = (Calendar*)u;
    }

    return c;
}

Calendar* U_EXPORT2
Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success)
{
    LocalPointer<TimeZone> zonePtr(zone);
    const SharedCalendar *shared = nullptr;
    UnifiedCache::getByLocale(aLocale, shared, success);
    if (U_FAILURE(success)) {
        return nullptr;
    }
    Calendar *c = (*shared)->clone();
    shared->removeRef();
    if (c == nullptr) {
        success = U_MEMORY_ALLOCATION_ERROR;
        return nullptr;
    }

    // Now, reset calendar to default state:
    c->adoptTimeZone(zonePtr.orphan()); //  Set the correct time zone
    c->setTimeInMillis(getNow(), success); // let the new calendar have the current time.

    return c;
}

// -------------------------------------

Calendar* U_EXPORT2
Calendar::createInstance(const TimeZone& zone, const Locale& aLocale, UErrorCode& success)
{
    Calendar* c = createInstance(aLocale, success);
    if(U_SUCCESS(success) && c) {
        c->setTimeZone(zone);
    }
    return c;
}

// -------------------------------------

void U_EXPORT2
Calendar::getCalendarTypeFromLocale(
        const Locale &aLocale,
        char *typeBuffer,
        int32_t typeBufferSize,
        UErrorCode &success) {
    const SharedCalendar *shared = nullptr;
    UnifiedCache::getByLocale(aLocale, shared, success);
    if (U_FAILURE(success)) {
        return;
    }
    uprv_strncpy(typeBuffer, (*shared)->getType(), typeBufferSize);
    shared->removeRef();
    if (typeBuffer[typeBufferSize - 1]) {
        success = U_BUFFER_OVERFLOW_ERROR;
    }
}

bool
Calendar::operator==(const Calendar& that) const
{
    UErrorCode status = U_ZERO_ERROR;
    return isEquivalentTo(that) &&
        getTimeInMillis(status) == that.getTimeInMillis(status) &&
        U_SUCCESS(status);
}

UBool
Calendar::isEquivalentTo(const Calendar& other) const
{
    return typeid(*this) == typeid(other) &&
        fLenient                == other.fLenient &&
        fRepeatedWallTime       == other.fRepeatedWallTime &&
        fSkippedWallTime        == other.fSkippedWallTime &&
        fFirstDayOfWeek         == other.fFirstDayOfWeek &&
        fMinimalDaysInFirstWeek == other.fMinimalDaysInFirstWeek &&
        fWeekendOnset           == other.fWeekendOnset &&
        fWeekendOnsetMillis     == other.fWeekendOnsetMillis &&
        fWeekendCease           == other.fWeekendCease &&
        fWeekendCeaseMillis     == other.fWeekendCeaseMillis &&
        *fZone                  == *other.fZone;
}

// -------------------------------------

UBool
Calendar::equals(const Calendar& when, UErrorCode& status) const
{
    return (this == &when ||
        getTime(status) == when.getTime(status));
}

// -------------------------------------

UBool
Calendar::before(const Calendar& when, UErrorCode& status) const
{
    return (this != &when &&
        getTimeInMillis(status) < when.getTimeInMillis(status));
}

// -------------------------------------

UBool
Calendar::after(const Calendar& when, UErrorCode& status) const
{
    return (this != &when &&
        getTimeInMillis(status) > when.getTimeInMillis(status));
}

// -------------------------------------


const Locale* U_EXPORT2
Calendar::getAvailableLocales(int32_t& count)
{
    return Locale::getAvailableLocales(count);
}

// -------------------------------------

StringEnumeration* U_EXPORT2
Calendar::getKeywordValuesForLocale(const char* key,
                    const Locale& locale, UBool commonlyUsed, UErrorCode& status)
{
    // This is a wrapper over ucal_getKeywordValuesForLocale
    UEnumeration *uenum = ucal_getKeywordValuesForLocale(key, locale.getName(),
                                                        commonlyUsed, &status);
    if (U_FAILURE(status)) {
        uenum_close(uenum);
        return nullptr;
    }
    UStringEnumeration* ustringenum = new UStringEnumeration(uenum);
    if (ustringenum == nullptr) {
        status = U_MEMORY_ALLOCATION_ERROR;
    }
    return ustringenum;
}

// -------------------------------------

UDate U_EXPORT2
Calendar::getNow()
{
    return uprv_getUTCtime(); // return as milliseconds
}

// -------------------------------------

/**
* 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
{
    if(U_FAILURE(status))
        return 0.0;

    if ( ! fIsTimeSet)
        const_cast<Calendar*>(this)->updateTime(status);

    /* Test for buffer overflows */
    if(U_FAILURE(status)) {
        return 0.0;
    }
    return fTime;
}

// -------------------------------------

/**
* 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 ) {
    if(U_FAILURE(status))
        return;

    if (millis > MAX_MILLIS) {
        if(isLenient()) {
            millis = MAX_MILLIS;
        } else {
      status = U_ILLEGAL_ARGUMENT_ERROR;
      return;
        }
    } else if (millis < MIN_MILLIS) {
        if(isLenient()) {
            millis = MIN_MILLIS;
        } else {
      status = U_ILLEGAL_ARGUMENT_ERROR;
      return;
        }
    } else if (uprv_isNaN(millis)) {
        status = U_ILLEGAL_ARGUMENT_ERROR;
        return;
    }

    fTime = millis;
    fAreFieldsSet = fAreAllFieldsSet = false;
    fIsTimeSet = fAreFieldsVirtuallySet = true;

    for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
        fFields[i]     = 0;
        fStamp[i]     = kUnset;
        fIsSet[i]     = false;
    }


}

// -------------------------------------

int32_t
Calendar::get(UCalendarDateFields field, UErrorCode& status) const
{
    if (U_FAILURE(status)) {
        return 0;
    }
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
        status = U_ILLEGAL_ARGUMENT_ERROR;
        return 0;
    }
    // field values are only computed when actually requested; for more on when computation
    // of various things happens, see the "data flow in Calendar" description at the top
    // of this file
    if (U_SUCCESS(status)) const_cast<Calendar*>(this)->complete(status); // Cast away const
    return U_SUCCESS(status) ? fFields[field] : 0;
}

// -------------------------------------

void
Calendar::set(UCalendarDateFields field, int32_t value)
{
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
        return;
    }
    if (fAreFieldsVirtuallySet) {
        UErrorCode ec = U_ZERO_ERROR;
        computeFields(ec);
    }
    fFields[field]     = value;
    /* Ensure that the fNextStamp value doesn't go pass max value for int32_t */
    if (fNextStamp == STAMP_MAX) {
        recalculateStamp();
    }
    fStamp[field]     = fNextStamp++;
    fIsSet[field]     = true// Remove later
    fIsTimeSet = fAreFieldsSet = fAreFieldsVirtuallySet = false;
}

// -------------------------------------

void
Calendar::set(int32_t year, int32_t month, int32_t date)
{
    set(UCAL_YEAR, year);
    set(UCAL_MONTH, month);
    set(UCAL_DATE, date);
}

// -------------------------------------

void
Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute)
{
    set(UCAL_YEAR, year);
    set(UCAL_MONTH, month);
    set(UCAL_DATE, date);
    set(UCAL_HOUR_OF_DAY, hour);
    set(UCAL_MINUTE, minute);
}

// -------------------------------------

void
Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t minute, int32_t second)
{
    set(UCAL_YEAR, year);
    set(UCAL_MONTH, month);
    set(UCAL_DATE, date);
    set(UCAL_HOUR_OF_DAY, hour);
    set(UCAL_MINUTE, minute);
    set(UCAL_SECOND, second);
}

// -------------------------------------
int32_t Calendar::getRelatedYear(UErrorCode &status) const
{
    return get(UCAL_EXTENDED_YEAR, status);
}

// -------------------------------------
void Calendar::setRelatedYear(int32_t year)
{
    // set extended year
    set(UCAL_EXTENDED_YEAR, year);
}

// -------------------------------------

void
Calendar::clear()
{
    for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
        fFields[i]     = 0; // Must do this; other code depends on it
        fStamp[i]     = kUnset;
        fIsSet[i]     = false// Remove later
    }
    fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = false;
    // fTime is not 'cleared' - may be used if no fields are set.
}

// -------------------------------------

void
Calendar::clear(UCalendarDateFields field)
{
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
        return;
    }
    if (fAreFieldsVirtuallySet) {
        UErrorCode ec = U_ZERO_ERROR;
        computeFields(ec);
    }
    fFields[field]         = 0;
    fStamp[field]         = kUnset;
    if (field == UCAL_MONTH) {
        fFields[UCAL_ORDINAL_MONTH]         = 0;
        fStamp[UCAL_ORDINAL_MONTH]         = kUnset;
        fIsSet[UCAL_ORDINAL_MONTH]         = false// Remove later
    }
    if (field == UCAL_ORDINAL_MONTH) {
        fFields[UCAL_MONTH]         = 0;
        fStamp[UCAL_MONTH]         = kUnset;
        fIsSet[UCAL_MONTH]         = false// Remove later
    }
    fIsTimeSet = fAreFieldsSet = fAreAllFieldsSet = fAreFieldsVirtuallySet = false;
}

// -------------------------------------

UBool
Calendar::isSet(UCalendarDateFields field) const
{
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
        return false;
    }
    return fAreFieldsVirtuallySet || (fStamp[field] != kUnset);
}


int32_t Calendar::newestStamp(UCalendarDateFields first, UCalendarDateFields last, int32_t bestStampSoFar) const
{
    int32_t bestStamp = bestStampSoFar;
    for (int32_t i = static_cast<int32_t>(first); i <= static_cast<int32_t>(last); ++i) {
        if (fStamp[i] > bestStamp) {
            bestStamp = fStamp[i];
        }
    }
    return bestStamp;
}


// -------------------------------------

void
Calendar::complete(UErrorCode& status)
{
    if (U_FAILURE(status)) {
       return;
    }
    if (!fIsTimeSet) {
        updateTime(status);
        /* Test for buffer overflows */
        if(U_FAILURE(status)) {
            return;
        }
    }
    if (!fAreFieldsSet) {
        computeFields(status); // fills in unset fields
        /* Test for buffer overflows */
        if(U_FAILURE(status)) {
            return;
        }
        fAreFieldsSet         = true;
        fAreAllFieldsSet     = true;
    }
}

//-------------------------------------------------------------------------
// 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) {
    if (U_FAILURE(status)) {
       return;
    }
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
       status = U_ILLEGAL_ARGUMENT_ERROR;
       return;
    }
    int32_t max = getActualMaximum(field, status);
    int32_t min = getActualMinimum(field, status);

    if (fFields[field] > max) {
        set(field, max);
    } else if (fFields[field] < min) {
        set(field, min);
    }
}


void Calendar::computeFields(UErrorCode &ec)
{
    if (U_FAILURE(ec)) {
        return;
    }
    // Compute local wall millis
    double localMillis = internalGetTime();
    int32_t rawOffset, dstOffset;
    getTimeZone().getOffset(localMillis, false, rawOffset, dstOffset, ec);
    if (U_FAILURE(ec)) {
        return;
    }
    localMillis += (rawOffset + dstOffset);

    // Mark fields as set.  Do this before calling handleComputeFields().
    uint32_t mask =   //fInternalSetMask;
        (1 << UCAL_ERA) |
        (1 << UCAL_YEAR) |
        (1 << UCAL_MONTH) |
        (1 << UCAL_DAY_OF_MONTH) | // = UCAL_DATE
        (1 << UCAL_DAY_OF_YEAR) |
        (1 << UCAL_EXTENDED_YEAR) |
        (1 << UCAL_ORDINAL_MONTH);

    for (int32_t i=0; i<UCAL_FIELD_COUNT; ++i) {
        if ((mask & 1) == 0) {
            fStamp[i] = kInternallySet;
            fIsSet[i] = true// Remove later
        } else {
            fStamp[i] = kUnset;
            fIsSet[i] = false// Remove later
        }
        mask >>= 1;
    }

    // We used to check for and correct extreme millis values (near
    // Long.MIN_VALUE or Long.MAX_VALUE) here.  Such values would cause
    // overflows from positive to negative (or vice versa) and had to
    // be manually tweaked.  We no longer need to do this because we
    // have limited the range of supported dates to those that have a
    // Julian day that fits into an int.  This allows us to implement a
    // JULIAN_DAY field and also removes some inelegant code. - Liu
    // 11/6/00

    int32_t millisInDay;
    double days = ClockMath::floorDivide(
        localMillis, U_MILLIS_PER_DAY, &millisInDay) +
        kEpochStartAsJulianDay;
    if (days > INT32_MAX || days < INT32_MIN) {
        ec = U_ILLEGAL_ARGUMENT_ERROR;
        return;
    }

    internalSet(UCAL_JULIAN_DAY, static_cast<int32_t>(days));

#if defined (U_DEBUG_CAL)
    //fprintf(stderr, "%s:%d- Hmm! Jules @ %d, as per %.0lf millis\n",
    //__FILE__, __LINE__, fFields[UCAL_JULIAN_DAY], localMillis);
#endif

    computeGregorianAndDOWFields(fFields[UCAL_JULIAN_DAY], ec);

    // Call framework method to have subclass compute its fields.
    // These must include, at a minimum, MONTH, DAY_OF_MONTH,
    // EXTENDED_YEAR, YEAR, DAY_OF_YEAR.  This method will call internalSet(),
    // which will update stamp[].
    handleComputeFields(fFields[UCAL_JULIAN_DAY], ec);

    // Compute week-related fields, based on the subclass-computed
    // fields computed by handleComputeFields().
    computeWeekFields(ec);

    // Compute time-related fields.  These are independent of the date and
    // of the subclass algorithm.  They depend only on the local zone
    // wall milliseconds in day.
    if (U_FAILURE(ec)) {
        return;
    }

    fFields[UCAL_MILLISECONDS_IN_DAY] = millisInDay;
    U_ASSERT(getMinimum(UCAL_MILLISECONDS_IN_DAY) <=
             fFields[UCAL_MILLISECONDS_IN_DAY]);
    U_ASSERT(fFields[UCAL_MILLISECONDS_IN_DAY] <=
             getMaximum(UCAL_MILLISECONDS_IN_DAY));

    fFields[UCAL_MILLISECOND] = millisInDay % 1000;
    U_ASSERT(getMinimum(UCAL_MILLISECOND) <= fFields[UCAL_MILLISECOND]);
    U_ASSERT(fFields[UCAL_MILLISECOND] <= getMaximum(UCAL_MILLISECOND));

    millisInDay /= 1000;
    fFields[UCAL_SECOND] = millisInDay % 60;
    U_ASSERT(getMinimum(UCAL_SECOND) <= fFields[UCAL_SECOND]);
    U_ASSERT(fFields[UCAL_SECOND] <= getMaximum(UCAL_SECOND));

    millisInDay /= 60;
    fFields[UCAL_MINUTE] = millisInDay % 60;
    U_ASSERT(getMinimum(UCAL_MINUTE) <= fFields[UCAL_MINUTE]);
    U_ASSERT(fFields[UCAL_MINUTE] <= getMaximum(UCAL_MINUTE));

    millisInDay /= 60;
    fFields[UCAL_HOUR_OF_DAY] = millisInDay;
    U_ASSERT(getMinimum(UCAL_HOUR_OF_DAY) <= fFields[UCAL_HOUR_OF_DAY]);
    U_ASSERT(fFields[UCAL_HOUR_OF_DAY] <= getMaximum(UCAL_HOUR_OF_DAY));

    fFields[UCAL_AM_PM] = millisInDay / 12; // Assume AM == 0
    U_ASSERT(getMinimum(UCAL_AM_PM) <= fFields[UCAL_AM_PM]);
    U_ASSERT(fFields[UCAL_AM_PM] <= getMaximum(UCAL_AM_PM));

    fFields[UCAL_HOUR] = millisInDay % 12;
    U_ASSERT(getMinimum(UCAL_HOUR) <= fFields[UCAL_HOUR]);
    U_ASSERT(fFields[UCAL_HOUR] <= getMaximum(UCAL_HOUR));

    fFields[UCAL_ZONE_OFFSET] = rawOffset;
    U_ASSERT(getMinimum(UCAL_ZONE_OFFSET) <= fFields[UCAL_ZONE_OFFSET]);
    U_ASSERT(fFields[UCAL_ZONE_OFFSET] <= getMaximum(UCAL_ZONE_OFFSET));

    fFields[UCAL_DST_OFFSET] = dstOffset;
    U_ASSERT(getMinimum(UCAL_DST_OFFSET) <= fFields[UCAL_DST_OFFSET]);
    U_ASSERT(fFields[UCAL_DST_OFFSET] <= getMaximum(UCAL_DST_OFFSET));
}

uint8_t Calendar::julianDayToDayOfWeek(int32_t julian)
{
    // If julian is negative, then julian%7 will be negative, so we adjust
    // accordingly.  We add 1 because Julian day 0 is Monday.
    int8_t dayOfWeek = static_cast<int8_t>((julian + 1LL) % 7);

    uint8_t result = static_cast<uint8_t>(dayOfWeek + ((dayOfWeek < 0) ? (7 + UCAL_SUNDAY) : UCAL_SUNDAY));
    return result;
}

/**
* 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)
{
    computeGregorianFields(julianDay, ec);
    if (U_FAILURE(ec)) {
        return;
    }

    // Compute day of week: JD 0 = Monday
    int32_t dow = julianDayToDayOfWeek(julianDay);
    internalSet(UCAL_DAY_OF_WEEK,dow);

    // Calculate 1-based localized day of week
    int32_t dowLocal = dow - getFirstDayOfWeek() + 1;
    if (dowLocal < 1) {
        dowLocal += 7;
    }
    internalSet(UCAL_DOW_LOCAL,dowLocal);
    fFields[UCAL_DOW_LOCAL] = dowLocal;
}

/**
* 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) {
    if (U_FAILURE(ec)) {
        return;
    }
    int32_t gregorianDayOfWeekUnused;
    if (uprv_add32_overflow(
            julianDay, -kEpochStartAsJulianDay, &julianDay)) {
        ec = U_ILLEGAL_ARGUMENT_ERROR;
        return;
    }
    Grego::dayToFields(julianDay, fGregorianYear, fGregorianMonth,
                       fGregorianDayOfMonth, gregorianDayOfWeekUnused,
                       fGregorianDayOfYear, 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) {
    if(U_FAILURE(ec)) {
        return;
    }
    int32_t eyear = fFields[UCAL_EXTENDED_YEAR];
    int32_t dayOfWeek = fFields[UCAL_DAY_OF_WEEK];
    int32_t dayOfYear = fFields[UCAL_DAY_OF_YEAR];

    // WEEK_OF_YEAR start
    // Compute the week of the year.  For the Gregorian calendar, valid week
    // numbers run from 1 to 52 or 53, depending on the year, the first day
    // of the week, and the minimal days in the first week.  For other
    // calendars, the valid range may be different -- it depends on the year
    // length.  Days at the start of the year may fall into the last week of
    // the previous year; days at the end of the year may fall into the
    // first week of the next year.  ASSUME that the year length is less than
    // 7000 days.
    int32_t yearOfWeekOfYear = eyear;
    int32_t relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; // 0..6
    int32_t relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; // 0..6
    int32_t woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53
    if ((7 - relDowJan1) >= getMinimalDaysInFirstWeek()) {
        ++woy;
    }

    // Adjust for weeks at the year end that overlap into the previous or
    // next calendar year.
    if (woy == 0) {
        // We are the last week of the previous year.
        // Check to see if we are in the last week; if so, we need
        // to handle the case in which we are the first week of the
        // next year.

        int32_t prevDoy = dayOfYear + handleGetYearLength(eyear - 1);
        woy = weekNumber(prevDoy, dayOfWeek);
        yearOfWeekOfYear--;
    } else {
        int32_t lastDoy = handleGetYearLength(eyear);
        // Fast check: For it to be week 1 of the next year, the DOY
        // must be on or after L-5, where L is yearLength(), then it
        // cannot possibly be week 1 of the next year:
        //          L-5                  L
        // doy: 359 360 361 362 363 364 365 001
        // dow:      1   2   3   4   5   6   7
        if (dayOfYear >= (lastDoy - 5)) {
            int32_t lastRelDow = (relDow + lastDoy - dayOfYear) % 7;
            if (lastRelDow < 0) {
                lastRelDow += 7;
            }
            if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) &&
                ((dayOfYear + 7 - relDow) > lastDoy)) {
                    woy = 1;
                    yearOfWeekOfYear++;
                }
        }
    }
    fFields[UCAL_WEEK_OF_YEAR] = woy;
    fFields[UCAL_YEAR_WOY] = yearOfWeekOfYear;
    // min/max of years are not constrains for caller, so not assert here.
    // WEEK_OF_YEAR end

    int32_t dayOfMonth = fFields[UCAL_DAY_OF_MONTH];
    fFields[UCAL_WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek);
    U_ASSERT(getMinimum(UCAL_WEEK_OF_MONTH) <= fFields[UCAL_WEEK_OF_MONTH]);
    U_ASSERT(fFields[UCAL_WEEK_OF_MONTH] <= getMaximum(UCAL_WEEK_OF_MONTH));

    fFields[UCAL_DAY_OF_WEEK_IN_MONTH] = (dayOfMonth-1) / 7 + 1;
    U_ASSERT(getMinimum(UCAL_DAY_OF_WEEK_IN_MONTH) <=
             fFields[UCAL_DAY_OF_WEEK_IN_MONTH]);
    U_ASSERT(fFields[UCAL_DAY_OF_WEEK_IN_MONTH] <=
             getMaximum(UCAL_DAY_OF_WEEK_IN_MONTH));

#if defined (U_DEBUG_CAL)
    if(fFields[UCAL_DAY_OF_WEEK_IN_MONTH]==0) fprintf(stderr, "%s:%d: DOWIM %d on %g\n",
        __FILE__, __LINE__,fFields[UCAL_DAY_OF_WEEK_IN_MONTH], fTime);
#endif
}


int32_t Calendar::weekNumber(int32_t desiredDay, int32_t dayOfPeriod, int32_t dayOfWeek)
{
    // Determine the day of the week of the first day of the period
    // in question (either a year or a month).  Zero represents the
    // first day of the week on this calendar.
    int32_t periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % 7;
    if (periodStartDayOfWeek < 0) periodStartDayOfWeek += 7;

    // Compute the week number.  Initially, ignore the first week, which
    // may be fractional (or may not be).  We add periodStartDayOfWeek in
    // order to fill out the first week, if it is fractional.
    int32_t weekNo = (desiredDay + periodStartDayOfWeek - 1)/7;

    // If the first week is long enough, then count it.  If
    // the minimal days in the first week is one, or if the period start
    // is zero, we always increment weekNo.
    if ((7 - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo;

    return weekNo;
}

void Calendar::handleComputeFields(int32_t /* julianDay */, UErrorCode& status)
{
    if (U_FAILURE(status)) {
        return;
    }
    int32_t month = getGregorianMonth();
    internalSet(UCAL_MONTH, month);
    internalSet(UCAL_ORDINAL_MONTH, month);
    internalSet(UCAL_DAY_OF_MONTH, getGregorianDayOfMonth());
    internalSet(UCAL_DAY_OF_YEAR, getGregorianDayOfYear());
    int32_t eyear = getGregorianYear();
    internalSet(UCAL_EXTENDED_YEAR, eyear);
    int32_t era = GregorianCalendar::AD;
    if (eyear < 1) {
        era = GregorianCalendar::BC;
        eyear = 1 - eyear;
    }
    internalSet(UCAL_ERA, era);
    internalSet(UCAL_YEAR, eyear);
}
// -------------------------------------


void Calendar::roll(EDateFields field, int32_t amount, UErrorCode& status)
{
    roll(static_cast<UCalendarDateFields>(field), amount, status);
}

void Calendar::roll(UCalendarDateFields field, int32_t amount, UErrorCode& status) UPRV_NO_SANITIZE_UNDEFINED {
    if (amount == 0) {
        return// Nothing to do
    }

    complete(status);

    if(U_FAILURE(status)) {
        return;
    }
    if (field < 0 || field >= UCAL_FIELD_COUNT) {
       status = U_ILLEGAL_ARGUMENT_ERROR;
       return;
    }
    switch (field) {
    case UCAL_DAY_OF_MONTH:
    case UCAL_AM_PM:
    case UCAL_MINUTE:
    case UCAL_SECOND:
    case UCAL_MILLISECOND:
    case UCAL_MILLISECONDS_IN_DAY:
    case UCAL_ERA:
        // These are the standard roll instructions.  These work for all
        // simple cases, that is, cases in which the limits are fixed, such
        // as the hour, the day of the month, and the era.
        {
            int32_t min = getActualMinimum(field,status);
            int32_t max = getActualMaximum(field,status);
            if (U_FAILURE(status)) {
               return;
            }
            int32_t gap = max - min + 1;

            int64_t value = internalGet(field);
            value = (value + amount - min) % gap;
            if (value < 0) {
                value += gap;
            }
            value += min;

            set(field, value);
            return;
        }

    case UCAL_HOUR:
    case UCAL_HOUR_OF_DAY:
        // Rolling the hour is difficult on the ONSET and CEASE days of
        // daylight savings.  For example, if the change occurs at
        // 2 AM, we have the following progression:
        // ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst
        // CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std
        // To get around this problem we don't use fields; we manipulate
        // the time in millis directly.
        {
            // Assume min == 0 in calculations below
            double start = getTimeInMillis(status);
            int64_t oldHour = internalGet(field);
            int32_t max = getMaximum(field);
            int32_t newHour = (oldHour + amount) % (max + 1);
            if (newHour < 0) {
                newHour += max + 1;
            }
            setTimeInMillis(start + kOneHour * (newHour - oldHour),status);
            return;
        }

    case UCAL_MONTH:
    case UCAL_ORDINAL_MONTH:
        // Rolling the month involves both pinning the final value
        // and adjusting the DAY_OF_MONTH if necessary.  We only adjust the
        // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
        // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
        {
            int32_t max = getActualMaximum(UCAL_MONTH, status) + 1;
            int64_t mon = internalGet(UCAL_MONTH);
            mon = (mon + amount) % max;

            if (mon < 0) {
                mon += max;
            }
            set(UCAL_MONTH, mon);

            // Keep the day of month in range.  We don't want to spill over
            // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 ->
            // mar3.
            pinField(UCAL_DAY_OF_MONTH,status);
            return;
        }

    case UCAL_YEAR:
    case UCAL_YEAR_WOY:
        {
            // * If era==0 and years go backwards in time, change sign of amount.
            // * Until we have new API per #9393, we temporarily hardcode knowledge of
            //   which calendars have era 0 years that go backwards.
            int32_t era = internalGet(UCAL_ERA);
            if (era == 0 && isEra0CountingBackward()) {
                if (uprv_mul32_overflow(amount, -1, &amount)) {
                    status = U_ILLEGAL_ARGUMENT_ERROR;
                    return;
                }
            }
            int32_t newYear;
            if (uprv_add32_overflow(
                amount, internalGet(field), &newYear)) {
                status = U_ILLEGAL_ARGUMENT_ERROR;
                return;
            }
            if (era > 0 || newYear >= 1) {
                int32_t maxYear = getActualMaximum(field, status);
                if (maxYear < 32768) {
                    // this era has real bounds, roll should wrap years
                    if (newYear < 1) {
                        newYear = maxYear - ((-newYear) % maxYear);
                    } else if (newYear > maxYear) {
                        newYear = ((newYear - 1) % maxYear) + 1;
                    }
                // else era is unbounded, just pin low year instead of wrapping
                } else if (newYear < 1) {
                    newYear = 1;
                }
            // else we are in era 0 with newYear < 1;
            // calendars with years that go backwards must pin the year value at 0,
            // other calendars can have years < 0 in era 0
            } else if (era == 0 && isEra0CountingBackward()) {
                newYear = 1;
            }
            set(field, newYear);
            pinField(UCAL_MONTH,status);
            pinField(UCAL_DAY_OF_MONTH,status);
            return;
        }

    case UCAL_EXTENDED_YEAR:
        // Rolling the year can involve pinning the DAY_OF_MONTH.
        if (uprv_add32_overflow(
            amount, internalGet(field), &amount)) {
            status = U_ILLEGAL_ARGUMENT_ERROR;
            return;
        }
        set(field, amount);
        pinField(UCAL_MONTH,status);
        pinField(UCAL_DAY_OF_MONTH,status);
        return;

    case UCAL_WEEK_OF_MONTH:
        {
            // This is tricky, because during the roll we may have to shift
            // to a different day of the week.  For example:

            //    s  m  t  w  r  f  s
            //          1  2  3  4  5
            //    6  7  8  9 10 11 12

            // When rolling from the 6th or 7th back one week, we go to the
            // 1st (assuming that the first partial week counts).  The same
            // thing happens at the end of the month.

            // The other tricky thing is that we have to figure out whether
            // the first partial week actually counts or not, based on the
            // minimal first days in the week.  And we have to use the
            // correct first day of the week to delineate the week
            // boundaries.

            // Here's our algorithm.  First, we find the real boundaries of
            // the month.  Then we discard the first partial week if it
            // doesn't count in this locale.  Then we fill in the ends with
            // phantom days, so that the first partial week and the last
            // partial week are full weeks.  We then have a nice square
            // block of weeks.  We do the usual rolling within this block,
            // as is done elsewhere in this method.  If we wind up on one of
            // the phantom days that we added, we recognize this and pin to
            // the first or the last day of the month.  Easy, eh?

            // Normalize the DAY_OF_WEEK so that 0 is the first day of the week
            // in this locale.  We have dow in 0..6.
            int32_t dow = internalGet(UCAL_DAY_OF_WEEK) - getFirstDayOfWeek();
            if (dow < 0) dow += 7;

            // Find the day of the week (normalized for locale) for the first
            // of the month.
            int32_t fdm = (dow - internalGet(UCAL_DAY_OF_MONTH) + 1) % 7;
            if (fdm < 0) fdm += 7;

            // Get the first day of the first full week of the month,
            // including phantom days, if any.  Figure out if the first week
            // counts or not; if it counts, then fill in phantom days.  If
            // not, advance to the first real full week (skip the partial week).
            int32_t start;
            if ((7 - fdm) < getMinimalDaysInFirstWeek())
                start = 8 - fdm; // Skip the first partial week
            else
                start = 1 - fdm; // This may be zero or negative

            // Get the day of the week (normalized for locale) for the last
            // day of the month.
            int32_t monthLen = getActualMaximum(UCAL_DAY_OF_MONTH, status);
            int32_t ldm = (monthLen - internalGet(UCAL_DAY_OF_MONTH) + dow) % 7;
            // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here.

            // Get the limit day for the blocked-off rectangular month; that
            // is, the day which is one past the last day of the month,
            // after the month has already been filled in with phantom days
            // to fill out the last week.  This day has a normalized DOW of 0.
            int32_t limit = monthLen + 7 - ldm;

            // Now roll between start and (limit - 1).
            int32_t gap = limit - start;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=83 H=98 G=90

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.