From: Frank Tang Date: Thu, 10 Nov 2022 01:22:16 +0000 (+0000) Subject: ICU-22164 Replace switch+getType with subclass X-Git-Tag: cldr/2022-12-02~9 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9c1fb785b30ad8ce3177f1a6dab13ba224ef54f9;p=icu ICU-22164 Replace switch+getType with subclass See #2215 fix --- diff --git a/icu4c/source/i18n/calendar.cpp b/icu4c/source/i18n/calendar.cpp index f5a40e48395..6e2278ba686 100644 --- a/icu4c/source/i18n/calendar.cpp +++ b/icu4c/source/i18n/calendar.cpp @@ -45,6 +45,7 @@ #include "hebrwcal.h" #include "persncal.h" #include "indiancal.h" +#include "iso8601cal.h" #include "chnsecal.h" #include "coptccal.h" #include "dangical.h" @@ -354,18 +355,19 @@ static Calendar *createStandardCalendar(ECalType calType, const Locale &loc, UEr cal.adoptInsteadAndCheckErrorCode(new PersianCalendar(loc, status), status); break; case CALTYPE_ISLAMIC_TBLA: - cal.adoptInsteadAndCheckErrorCode(new IslamicCalendar(loc, status, IslamicCalendar::TBLA), status); + cal.adoptInsteadAndCheckErrorCode(new IslamicTBLACalendar(loc, status), status); break; case CALTYPE_ISLAMIC_CIVIL: - cal.adoptInsteadAndCheckErrorCode(new IslamicCalendar(loc, status, IslamicCalendar::CIVIL), status); + cal.adoptInsteadAndCheckErrorCode(new IslamicCivilCalendar(loc, status), status); break; case CALTYPE_ISLAMIC_RGSA: - // default any region specific not handled individually to islamic + cal.adoptInsteadAndCheckErrorCode(new IslamicRGSACalendar(loc, status), status); + break; case CALTYPE_ISLAMIC: - cal.adoptInsteadAndCheckErrorCode(new IslamicCalendar(loc, status, IslamicCalendar::ASTRONOMICAL), status); + cal.adoptInsteadAndCheckErrorCode(new IslamicCalendar(loc, status), status); break; case CALTYPE_ISLAMIC_UMALQURA: - cal.adoptInsteadAndCheckErrorCode(new IslamicCalendar(loc, status, IslamicCalendar::UMALQURA), status); + cal.adoptInsteadAndCheckErrorCode(new IslamicUmalquraCalendar(loc, status), status); break; case CALTYPE_HEBREW: cal.adoptInsteadAndCheckErrorCode(new HebrewCalendar(loc, status), status); @@ -380,17 +382,13 @@ static Calendar *createStandardCalendar(ECalType calType, const Locale &loc, UEr cal.adoptInsteadAndCheckErrorCode(new CopticCalendar(loc, status), status); break; case CALTYPE_ETHIOPIC: - cal.adoptInsteadAndCheckErrorCode(new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_MIHRET_ERA), status); + cal.adoptInsteadAndCheckErrorCode(new EthiopicCalendar(loc, status), status); break; case CALTYPE_ETHIOPIC_AMETE_ALEM: - cal.adoptInsteadAndCheckErrorCode(new EthiopicCalendar(loc, status, EthiopicCalendar::AMETE_ALEM_ERA), status); + cal.adoptInsteadAndCheckErrorCode(new EthiopicAmeteAlemCalendar(loc, status), status); break; case CALTYPE_ISO8601: - cal.adoptInsteadAndCheckErrorCode(new GregorianCalendar(loc, status), status); - if (cal.isValid()) { - cal->setFirstDayOfWeek(UCAL_MONDAY); - cal->setMinimalDaysInFirstWeek(4); - } + cal.adoptInsteadAndCheckErrorCode(new ISO8601Calendar(loc, status), status); break; case CALTYPE_DANGI: cal.adoptInsteadAndCheckErrorCode(new DangiCalendar(loc, status), status); diff --git a/icu4c/source/i18n/ethpccal.cpp b/icu4c/source/i18n/ethpccal.cpp index 9b97b1ca33c..b9ec17b46fe 100644 --- a/icu4c/source/i18n/ethpccal.cpp +++ b/icu4c/source/i18n/ethpccal.cpp @@ -19,6 +19,7 @@ U_NAMESPACE_BEGIN UOBJECT_DEFINE_RTTI_IMPLEMENTATION(EthiopicCalendar) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(EthiopicAmeteAlemCalendar) //static const int32_t JD_EPOCH_OFFSET_AMETE_ALEM = -285019; static const int32_t JD_EPOCH_OFFSET_AMETE_MIHRET = 1723856; @@ -29,16 +30,8 @@ static const int32_t AMETE_MIHRET_DELTA = 5500; // 5501 - 1 (Amete Alem 5501 = A //------------------------------------------------------------------------- EthiopicCalendar::EthiopicCalendar(const Locale& aLocale, - UErrorCode& success, - EEraType type /*= AMETE_MIHRET_ERA*/) -: CECalendar(aLocale, success), - eraType(type) -{ -} - -EthiopicCalendar::EthiopicCalendar(const EthiopicCalendar& other) -: CECalendar(other), - eraType(other.eraType) + UErrorCode& success) +: CECalendar(aLocale, success) { } @@ -55,24 +48,9 @@ EthiopicCalendar::clone() const const char * EthiopicCalendar::getType() const { - if (isAmeteAlemEra()) { - return "ethiopic-amete-alem"; - } return "ethiopic"; } -void -EthiopicCalendar::setAmeteAlemEra(UBool onOff) -{ - eraType = onOff ? AMETE_ALEM_ERA : AMETE_MIHRET_ERA; -} - -UBool -EthiopicCalendar::isAmeteAlemEra() const -{ - return (eraType == AMETE_ALEM_ERA); -} - //------------------------------------------------------------------------- // Calendar framework //------------------------------------------------------------------------- @@ -82,63 +60,31 @@ EthiopicCalendar::handleGetExtendedYear() { // Ethiopic calendar uses EXTENDED_YEAR aligned to // Amelete Hihret year always. - int32_t eyear; if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { - eyear = internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 - } else if (isAmeteAlemEra()) { - eyear = internalGet(UCAL_YEAR, 1 + AMETE_MIHRET_DELTA) - - AMETE_MIHRET_DELTA; // Default to year 1 of Amelete Mihret - } else { - // The year defaults to the epoch start, the era to AMETE_MIHRET - int32_t era = internalGet(UCAL_ERA, AMETE_MIHRET); - if (era == AMETE_MIHRET) { - eyear = internalGet(UCAL_YEAR, 1); // Default to year 1 - } else { - eyear = internalGet(UCAL_YEAR, 1) - AMETE_MIHRET_DELTA; - } + return internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 } - return eyear; + // The year defaults to the epoch start, the era to AMETE_MIHRET + if (internalGet(UCAL_ERA, AMETE_MIHRET) == AMETE_MIHRET) { + return internalGet(UCAL_YEAR, 1); // Default to year 1 + } + return internalGet(UCAL_YEAR, 1) - AMETE_MIHRET_DELTA; } void EthiopicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) { - int32_t eyear, month, day, era, year; + int32_t eyear, month, day; jdToCE(julianDay, getJDEpochOffset(), eyear, month, day); - if (isAmeteAlemEra()) { - era = AMETE_ALEM; - year = eyear + AMETE_MIHRET_DELTA; - } else { - if (eyear > 0) { - era = AMETE_MIHRET; - year = eyear; - } else { - era = AMETE_ALEM; - year = eyear + AMETE_MIHRET_DELTA; - } - } - internalSet(UCAL_EXTENDED_YEAR, eyear); - internalSet(UCAL_ERA, era); - internalSet(UCAL_YEAR, year); + internalSet(UCAL_ERA, (eyear > 0) ? AMETE_MIHRET : AMETE_ALEM); + internalSet(UCAL_YEAR, (eyear > 0) ? eyear : (eyear + AMETE_MIHRET_DELTA)); internalSet(UCAL_MONTH, month); internalSet(UCAL_DATE, day); internalSet(UCAL_DAY_OF_YEAR, (30 * month) + day); } -int32_t -EthiopicCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const -{ - if (isAmeteAlemEra() && field == UCAL_ERA) { - return 0; // Only one era in this mode, era is always 0 - } - return CECalendar::handleGetLimit(field, limitType); -} - - constexpr uint32_t kEthiopicRelatedYearDiff = 8; -constexpr uint32_t kEthiopicAmeteAlemRelatedYearDiff = -5492; int32_t EthiopicCalendar::getRelatedYear(UErrorCode &status) const { @@ -146,14 +92,13 @@ int32_t EthiopicCalendar::getRelatedYear(UErrorCode &status) const if (U_FAILURE(status)) { return 0; } - return year + (isAmeteAlemEra() ? kEthiopicAmeteAlemRelatedYearDiff : kEthiopicRelatedYearDiff); + return year + kEthiopicRelatedYearDiff; } void EthiopicCalendar::setRelatedYear(int32_t year) { // set extended year - set(UCAL_EXTENDED_YEAR, year - - (isAmeteAlemEra() ? kEthiopicAmeteAlemRelatedYearDiff : kEthiopicRelatedYearDiff)); + set(UCAL_EXTENDED_YEAR, year - kEthiopicRelatedYearDiff); } /** @@ -193,9 +138,6 @@ EthiopicCalendar::defaultCenturyStartYear() const { // lazy-evaluate systemDefaultCenturyStartYear umtx_initOnce(gSystemDefaultCenturyInit, &initializeSystemDefaultCentury); - if (isAmeteAlemEra()) { - return gSystemDefaultCenturyStartYear + AMETE_MIHRET_DELTA; - } return gSystemDefaultCenturyStartYear; } @@ -222,6 +164,94 @@ EthiopicCalendar::ethiopicToJD(int32_t year, int32_t month, int32_t date) } #endif +//------------------------------------------------------------------------- +// Constructors... +//------------------------------------------------------------------------- + +EthiopicAmeteAlemCalendar::EthiopicAmeteAlemCalendar(const Locale& aLocale, + UErrorCode& success) +: EthiopicCalendar(aLocale, success) +{ +} + +EthiopicAmeteAlemCalendar::~EthiopicAmeteAlemCalendar() +{ +} + +EthiopicAmeteAlemCalendar* +EthiopicAmeteAlemCalendar::clone() const +{ + return new EthiopicAmeteAlemCalendar(*this); +} + +//------------------------------------------------------------------------- +// Calendar framework +//------------------------------------------------------------------------- + +const char * +EthiopicAmeteAlemCalendar::getType() const +{ + return "ethiopic-amete-alem"; +} + +int32_t +EthiopicAmeteAlemCalendar::handleGetExtendedYear() +{ + // Ethiopic calendar uses EXTENDED_YEAR aligned to + // Amelete Hihret year always. + if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) { + return internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1 + } + return internalGet(UCAL_YEAR, 1 + AMETE_MIHRET_DELTA) + - AMETE_MIHRET_DELTA; // Default to year 1 of Amelete Mihret +} + +void +EthiopicAmeteAlemCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) +{ + int32_t eyear, month, day; + jdToCE(julianDay, getJDEpochOffset(), eyear, month, day); + + internalSet(UCAL_EXTENDED_YEAR, eyear); + internalSet(UCAL_ERA, AMETE_ALEM); + internalSet(UCAL_YEAR, eyear + AMETE_MIHRET_DELTA); + internalSet(UCAL_MONTH, month); + internalSet(UCAL_DATE, day); + internalSet(UCAL_DAY_OF_YEAR, (30 * month) + day); +} + +int32_t +EthiopicAmeteAlemCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType) const +{ + if (field == UCAL_ERA) { + return 0; // Only one era in this mode, era is always 0 + } + return EthiopicCalendar::handleGetLimit(field, limitType); +} + +constexpr uint32_t kEthiopicAmeteAlemRelatedYearDiff = -5492; + +int32_t EthiopicAmeteAlemCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kEthiopicAmeteAlemRelatedYearDiff; +} + +void EthiopicAmeteAlemCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kEthiopicAmeteAlemRelatedYearDiff); +} + +int32_t +EthiopicAmeteAlemCalendar::defaultCenturyStartYear() const +{ + return EthiopicCalendar::defaultCenturyStartYear() + AMETE_MIHRET_DELTA; +} + U_NAMESPACE_END #endif diff --git a/icu4c/source/i18n/ethpccal.h b/icu4c/source/i18n/ethpccal.h index 736ae1c565a..c5828d57706 100644 --- a/icu4c/source/i18n/ethpccal.h +++ b/icu4c/source/i18n/ethpccal.h @@ -26,15 +26,6 @@ U_NAMESPACE_BEGIN class EthiopicCalendar : public CECalendar { public: - /** - * Calendar type - use Amete Alem era for all the time or not - * @internal - */ - enum EEraType { - AMETE_MIHRET_ERA, - AMETE_ALEM_ERA - }; - /** * Useful constants for EthiopicCalendar. * @internal @@ -122,13 +113,13 @@ public: * only use Amete Alem for all the time. * @internal */ - EthiopicCalendar(const Locale& aLocale, UErrorCode& success, EEraType type = AMETE_MIHRET_ERA); + EthiopicCalendar(const Locale& aLocale, UErrorCode& success); /** * Copy Constructor * @internal */ - EthiopicCalendar(const EthiopicCalendar& other); + EthiopicCalendar(const EthiopicCalendar& other) = default; /** * Destructor. @@ -144,26 +135,12 @@ public: virtual EthiopicCalendar* clone() const override; /** - * return the calendar type, "ethiopic" + * Return the calendar type, "ethiopic" * @return calendar type * @internal */ virtual const char * getType() const override; - /** - * Set Alem or Mihret era. - * @param onOff Set Amete Alem era if true, otherwise set Amete Mihret era. - * @internal - */ - void setAmeteAlemEra (UBool onOff); - - /** - * Return true if this calendar is set to the Amete Alem era. - * @return true if set to the Amete Alem era. - * @internal - */ - UBool isAmeteAlemEra() const; - /** * @return The related Gregorian year; will be obtained by modifying the value * obtained by get from UCAL_EXTENDED_YEAR field @@ -185,6 +162,11 @@ protected: /** * Return the extended year defined by the current fields. + * This calendar uses both AMETE_ALEM and AMETE_MIHRET. + * + * EXTENDED_YEAR ERA YEAR + * 0 AMETE_ALEM 5500 + * 1 AMETE_MIHRET 1 * @internal */ virtual int32_t handleGetExtendedYear() override; @@ -195,12 +177,6 @@ protected: */ virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; - /** - * Calculate the limit for a specified type of limit and field - * @internal - */ - virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; - /** * Returns the date of the start of the default century * @return start of century - in milliseconds since epoch, 1970 @@ -220,18 +196,6 @@ protected: */ virtual int32_t getJDEpochOffset() const override; -private: - /** - * When eraType is AMETE_ALEM_ERA, then this calendar use only AMETE_ALEM - * for the era. Otherwise (default), this calendar uses both AMETE_ALEM - * and AMETE_MIHRET. - * - * EXTENDED_YEAR AMETE_ALEM_ERA AMETE_MIHRET_ERA - * 0 Amete Alem 5500 Amete Alem 5500 - * 1 Amete Mihret 1 Amete Alem 5501 - */ - EEraType eraType; - public: /** * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual @@ -280,6 +244,123 @@ public: #endif }; +/** + * Implement the Ethiopic Amete Alem calendar system. + * @internal + */ +class EthiopicAmeteAlemCalendar : public EthiopicCalendar { + +public: + /** + * Constructs a EthiopicAmeteAlemCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of EthiopicCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + EthiopicAmeteAlemCalendar(const Locale& aLocale, UErrorCode& success); + + /** + * Copy Constructor + * @internal + */ + EthiopicAmeteAlemCalendar(const EthiopicAmeteAlemCalendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~EthiopicAmeteAlemCalendar(); + + /** + * Create and return a polymorphic copy of this calendar. + * @return return a polymorphic copy of this calendar. + * @internal + */ + virtual EthiopicAmeteAlemCalendar* clone() const override; + + /** + * Return the calendar type, "ethiopic-amete-alem" + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + /** + * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure virtual + * override. This method is to implement a simple version of RTTI, since not all C++ + * compilers support genuine RTTI. Polymorphic operator==() and clone() methods call + * this method. + * + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID(void) const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(void); + + /** + * @return The related Gregorian year; will be obtained by modifying the value + * obtained by get from UCAL_EXTENDED_YEAR field + * @internal + */ + virtual int32_t getRelatedYear(UErrorCode &status) const override; + + /** + * @param year The related Gregorian year to set; will be modified as necessary then + * set in UCAL_EXTENDED_YEAR field + * @internal + */ + virtual void setRelatedYear(int32_t year) override; + +protected: + //------------------------------------------------------------------------- + // Calendar framework + //------------------------------------------------------------------------- + + /** + * Return the extended year defined by the current fields. + * This calendar use only AMETE_ALEM for the era. + * + * EXTENDED_YEAR ERA YEAR + * 0 AMETE_ALEM 5500 + * 1 AMETE_ALEM 5501 + * @internal + */ + virtual int32_t handleGetExtendedYear() override; + + /** + * Compute fields from the JD + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; + + /** + * Calculate the limit for a specified type of limit and field + * @internal + */ + virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType limitType) const override; + /** + * Returns the year in which the default century begins + * @internal + */ + virtual int32_t defaultCenturyStartYear() const override; +}; + U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ #endif /* ETHPCCAL_H */ diff --git a/icu4c/source/i18n/i18n.vcxproj b/icu4c/source/i18n/i18n.vcxproj index 60bbe0f89ee..eef2dd190e6 100644 --- a/icu4c/source/i18n/i18n.vcxproj +++ b/icu4c/source/i18n/i18n.vcxproj @@ -166,6 +166,7 @@ + @@ -384,6 +385,7 @@ + diff --git a/icu4c/source/i18n/i18n.vcxproj.filters b/icu4c/source/i18n/i18n.vcxproj.filters index 74fd8c48614..d2175c7c768 100644 --- a/icu4c/source/i18n/i18n.vcxproj.filters +++ b/icu4c/source/i18n/i18n.vcxproj.filters @@ -186,6 +186,9 @@ formatting + + formatting + formatting @@ -851,6 +854,9 @@ formatting + + formatting + formatting diff --git a/icu4c/source/i18n/i18n_uwp.vcxproj b/icu4c/source/i18n/i18n_uwp.vcxproj index 7ce4ae64f96..25c6c333d7b 100644 --- a/icu4c/source/i18n/i18n_uwp.vcxproj +++ b/icu4c/source/i18n/i18n_uwp.vcxproj @@ -399,6 +399,7 @@ + @@ -615,6 +616,7 @@ + diff --git a/icu4c/source/i18n/islamcal.cpp b/icu4c/source/i18n/islamcal.cpp index 54b2a97ff49..2b6d32210be 100644 --- a/icu4c/source/i18n/islamcal.cpp +++ b/icu4c/source/i18n/islamcal.cpp @@ -206,67 +206,22 @@ int32_t getUmalqura_MonthLength(int32_t y, int32_t m) { //------------------------------------------------------------------------- const char *IslamicCalendar::getType() const { - const char *sType = NULL; - - switch (cType) { - case CIVIL: - sType = "islamic-civil"; - break; - case ASTRONOMICAL: - sType = "islamic"; - break; - case TBLA: - sType = "islamic-tbla"; - break; - case UMALQURA: - sType = "islamic-umalqura"; - break; - default: - UPRV_UNREACHABLE_EXIT; // out of range - } - return sType; + return "islamic"; } IslamicCalendar* IslamicCalendar::clone() const { return new IslamicCalendar(*this); } -IslamicCalendar::IslamicCalendar(const Locale& aLocale, UErrorCode& success, ECalculationType type) -: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success), -cType(type) +IslamicCalendar::IslamicCalendar(const Locale& aLocale, UErrorCode& success) +: Calendar(TimeZone::forLocaleOrDefault(aLocale), aLocale, success) { setTimeInMillis(getNow(), success); // Call this again now that the vtable is set up properly. } -IslamicCalendar::IslamicCalendar(const IslamicCalendar& other) : Calendar(other), cType(other.cType) { -} - IslamicCalendar::~IslamicCalendar() { } - -void IslamicCalendar::setCalculationType(ECalculationType type, UErrorCode &status) -{ - if (cType != type) { - // The fields of the calendar will become invalid, because the calendar - // rules are different - UDate m = getTimeInMillis(status); - cType = type; - clear(); - setTimeInMillis(m, status); - } -} - -/** -* Returns true if this object is using the fixed-cycle civil -* calendar, or false if using the religious, astronomical -* calendar. -* @draft ICU 2.4 -*/ -UBool IslamicCalendar::isCivil() { - return (cType == CIVIL); -} - //------------------------------------------------------------------------- // Minimum / Maximum access functions //------------------------------------------------------------------------- @@ -365,19 +320,7 @@ UBool IslamicCalendar::civilLeapYear(int32_t year) * from the Hijri epoch, origin 0. */ int32_t IslamicCalendar::yearStart(int32_t year) const{ - if (cType == CIVIL || cType == TBLA || - (cType == UMALQURA && (year < UMALQURA_YEAR_START || year > UMALQURA_YEAR_END))) - { - return (year-1)*354 + ClockMath::floorDivide((3+11*(int64_t)year),(int64_t)30); - } else if(cType==ASTRONOMICAL){ - return trueMonthStart(12*(year-1)); - } else { - year -= UMALQURA_YEAR_START; - // rounded least-squares fit of the dates previously calculated from UMALQURA_MONTHLENGTH iteration - int32_t yrStartLinearEstimate = (int32_t)((354.36720 * (double)year) + 460322.05 + 0.5); - // need a slight correction to some - return yrStartLinearEstimate + umAlQuraYrStartEstimateFix[year]; - } + return trueMonthStart(12*(year-1)); } /** @@ -388,19 +331,7 @@ int32_t IslamicCalendar::yearStart(int32_t year) const{ * @param month The hijri month, 0-based (assumed to be in range 0..11) */ int32_t IslamicCalendar::monthStart(int32_t year, int32_t month) const { - if (cType == CIVIL || cType == TBLA) { - // This does not handle months out of the range 0..11 - return (int32_t)uprv_ceil(29.5*month) - + (year-1)*354 + (int32_t)ClockMath::floorDivide((3+11*(int64_t)year),(int64_t)30); - } else if(cType==ASTRONOMICAL){ - return trueMonthStart(12*(year-1) + month); - } else { - int32_t ms = yearStart(year); - for(int i=0; i< month; i++){ - ms+= handleGetMonthLength(year, i); - } - return ms; - } + return trueMonthStart(12*(year-1) + month); } /** @@ -506,22 +437,8 @@ double IslamicCalendar::moonAge(UDate time, UErrorCode &status) * @draft ICU 2.4 */ int32_t IslamicCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { - - int32_t length = 0; - - if (cType == CIVIL || cType == TBLA || - (cType == UMALQURA && (extendedYearUMALQURA_YEAR_END)) ) { - length = 29 + (month+1) % 2; - if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) { - length++; - } - } else if(cType == ASTRONOMICAL){ - month = 12*(extendedYear-1) + month; - length = trueMonthStart(month+1) - trueMonthStart(month) ; - } else { - length = getUmalqura_MonthLength(extendedYear - UMALQURA_YEAR_START, month); - } - return length; + month = 12*(extendedYear-1) + month; + return trueMonthStart(month+1) - trueMonthStart(month) ; } /** @@ -529,19 +446,8 @@ int32_t IslamicCalendar::handleGetMonthLength(int32_t extendedYear, int32_t mont * @draft ICU 2.4 */ int32_t IslamicCalendar::handleGetYearLength(int32_t extendedYear) const { - if (cType == CIVIL || cType == TBLA || - (cType == UMALQURA && (extendedYearUMALQURA_YEAR_END)) ) { - return 354 + (civilLeapYear(extendedYear) ? 1 : 0); - } else if(cType == ASTRONOMICAL){ - int32_t month = 12*(extendedYear-1); - return (trueMonthStart(month + 12) - trueMonthStart(month)); - } else { - int len = 0; - for(int i=0; i<12; i++) { - len += handleGetMonthLength(extendedYear, i); - } - return len; - } + int32_t month = 12*(extendedYear-1); + return (trueMonthStart(month + 12) - trueMonthStart(month)); } //------------------------------------------------------------------------- @@ -567,8 +473,8 @@ int32_t IslamicCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, U eyear += (month / 12) - 1; month = (month % 12) + 11; } - return monthStart(eyear, month) + ((cType == TBLA)? ASTRONOMICAL_EPOC: CIVIL_EPOC) - 1; -} + return monthStart(eyear, month) + getEpoc() - 1; +} //------------------------------------------------------------------------- // Functions for converting from milliseconds to field values @@ -604,93 +510,49 @@ int32_t IslamicCalendar::handleGetExtendedYear() { * @draft ICU 2.4 */ void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { - int32_t year, month, dayOfMonth, dayOfYear; - int32_t startDate; - int32_t days = julianDay - CIVIL_EPOC; + if (U_FAILURE(status)) return; + int32_t days = julianDay - getEpoc(); - if (cType == CIVIL || cType == TBLA) { - if(cType == TBLA) { - days = julianDay - ASTRONOMICAL_EPOC; - } - // Use the civil calendar approximation, which is just arithmetic - year = (int32_t)ClockMath::floorDivide(30 * (int64_t)days + 10646, (int64_t)10631); - month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 ); - month = month<11?month:11; - startDate = monthStart(year, month); - } else if(cType == ASTRONOMICAL){ - // Guess at the number of elapsed full months since the epoch - int32_t months = (int32_t)uprv_floor((double)days / CalendarAstronomer::SYNODIC_MONTH); + // Guess at the number of elapsed full months since the epoch + int32_t month = (int32_t)uprv_floor((double)days / CalendarAstronomer::SYNODIC_MONTH); - startDate = (int32_t)uprv_floor(months * CalendarAstronomer::SYNODIC_MONTH); + int32_t startDate = (int32_t)uprv_floor(month * CalendarAstronomer::SYNODIC_MONTH); - double age = moonAge(internalGetTime(), status); - if (U_FAILURE(status)) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - if ( days - startDate >= 25 && age > 0) { - // If we're near the end of the month, assume next month and search backwards - months++; - } - - // Find out the last time that the new moon was actually visible at this longitude - // This returns midnight the night that the moon was visible at sunset. - while ((startDate = trueMonthStart(months)) > days) { - // If it was after the date in question, back up a month and try again - months--; - } + double age = moonAge(internalGetTime(), status); + if (U_FAILURE(status)) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if ( days - startDate >= 25 && age > 0) { + // If we're near the end of the month, assume next month and search backwards + month++; + } - year = months >= 0 ? ((months / 12) + 1) : ((months + 1 ) / 12); - month = ((months % 12) + 12 ) % 12; - } else if(cType == UMALQURA) { - int32_t umalquraStartdays = yearStart(UMALQURA_YEAR_START) ; - if( days < umalquraStartdays){ - //Use Civil calculation - year = (int32_t)ClockMath::floorDivide( - (30 * (int64_t)days + 10646) , (int64_t)10631.0 ); - month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 ); - month = month<11?month:11; - startDate = monthStart(year, month); - }else{ - int y =UMALQURA_YEAR_START-1, m =0; - long d = 1; - while(d > 0){ - y++; - d = days - yearStart(y) +1; - if(d == handleGetYearLength(y)){ - m=11; - break; - }else if(d < handleGetYearLength(y) ){ - int monthLen = handleGetMonthLength(y, m); - m=0; - while(d > monthLen){ - d -= monthLen; - m++; - monthLen = handleGetMonthLength(y, m); - } - break; - } - } - year = y; - month = m; - } - } else { // invalid 'civil' - UPRV_UNREACHABLE_EXIT; // should not get here, out of range + // Find out the last time that the new moon was actually visible at this longitude + // This returns midnight the night that the moon was visible at sunset. + while ((startDate = trueMonthStart(month)) > days) { + // If it was after the date in question, back up a month and try again + month--; } - dayOfMonth = (days - monthStart(year, month)) + 1; + int32_t year = month >= 0 ? ((month / 12) + 1) : ((month + 1 ) / 12); + month = ((month % 12) + 12 ) % 12; + int32_t dayOfMonth = (days - monthStart(year, month)) + 1; // Now figure out the day of the year. - dayOfYear = (days - monthStart(year, 0)) + 1; - + int32_t dayOfYear = (days - monthStart(year, 0)) + 1; internalSet(UCAL_ERA, 0); internalSet(UCAL_YEAR, year); internalSet(UCAL_EXTENDED_YEAR, year); internalSet(UCAL_MONTH, month); internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); - internalSet(UCAL_DAY_OF_YEAR, dayOfYear); -} + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); +} + +int32_t IslamicCalendar::getEpoc() const { + return CIVIL_EPOC; +} static int32_t gregoYearFromIslamicStart(int32_t year) { // ad hoc conversion, improve under #10752 @@ -789,9 +651,315 @@ IslamicCalendar::initializeSystemDefaultCentury() // out. } +/***************************************************************************** + * IslamicCivilCalendar + *****************************************************************************/ +IslamicCivilCalendar::IslamicCivilCalendar(const Locale& aLocale, UErrorCode& success) + : IslamicCalendar(aLocale, success) +{ +} + +IslamicCivilCalendar::~IslamicCivilCalendar() +{ +} + +const char *IslamicCivilCalendar::getType() const { + return "islamic-civil"; +} + +IslamicCivilCalendar* IslamicCivilCalendar::clone() const { + return new IslamicCivilCalendar(*this); +} + +/** +* Return the day # on which the given year starts. Days are counted +* from the Hijri epoch, origin 0. +*/ +int32_t IslamicCivilCalendar::yearStart(int32_t year) const{ + return static_cast( + (year-1)*354 + ClockMath::floorDivide((3+11*static_cast(year)), + static_cast(30))); +} + +/** +* Return the day # on which the given month starts. Days are counted +* from the Hijri epoch, origin 0. +* +* @param year The hijri year +* @param month The hijri month, 0-based (assumed to be in range 0..11) +*/ +int32_t IslamicCivilCalendar::monthStart(int32_t year, int32_t month) const { + // This does not handle months out of the range 0..11 + return static_cast( + uprv_ceil(29.5*month) + (year-1)*354 + + static_cast(ClockMath::floorDivide( + 3+11*static_cast(year), + static_cast(30)))); +} + +/** +* Return the length (in days) of the given month. +* +* @param year The hijri year +* @param year The hijri month, 0-based +* @draft ICU 2.4 +*/ +int32_t IslamicCivilCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { + int32_t length = 29 + (month+1) % 2; + if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) { + length++; + } + return length; +} + +/** +* Return the number of days in the given Islamic year +* @draft ICU 2.4 +*/ +int32_t IslamicCivilCalendar::handleGetYearLength(int32_t extendedYear) const { + return 354 + (civilLeapYear(extendedYear) ? 1 : 0); +} + +/** +* Override Calendar to compute several fields specific to the Islamic +* calendar system. These are: +* +*
  • ERA +*
  • YEAR +*
  • MONTH +*
  • DAY_OF_MONTH +*
  • DAY_OF_YEAR +*
  • EXTENDED_YEAR
+* +* The DAY_OF_WEEK and DOW_LOCAL fields are already set when this +* method is called. The getGregorianXxx() methods return Gregorian +* calendar equivalents for the given Julian day. +* @draft ICU 2.4 +*/ +void IslamicCivilCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { + if (U_FAILURE(status)) return; + int32_t days = julianDay - getEpoc(); + + // Use the civil calendar approximation, which is just arithmetic + int32_t year = static_cast( + ClockMath::floorDivide(30 * static_cast(days) + 10646, + static_cast(10631))); + int32_t month = static_cast( + uprv_ceil((days - 29 - yearStart(year)) / 29.5 )); + month = month<11?month:11; + + int32_t dayOfMonth = (days - monthStart(year, month)) + 1; + + // Now figure out the day of the year. + int32_t dayOfYear = (days - monthStart(year, 0)) + 1; + + internalSet(UCAL_ERA, 0); + internalSet(UCAL_YEAR, year); + internalSet(UCAL_EXTENDED_YEAR, year); + internalSet(UCAL_MONTH, month); + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); +} +/***************************************************************************** + * IslamicTBLACalendar + *****************************************************************************/ +IslamicTBLACalendar::IslamicTBLACalendar(const Locale& aLocale, UErrorCode& success) + : IslamicCivilCalendar(aLocale, success) +{ +} + +IslamicTBLACalendar::~IslamicTBLACalendar() +{ +} + +const char *IslamicTBLACalendar::getType() const { + return "islamic-tbla"; +} + +IslamicTBLACalendar* IslamicTBLACalendar::clone() const { + return new IslamicTBLACalendar(*this); +} + +int32_t IslamicTBLACalendar::getEpoc() const { + return ASTRONOMICAL_EPOC; +} + +/***************************************************************************** + * IslamicUmalquraCalendar + *****************************************************************************/ +IslamicUmalquraCalendar::IslamicUmalquraCalendar(const Locale& aLocale, UErrorCode& success) + : IslamicCalendar(aLocale, success) +{ +} + +IslamicUmalquraCalendar::~IslamicUmalquraCalendar() +{ +} + +const char *IslamicUmalquraCalendar::getType() const { + return "islamic-umalqura"; +} + +IslamicUmalquraCalendar* IslamicUmalquraCalendar::clone() const { + return new IslamicUmalquraCalendar(*this); +} + +/** +* Return the day # on which the given year starts. Days are counted +* from the Hijri epoch, origin 0. +*/ +int32_t IslamicUmalquraCalendar::yearStart(int32_t year) const { + if (year < UMALQURA_YEAR_START || year > UMALQURA_YEAR_END) { + return static_cast( + (year-1)*354 + ClockMath::floorDivide((3+11*static_cast(year)), + static_cast(30))); + } + year -= UMALQURA_YEAR_START; + // rounded least-squares fit of the dates previously calculated from UMALQURA_MONTHLENGTH iteration + int32_t yrStartLinearEstimate = static_cast( + (354.36720 * (double)year) + 460322.05 + 0.5); + // need a slight correction to some + return yrStartLinearEstimate + umAlQuraYrStartEstimateFix[year]; +} +/** +* Return the day # on which the given month starts. Days are counted +* from the Hijri epoch, origin 0. +* +* @param year The hijri year +* @param month The hijri month, 0-based (assumed to be in range 0..11) +*/ +int32_t IslamicUmalquraCalendar::monthStart(int32_t year, int32_t month) const { + int32_t ms = yearStart(year); + for(int i=0; i< month; i++){ + ms+= handleGetMonthLength(year, i); + } + return ms; +} + +/** +* Return the length (in days) of the given month. +* +* @param year The hijri year +* @param year The hijri month, 0-based +*/ +int32_t IslamicUmalquraCalendar::handleGetMonthLength(int32_t extendedYear, int32_t month) const { + int32_t length = 0; + if (extendedYearUMALQURA_YEAR_END) { + length = 29 + (month+1) % 2; + if (month == DHU_AL_HIJJAH && civilLeapYear(extendedYear)) { + length++; + } + return length; + } + return getUmalqura_MonthLength(extendedYear - UMALQURA_YEAR_START, month); +} + +/** +* Return the number of days in the given Islamic year +* @draft ICU 2.4 +*/ +int32_t IslamicUmalquraCalendar::handleGetYearLength(int32_t extendedYear) const { + if (extendedYearUMALQURA_YEAR_END) { + return 354 + (civilLeapYear(extendedYear) ? 1 : 0); + } + int len = 0; + for(int i=0; i<12; i++) { + len += handleGetMonthLength(extendedYear, i); + } + return len; +} + +/** +* Override Calendar to compute several fields specific to the Islamic +* calendar system. These are: +* +*
  • ERA +*
  • YEAR +*
  • MONTH +*
  • DAY_OF_MONTH +*
  • DAY_OF_YEAR +*
  • EXTENDED_YEAR
+* +* The DAY_OF_WEEK and DOW_LOCAL fields are already set when this +* method is called. The getGregorianXxx() methods return Gregorian +* calendar equivalents for the given Julian day. +* @draft ICU 2.4 +*/ +void IslamicUmalquraCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) { + if (U_FAILURE(status)) return; + int32_t year, month, dayOfMonth, dayOfYear; + int32_t days = julianDay - getEpoc(); + + int32_t umalquraStartdays = yearStart(UMALQURA_YEAR_START) ; + if (days < umalquraStartdays) { + //Use Civil calculation + year = (int32_t)ClockMath::floorDivide( + (30 * (int64_t)days + 10646) , (int64_t)10631.0 ); + month = (int32_t)uprv_ceil((days - 29 - yearStart(year)) / 29.5 ); + month = month < 11 ? month : 11; + } else { + int y =UMALQURA_YEAR_START-1, m =0; + long d = 1; + while (d > 0) { + y++; + d = days - yearStart(y) +1; + if (d == handleGetYearLength(y)) { + m=11; + break; + } + if (d < handleGetYearLength(y)){ + int monthLen = handleGetMonthLength(y, m); + m=0; + while(d > monthLen){ + d -= monthLen; + m++; + monthLen = handleGetMonthLength(y, m); + } + break; + } + } + year = y; + month = m; + } + + dayOfMonth = (days - monthStart(year, month)) + 1; + + // Now figure out the day of the year. + dayOfYear = (days - monthStart(year, 0)) + 1; + + internalSet(UCAL_ERA, 0); + internalSet(UCAL_YEAR, year); + internalSet(UCAL_EXTENDED_YEAR, year); + internalSet(UCAL_MONTH, month); + internalSet(UCAL_DAY_OF_MONTH, dayOfMonth); + internalSet(UCAL_DAY_OF_YEAR, dayOfYear); +} +/***************************************************************************** + * IslamicRGSACalendar + *****************************************************************************/ +IslamicRGSACalendar::IslamicRGSACalendar(const Locale& aLocale, UErrorCode& success) + : IslamicCalendar(aLocale, success) +{ +} + +IslamicRGSACalendar::~IslamicRGSACalendar() +{ +} + +const char *IslamicRGSACalendar::getType() const { + return "islamic-rgsa"; +} + +IslamicRGSACalendar* IslamicRGSACalendar::clone() const { + return new IslamicRGSACalendar(*this); +} UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicCalendar) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicCivilCalendar) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicUmalquraCalendar) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicTBLACalendar) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IslamicRGSACalendar) U_NAMESPACE_END diff --git a/icu4c/source/i18n/islamcal.h b/icu4c/source/i18n/islamcal.h index 5ff212485e0..6d7c0503a2c 100644 --- a/icu4c/source/i18n/islamcal.h +++ b/icu4c/source/i18n/islamcal.h @@ -53,7 +53,8 @@ U_NAMESPACE_BEGIN * every 30 years. This calendar is easily calculated and thus predictable in * advance, so it is used as the civil calendar in a number of Arab countries. * This is the default behavior of a newly-created IslamicCalendar - * object. + * object. This calendar variant is implemented in the IslamicCivilCalendar + * class. *

* The Islamic religious calendar, however, is based on the observation * of the crescent moon. It is thus affected by the position at which the @@ -71,10 +72,6 @@ U_NAMESPACE_BEGIN * calculations. At present, the approximations used in this class are fairly * simplistic; they will be improved in later versions of the code. *

- * The {@link #setCivil setCivil} method determines - * which approach is used to determine the start of a month. By default, the - * fixed-cycle civil calendar is used. However, if setCivil(false) - * is called, an approximation of the true lunar calendar will be used. * * @see GregorianCalendar * @@ -88,18 +85,6 @@ class U_I18N_API IslamicCalendar : public Calendar { //------------------------------------------------------------------------- // Constants... //------------------------------------------------------------------------- - - /** - * Calendar type - civil or religious or um alqura - * @internal - */ - enum ECalculationType { - ASTRONOMICAL, - CIVIL, - UMALQURA, - TBLA - }; - /** * Constants for the months * @internal @@ -192,16 +177,15 @@ class U_I18N_API IslamicCalendar : public Calendar { * @param aLocale The given locale. * @param success Indicates the status of IslamicCalendar object construction. * Returns U_ZERO_ERROR if constructed successfully. - * @param type The Islamic calendar calculation type. The default value is CIVIL. * @internal */ - IslamicCalendar(const Locale& aLocale, UErrorCode &success, ECalculationType type = CIVIL); + IslamicCalendar(const Locale& aLocale, UErrorCode &success); /** * Copy Constructor * @internal */ - IslamicCalendar(const IslamicCalendar& other); + IslamicCalendar(const IslamicCalendar& other) = default; /** * Destructor. @@ -209,40 +193,20 @@ class U_I18N_API IslamicCalendar : public Calendar { */ virtual ~IslamicCalendar(); - /** - * Sets Islamic calendar calculation type used by this instance. - * - * @param type The calendar calculation type, CIVIL to use the civil - * calendar, ASTRONOMICAL to use the astronomical calendar. - * @internal - */ - void setCalculationType(ECalculationType type, UErrorCode &status); - - /** - * Returns true if this object is using the fixed-cycle civil - * calendar, or false if using the religious, astronomical - * calendar. - * @internal - */ - UBool isCivil(); - - - // TODO: copy c'tor, etc - // clone virtual IslamicCalendar* clone() const override; - private: + protected: /** * Determine whether a year is a leap year in the Islamic civil calendar */ static UBool civilLeapYear(int32_t year); - + /** * Return the day # on which the given year starts. Days are counted * from the Hijri epoch, origin 0. */ - int32_t yearStart(int32_t year) const; + virtual int32_t yearStart(int32_t year) const; /** * Return the day # on which the given month starts. Days are counted @@ -251,7 +215,7 @@ class U_I18N_API IslamicCalendar : public Calendar { * @param year The hijri year * @param year The hijri month, 0-based */ - int32_t monthStart(int32_t year, int32_t month) const; + virtual int32_t monthStart(int32_t year, int32_t month) const; /** * Find the day number on which a particular month of the true/lunar @@ -263,6 +227,7 @@ class U_I18N_API IslamicCalendar : public Calendar { */ int32_t trueMonthStart(int32_t month) const; + private: /** * Return the "age" of the moon at the given time; this is the difference * in ecliptic latitude between the moon and the sun. This method simply @@ -274,17 +239,6 @@ class U_I18N_API IslamicCalendar : public Calendar { */ static double moonAge(UDate time, UErrorCode &status); - //------------------------------------------------------------------------- - // Internal data.... - // - - /** - * CIVIL if this object uses the fixed-cycle Islamic civil calendar, - * and ASTRONOMICAL if it approximates the true religious calendar using - * astronomical calculations for the time of the new moon. - */ - ECalculationType cType; - //---------------------------------------------------------------------- // Calendar framework //---------------------------------------------------------------------- @@ -346,6 +300,12 @@ class U_I18N_API IslamicCalendar : public Calendar { */ virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; + /** + * Return the epoc. + * @internal + */ + virtual int32_t getEpoc() const; + // UObject stuff public: /** @@ -369,7 +329,7 @@ class U_I18N_API IslamicCalendar : public Calendar { /*U_I18N_API*/ static UClassID U_EXPORT2 getStaticClassID(void); /** - * return the calendar type, "buddhist". + * return the calendar type, "islamic". * * @return calendar type * @internal @@ -423,10 +383,372 @@ class U_I18N_API IslamicCalendar : public Calendar { static void U_CALLCONV initializeSystemDefaultCentury(void); }; -U_NAMESPACE_END +/* + * IslamicCivilCalendar is one of the two main variants of the Islamic calendar. + * The civil calendar, which uses a fixed cycle of alternating 29- + * and 30-day months, with a leap day added to the last month of 11 out of + * every 30 years. This calendar is easily calculated and thus predictable in + * advance, so it is used as the civil calendar in a number of Arab countries. + * This calendar is referring as "Islamic calendar, tabular (intercalary years + * [2,5,7,10,13,16,18,21,24,26,29]- civil epoch" in CLDR. + */ +class U_I18N_API IslamicCivilCalendar : public IslamicCalendar { + public: + /** + * Constructs an IslamicCivilCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of IslamicCivilCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + IslamicCivilCalendar(const Locale& aLocale, UErrorCode &success); -#endif -#endif + /** + * Copy Constructor + * @internal + */ + IslamicCivilCalendar(const IslamicCivilCalendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~IslamicCivilCalendar(); + + // clone + virtual IslamicCivilCalendar* clone() const override; + + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID(void) const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID U_EXPORT2 getStaticClassID(void); + + /** + * return the calendar type, "islamic-civil". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + protected: + /** + * Return the day # on which the given year starts. Days are counted + * from the Hijri epoch, origin 0. + * @internal + */ + virtual int32_t yearStart(int32_t year) const override; + + /** + * Return the day # on which the given month starts. Days are counted + * from the Hijri epoch, origin 0. + * + * @param year The hijri year + * @param year The hijri month, 0-based + * @internal + */ + virtual int32_t monthStart(int32_t year, int32_t month) const override; + /** + * Return the length (in days) of the given month. + * + * @param year The hijri year + * @param year The hijri month, 0-based + * @internal + */ + virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; + /** + * Return the number of days in the given Islamic year + * @internal + */ + virtual int32_t handleGetYearLength(int32_t extendedYear) const override; + + /** + * Override Calendar to compute several fields specific to the Islamic + * calendar system. These are: + * + *

  • ERA + *
  • YEAR + *
  • MONTH + *
  • DAY_OF_MONTH + *
  • DAY_OF_YEAR + *
  • EXTENDED_YEAR
+ * + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this + * method is called. The getGregorianXxx() methods return Gregorian + * calendar equivalents for the given Julian day. + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; +}; + +/* + * IslamicTBLACalendar calendar. + * This is a subclass of IslamicCivilCalendar. The only differences in the + * calendar math is it uses different epoch. + * This calendar is referring as "Islamic calendar, tabular (intercalary years + * [2,5,7,10,13,16,18,21,24,26,29] - astronomical epoch" in CLDR. + */ +class U_I18N_API IslamicTBLACalendar : public IslamicCivilCalendar { + public: + /** + * Constructs an IslamicTBLACalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of IslamicTBLACalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + IslamicTBLACalendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + IslamicTBLACalendar(const IslamicTBLACalendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~IslamicTBLACalendar(); + + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID(void) const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID U_EXPORT2 getStaticClassID(void); + /** + * return the calendar type, "islamic-tbla". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + // clone + virtual IslamicTBLACalendar* clone() const override; + + protected: + /** + * Return the epoc. + * @internal + */ + virtual int32_t getEpoc() const override; +}; + +/* + * IslamicUmalquraCalendar + * This calendar is referred as "Islamic calendar, Umm al-Qura" in CLDR. + */ +class U_I18N_API IslamicUmalquraCalendar : public IslamicCalendar { + public: + /** + * Constructs an IslamicUmalquraCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of IslamicUmalquraCalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + IslamicUmalquraCalendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + IslamicUmalquraCalendar(const IslamicUmalquraCalendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~IslamicUmalquraCalendar(); + + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID(void) const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID U_EXPORT2 getStaticClassID(void); + + /** + * return the calendar type, "islamic-umalqura". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + // clone + virtual IslamicUmalquraCalendar* clone() const override; + + protected: + /** + * Return the day # on which the given year starts. Days are counted + * from the Hijri epoch, origin 0. + * @internal + */ + virtual int32_t yearStart(int32_t year) const override; + + /** + * Return the day # on which the given month starts. Days are counted + * from the Hijri epoch, origin 0. + * + * @param year The hijri year + * @param year The hijri month, 0-based + * @internal + */ + virtual int32_t monthStart(int32_t year, int32_t month) const override; + + /** + * Return the length (in days) of the given month. + * + * @param year The hijri year + * @param year The hijri month, 0-based + * @internal + */ + virtual int32_t handleGetMonthLength(int32_t extendedYear, int32_t month) const override; + + /** + * Return the number of days in the given Islamic year + * @internal + */ + virtual int32_t handleGetYearLength(int32_t extendedYear) const override; + + /** + * Override Calendar to compute several fields specific to the Islamic + * calendar system. These are: + * + *
  • ERA + *
  • YEAR + *
  • MONTH + *
  • DAY_OF_MONTH + *
  • DAY_OF_YEAR + *
  • EXTENDED_YEAR
+ * + * The DAY_OF_WEEK and DOW_LOCAL fields are already set when this + * method is called. The getGregorianXxx() methods return Gregorian + * calendar equivalents for the given Julian day. + * @internal + */ + virtual void handleComputeFields(int32_t julianDay, UErrorCode &status) override; +}; + + +/* + * IslamicRGSACalendar + * Islamic calendar, Saudi Arabia sighting. Since the calendar depends on the + * sighting, it is impossible to implement by algorithm ahead of time. It is + * currently identical to IslamicCalendar except the getType will return + * "islamic-rgsa". + */ +class U_I18N_API IslamicRGSACalendar : public IslamicCalendar { + public: + /** + * Constructs an IslamicRGSACalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of IslamicRGSACalendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + IslamicRGSACalendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + IslamicRGSACalendar(const IslamicRGSACalendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~IslamicRGSACalendar(); + + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID(void) const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + static UClassID U_EXPORT2 getStaticClassID(void); + + /** + * return the calendar type, "islamic-rgsa". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + // clone + virtual IslamicRGSACalendar* clone() const override; +}; + +U_NAMESPACE_END + +#endif +#endif diff --git a/icu4c/source/i18n/iso8601cal.cpp b/icu4c/source/i18n/iso8601cal.cpp new file mode 100644 index 00000000000..1bc81fac15e --- /dev/null +++ b/icu4c/source/i18n/iso8601cal.cpp @@ -0,0 +1,37 @@ +// © 2022 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "iso8601cal.h" +#include "unicode/gregocal.h" + +U_NAMESPACE_BEGIN + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ISO8601Calendar) + +ISO8601Calendar::ISO8601Calendar(const Locale& aLocale, UErrorCode& success) +: GregorianCalendar(aLocale, success) +{ + setFirstDayOfWeek(UCAL_MONDAY); + setMinimalDaysInFirstWeek(4); +} + +ISO8601Calendar::~ISO8601Calendar() +{ +} + +ISO8601Calendar* ISO8601Calendar::clone() const +{ + return new ISO8601Calendar(*this); +} + +const char *ISO8601Calendar::getType() const +{ + return "iso8601"; +} + +U_NAMESPACE_END + +#endif diff --git a/icu4c/source/i18n/iso8601cal.h b/icu4c/source/i18n/iso8601cal.h new file mode 100644 index 00000000000..48dff5a3b44 --- /dev/null +++ b/icu4c/source/i18n/iso8601cal.h @@ -0,0 +1,102 @@ +// © 2022 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +#ifndef ISO8601CAL_H +#define ISO8601CAL_H + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/calendar.h" +#include "unicode/gregocal.h" +#include "unicode/timezone.h" + +U_NAMESPACE_BEGIN + +/** + * Concrete class which provides the ISO8601 calendar. + *

+ * ISO8601Calendar is a subclass of GregorianCalendar + * that the first day of a week is Monday and the minimal days in the first + * week of a year or month is four days. + *

+ * The ISO8601 calendar is identical to the Gregorian calendar in all respects + * except for the first day of week and the minimal days in the first week + * of a year. + * @internal + */ +class ISO8601Calendar : public GregorianCalendar { + public: + //------------------------------------------------------------------------- + // Constructors... + //------------------------------------------------------------------------- + + /** + * Constructs a DangiCalendar based on the current time in the default time zone + * with the given locale. + * + * @param aLocale The given locale. + * @param success Indicates the status of ISO8601Calendar object construction. + * Returns U_ZERO_ERROR if constructed successfully. + * @internal + */ + ISO8601Calendar(const Locale& aLocale, UErrorCode &success); + + /** + * Copy Constructor + * @internal + */ + ISO8601Calendar(const ISO8601Calendar& other) = default; + + /** + * Destructor. + * @internal + */ + virtual ~ISO8601Calendar(); + + /** + * Clone. + * @internal + */ + virtual ISO8601Calendar* clone() const override; + + // UObject stuff + public: + /** + * @return The class ID for this object. All objects of a given class have the + * same class ID. Objects of other classes have different class IDs. + * @internal + */ + virtual UClassID getDynamicClassID(void) const override; + + /** + * Return the class ID for this class. This is useful only for comparing to a return + * value from getDynamicClassID(). For example: + * + * Base* polymorphic_pointer = createPolymorphicObject(); + * if (polymorphic_pointer->getDynamicClassID() == + * Derived::getStaticClassID()) ... + * + * @return The class ID for all objects of this class. + * @internal + */ + U_I18N_API static UClassID U_EXPORT2 getStaticClassID(void); + + /** + * return the calendar type, "iso8601". + * + * @return calendar type + * @internal + */ + virtual const char * getType() const override; + + + private: + + ISO8601Calendar(); // default constructor not implemented +}; + +U_NAMESPACE_END + +#endif +#endif diff --git a/icu4c/source/i18n/sources.txt b/icu4c/source/i18n/sources.txt index b882b0464a9..69705c87b4e 100644 --- a/icu4c/source/i18n/sources.txt +++ b/icu4c/source/i18n/sources.txt @@ -86,6 +86,7 @@ hebrwcal.cpp indiancal.cpp inputext.cpp islamcal.cpp +iso8601cal.cpp japancal.cpp listformatter.cpp measfmt.cpp diff --git a/icu4c/source/test/depstest/dependencies.txt b/icu4c/source/test/depstest/dependencies.txt index d0cb8a80a22..878dd95860a 100644 --- a/icu4c/source/test/depstest/dependencies.txt +++ b/icu4c/source/test/depstest/dependencies.txt @@ -1078,7 +1078,7 @@ group: formatting measfmt.o quantityformatter.o # dateformat astro.o buddhcal.o calendar.o cecal.o chnsecal.o coptccal.o dangical.o ethpccal.o - gregocal.o gregoimp.o hebrwcal.o indiancal.o islamcal.o japancal.o persncal.o taiwncal.o + gregocal.o gregoimp.o hebrwcal.o indiancal.o islamcal.o iso8601cal.o japancal.o persncal.o taiwncal.o erarules.o # mostly for Japanese eras ucal.o basictz.o olsontz.o rbtz.o simpletz.o timezone.o tzrule.o tztrans.o diff --git a/icu4c/source/test/intltest/caltest.cpp b/icu4c/source/test/intltest/caltest.cpp index 9fc46ef1d4c..96c1ff9c80e 100644 --- a/icu4c/source/test/intltest/caltest.cpp +++ b/icu4c/source/test/intltest/caltest.cpp @@ -353,6 +353,34 @@ void CalendarTest::runIndexedTest( int32_t index, UBool exec, const char* &name, TestTimeZoneInLocale(); } break; +#define CASE(num, NAME) \ + case num: \ + name = #NAME; \ + if(exec) { \ + logln(#NAME "---"); logln(""); \ + NAME(); \ + } \ + break; + + CASE(38, TestBasicConversionISO8601); + CASE(39, TestBasicConversionJapanese); + CASE(40, TestBasicConversionBuddhist); + CASE(41, TestBasicConversionTaiwan); + CASE(42, TestBasicConversionPersian); + CASE(43, TestBasicConversionIslamic); + CASE(44, TestBasicConversionIslamicTBLA); + CASE(45, TestBasicConversionIslamicCivil); + CASE(46, TestBasicConversionIslamicRGSA); + CASE(47, TestBasicConversionIslamicUmalqura); + CASE(48, TestBasicConversionHebrew); + CASE(49, TestBasicConversionChinese); + CASE(50, TestBasicConversionDangi); + CASE(51, TestBasicConversionIndian); + CASE(52, TestBasicConversionCoptic); + CASE(53, TestBasicConversionEthiopic); + CASE(54, TestBasicConversionEthiopicAmeteAlem); + +#undef CASE default: name = ""; break; } } @@ -2390,8 +2418,8 @@ void CalendarTest::TestISO8601() { errln("Error: Failed to create a calendar for locale: %s", TEST_LOCALES[i]); continue; } - if (uprv_strcmp(cal->getType(), "gregorian") != 0) { - errln("Error: Gregorian calendar is not used for locale: %s", TEST_LOCALES[i]); + if (uprv_strcmp(cal->getType(), "iso8601") != 0) { + errln("Error: iso8601 calendar is not used for locale: %s", TEST_LOCALES[i]); continue; } for (int j = 0; TEST_DATA[j][0] != 0; j++) { @@ -2833,8 +2861,8 @@ void CalendarTest::TestTimeZoneInLocale(void) { { "my-u-ca-islamic-umalqura-tz-kzala", "Asia/Almaty", "islamic-umalqura" }, { "lo-u-ca-islamic-tbla-tz-bmbda", "Atlantic/Bermuda", "islamic-tbla" }, { "km-u-ca-islamic-civil-tz-aqplm", "Antarctica/Palmer", "islamic-civil" }, - { "kk-u-ca-islamic-rgsa-tz-usanc", "America/Anchorage", "islamic" }, - { "ar-u-ca-iso8601-tz-bjptn", "Africa/Porto-Novo", "gregorian" }, + { "kk-u-ca-islamic-rgsa-tz-usanc", "America/Anchorage", "islamic-rgsa" }, + { "ar-u-ca-iso8601-tz-bjptn", "Africa/Porto-Novo", "iso8601" }, { "he-u-ca-japanese-tz-tzdar", "Africa/Dar_es_Salaam", "japanese" }, { "bs-u-ca-persian-tz-etadd", "Africa/Addis_Ababa", "persian" }, { "it-u-ca-roc-tz-aruaq", "America/Argentina/San_Juan", "roc" }, @@ -2859,6 +2887,286 @@ void CalendarTest::TestTimeZoneInLocale(void) { } } +void CalendarTest::AsssertCalendarFieldValue( + Calendar* cal, double time, const char* type, + int32_t era, int32_t year, int32_t month, int32_t week_of_year, + int32_t week_of_month, int32_t date, int32_t day_of_year, int32_t day_of_week, + int32_t day_of_week_in_month, int32_t am_pm, int32_t hour, int32_t hour_of_day, + int32_t minute, int32_t second, int32_t millisecond, int32_t zone_offset, + int32_t dst_offset, int32_t year_woy, int32_t dow_local, int32_t extended_year, + int32_t julian_day, int32_t milliseconds_in_day, int32_t is_leap_month) { + + UErrorCode status = U_ZERO_ERROR; + cal->setTime(time, status); + assertEquals("getType", type, cal->getType()); + + assertEquals("UCAL_ERA", era, cal->get(UCAL_ERA, status)); + assertEquals("UCAL_YEAR", year, cal->get(UCAL_YEAR, status)); + assertEquals("UCAL_MONTH", month, cal->get(UCAL_MONTH, status)); + assertEquals("UCAL_WEEK_OF_YEAR", week_of_year, cal->get(UCAL_WEEK_OF_YEAR, status)); + assertEquals("UCAL_WEEK_OF_MONTH", week_of_month, cal->get(UCAL_WEEK_OF_MONTH, status)); + assertEquals("UCAL_DATE", date, cal->get(UCAL_DATE, status)); + assertEquals("UCAL_DAY_OF_YEAR", day_of_year, cal->get(UCAL_DAY_OF_YEAR, status)); + assertEquals("UCAL_DAY_OF_WEEK", day_of_week, cal->get(UCAL_DAY_OF_WEEK, status)); + assertEquals("UCAL_DAY_OF_WEEK_IN_MONTH", day_of_week_in_month, cal->get(UCAL_DAY_OF_WEEK_IN_MONTH, status)); + assertEquals("UCAL_AM_PM", am_pm, cal->get(UCAL_AM_PM, status)); + assertEquals("UCAL_HOUR", hour, cal->get(UCAL_HOUR, status)); + assertEquals("UCAL_HOUR_OF_DAY", hour_of_day, cal->get(UCAL_HOUR_OF_DAY, status)); + assertEquals("UCAL_MINUTE", minute, cal->get(UCAL_MINUTE, status)); + assertEquals("UCAL_SECOND", second, cal->get(UCAL_SECOND, status)); + assertEquals("UCAL_MILLISECOND", millisecond, cal->get(UCAL_MILLISECOND, status)); + assertEquals("UCAL_ZONE_OFFSET", zone_offset, cal->get(UCAL_ZONE_OFFSET, status)); + assertEquals("UCAL_DST_OFFSET", dst_offset, cal->get(UCAL_DST_OFFSET, status)); + assertEquals("UCAL_YEAR_WOY", year_woy, cal->get(UCAL_YEAR_WOY, status)); + assertEquals("UCAL_DOW_LOCAL", dow_local, cal->get(UCAL_DOW_LOCAL, status)); + assertEquals("UCAL_EXTENDED_YEAR", extended_year, cal->get(UCAL_EXTENDED_YEAR, status)); + assertEquals("UCAL_JULIAN_DAY", julian_day, cal->get(UCAL_JULIAN_DAY, status)); + assertEquals("UCAL_MILLISECONDS_IN_DAY", milliseconds_in_day, cal->get(UCAL_MILLISECONDS_IN_DAY, status)); + assertEquals("UCAL_IS_LEAP_MONTH", is_leap_month, cal->get(UCAL_IS_LEAP_MONTH, status)); +} + +static constexpr double test_time = 1667277891323; // Nov 1, 2022 4:44:51 GMT + +void CalendarTest::TestBasicConversionGregorian(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=gregorian"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get Gregorian calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "gregorian", + 1, 2022, 10, 45, 1, 1, 305, 3, 1, 0, 4, 4, 44, 51, + 323, 0, 0, 2022, 3, 2022, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionISO8601(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=iso8601"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get ISO8601 calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "iso8601", + 1, 2022, 10, 44, 1, 1, 305, 3, 1, 0, 4, 4, 44, 51, + 323, 0, 0, 2022, 2, 2022, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionJapanese(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=japanese"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get Japanese calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "japanese", + 236, 4, 10, 45, 1, 1, 305, 3, 1, 0, 4, 4, 44, 51, + 323, 0, 0, 2022, 3, 2022, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionBuddhist(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=buddhist"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get Buddhist calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "buddhist", + 0, 2565, 10, 45, 1, 1, 305, 3, 1, 0, 4, 4, 44, 51, + 323, 0, 0, 2022, 3, 2022, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionTaiwan(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=roc"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get Taiwan calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "roc", + 1, 111, 10, 45, 1, 1, 305, 3, 1, 0, 4, 4, 44, 51, + 323, 0, 0, 2022, 3, 2022, 2459885, 17091323, 0); + +} +void CalendarTest::TestBasicConversionPersian(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=persian"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get Persian calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "persian", + 0, 1401, 7, 33, 2, 10, 226, 3, 2, 0, 4, 4, 44, 51, + 323, 0, 0, 1401, 3, 1401, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionIslamic(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=islamic"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get Islamic calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "islamic", + 0, 1444, 3, 15, 2, 7, 96, 3, 1, 0, 4, 4, 44, 51, + 323, 0, 0, 1444, 3, 1444, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionIslamicTBLA(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=islamic-tbla"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get IslamicTBLA calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "islamic-tbla", + 0, 1444, 3, 15, 2, 7, 96, 3, 1, 0, 4, 4, 44, 51, + 323, 0, 0, 1444, 3, 1444, 2459885, 17091323, 0); + +} +void CalendarTest::TestBasicConversionIslamicCivil(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=islamic-civil"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get IslamicCivil calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "islamic-civil", + 0, 1444, 3, 15, 2, 6, 95, 3, 1, 0, 4, 4, 44, 51, + 323, 0, 0, 1444, 3, 1444, 2459885, 17091323, 0); + +} +void CalendarTest::TestBasicConversionIslamicRGSA(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=islamic-rgsa"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get IslamicRGSA calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "islamic-rgsa", + 0, 1444, 3, 15, 2, 7, 96, 3, 1, 0, 4, 4, 44, 51, + 323, 0, 0, 1444, 3, 1444, 2459885, 17091323, 0); + +} +void CalendarTest::TestBasicConversionIslamicUmalqura(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=islamic-umalqura"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get IslamicUmalqura calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "islamic-umalqura", + 0, 1444, 3, 15, 2, 7, 95, 3, 1, 0, 4, 4, 44, 51, + 323, 0, 0, 1444, 3, 1444, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionHebrew(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=hebrew"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get Hebrew calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "hebrew", + 0, 5783, 1, 6, 2, 7, 37, 3, 1, 0, 4, 4, 44, 51, + 323, 0, 0, 5783, 3, 5783, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionChinese(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=chinese"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get Chinese calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "chinese", + 78, 39, 9, 40, 2, 8, 274, 3, 2, 0, 4, 4, 44, 51, + 323, 0, 0, 4659, 3, 4659, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionDangi(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=dangi"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get Dangi calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "dangi", + 78, 39, 9, 40, 2, 8, 274, 3, 2, 0, 4, 4, 44, 51, + 323, 0, 0, 4355, 3, 4355, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionIndian(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=indian"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get Indian calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "indian", + 0, 1944, 7, 33, 2, 10, 225, 3, 2, 0, 4, 4, 44, 51, + 323, 0, 0, 1944, 3, 1944, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionCoptic(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=coptic"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get Coptic calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "coptic", + 1, 1739, 1, 8, 4, 22, 52, 3, 4, 0, 4, 4, 44, 51, + 323, 0, 0, 1739, 3, 1739, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionEthiopic(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=ethiopic"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get Ethiopic calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "ethiopic", + 1, 2015, 1, 8, 4, 22, 52, 3, 4, 0, 4, 4, 44, 51, + 323, 0, 0, 2015, 3, 2015, 2459885, 17091323, 0); +} +void CalendarTest::TestBasicConversionEthiopicAmeteAlem(void) { + UErrorCode status = U_ZERO_ERROR; + LocalPointer cal(icu::Calendar::createInstance( + *TimeZone::getGMT(), Locale("en@calendar=ethiopic-amete-alem"), status)); + if (U_FAILURE(status)) { + errln("Fail: Cannot get EthiopicAmeteAlem calendar"); + return; + } + AsssertCalendarFieldValue( + cal.getAlias(), test_time, "ethiopic-amete-alem", + 0, 7515, 1, 8, 4, 22, 52, 3, 4, 0, 4, 4, 44, 51, + 323, 0, 0, 2015, 3, 2015, 2459885, 17091323, 0); +} + + void CalendarTest::setAndTestCalendar(Calendar* cal, int32_t initMonth, int32_t initDay, int32_t initYear, UErrorCode& status) { cal->clear(); cal->setLenient(false); diff --git a/icu4c/source/test/intltest/caltest.h b/icu4c/source/test/intltest/caltest.h index 7f14c89b151..55225ac828d 100644 --- a/icu4c/source/test/intltest/caltest.h +++ b/icu4c/source/test/intltest/caltest.h @@ -257,6 +257,34 @@ public: // package void TestAddAcrossZoneTransition(void); void TestChineseCalendarMapping(void); + + void TestBasicConversionGregorian(void); + void TestBasicConversionISO8601(void); + void TestBasicConversionJapanese(void); + void TestBasicConversionBuddhist(void); + void TestBasicConversionTaiwan(void); + void TestBasicConversionPersian(void); + void TestBasicConversionIslamic(void); + void TestBasicConversionIslamicTBLA(void); + void TestBasicConversionIslamicCivil(void); + void TestBasicConversionIslamicRGSA(void); + void TestBasicConversionIslamicUmalqura(void); + void TestBasicConversionHebrew(void); + void TestBasicConversionChinese(void); + void TestBasicConversionDangi(void); + void TestBasicConversionIndian(void); + void TestBasicConversionCoptic(void); + void TestBasicConversionEthiopic(void); + void TestBasicConversionEthiopicAmeteAlem(void); + + void AsssertCalendarFieldValue( + Calendar* cal, double time, const char* type, + int32_t era, int32_t year, int32_t month, int32_t week_of_year, + int32_t week_of_month, int32_t date, int32_t day_of_year, int32_t day_of_week, + int32_t day_of_week_in_month, int32_t am_pm, int32_t hour, int32_t hour_of_day, + int32_t minute, int32_t second, int32_t millisecond, int32_t zone_offset, + int32_t dst_offset, int32_t year_woy, int32_t dow_local, int32_t extended_year, + int32_t julian_day, int32_t milliseconds_in_day, int32_t is_leap_month); }; #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/dtfmttst.cpp b/icu4c/source/test/intltest/dtfmttst.cpp index 317ae437ba2..e29c7a16f36 100644 --- a/icu4c/source/test/intltest/dtfmttst.cpp +++ b/icu4c/source/test/intltest/dtfmttst.cpp @@ -3477,8 +3477,8 @@ void DateFormatTest::TestTimeZoneInLocale() { "my-u-ca-islamic-umalqura-tz-kzala", "Asia/Almaty", "islamic-umalqura" }, { "lo-u-ca-islamic-tbla-tz-bmbda", "Atlantic/Bermuda", "islamic-tbla" }, { "km-u-ca-islamic-civil-tz-aqplm", "Antarctica/Palmer", "islamic-civil" }, - { "kk-u-ca-islamic-rgsa-tz-usanc", "America/Anchorage", "islamic" }, - { "ar-u-ca-iso8601-tz-bjptn", "Africa/Porto-Novo", "gregorian" }, + { "kk-u-ca-islamic-rgsa-tz-usanc", "America/Anchorage", "islamic-rgsa" }, + { "ar-u-ca-iso8601-tz-bjptn", "Africa/Porto-Novo", "iso8601" }, { "he-u-ca-japanese-tz-tzdar", "Africa/Dar_es_Salaam", "japanese" }, { "bs-u-ca-persian-tz-etadd", "Africa/Addis_Ababa", "persian" }, { "it-u-ca-roc-tz-aruaq", "America/Argentina/San_Juan", "roc" },