From b0ab1171ad3c577325c878e40c2c8cdce1354f60 Mon Sep 17 00:00:00 2001 From: Frank Tang Date: Wed, 21 Sep 2022 21:11:38 -0700 Subject: [PATCH] ICU-10752 Spread (s|g)etRelativeYear to subclass Remove the switch statment implementaiton in Calendar::(g|s)etRelatedYear and move the code into each subclass as proper OOP style. --- icu4c/source/i18n/calendar.cpp | 118 +------------------------------- icu4c/source/i18n/chnsecal.cpp | 16 +++++ icu4c/source/i18n/chnsecal.h | 14 ++++ icu4c/source/i18n/coptccal.cpp | 17 +++++ icu4c/source/i18n/coptccal.h | 14 ++++ icu4c/source/i18n/dangical.cpp | 17 +++++ icu4c/source/i18n/dangical.h | 14 ++++ icu4c/source/i18n/ethpccal.cpp | 20 ++++++ icu4c/source/i18n/ethpccal.h | 14 ++++ icu4c/source/i18n/hebrwcal.cpp | 17 +++++ icu4c/source/i18n/hebrwcal.h | 14 ++++ icu4c/source/i18n/indiancal.cpp | 17 +++++ icu4c/source/i18n/indiancal.h | 15 ++++ icu4c/source/i18n/islamcal.cpp | 48 +++++++++++++ icu4c/source/i18n/islamcal.h | 14 ++++ icu4c/source/i18n/persncal.cpp | 17 +++++ icu4c/source/i18n/persncal.h | 14 ++++ 17 files changed, 283 insertions(+), 117 deletions(-) diff --git a/icu4c/source/i18n/calendar.cpp b/icu4c/source/i18n/calendar.cpp index ffd84761bec..f5a40e48395 100644 --- a/icu4c/source/i18n/calendar.cpp +++ b/icu4c/source/i18n/calendar.cpp @@ -1267,130 +1267,14 @@ Calendar::set(int32_t year, int32_t month, int32_t date, int32_t hour, int32_t m } // ------------------------------------- -// For now the full getRelatedYear implementation is here; -// per #10752 move the non-default implementation to subclasses -// (default implementation will do no year adjustment) - -static int32_t gregoYearFromIslamicStart(int32_t year) { - // ad hoc conversion, improve under #10752 - // rough est for now, ok for grego 1846-2138, - // otherwise occasionally wrong (for 3% of years) - int cycle, offset, shift = 0; - if (year >= 1397) { - cycle = (year - 1397) / 67; - offset = (year - 1397) % 67; - shift = 2*cycle + ((offset >= 33)? 1: 0); - } else { - cycle = (year - 1396) / 67 - 1; - offset = -(year - 1396) % 67; - shift = 2*cycle + ((offset <= 33)? 1: 0); - } - return year + 579 - shift; -} - int32_t Calendar::getRelatedYear(UErrorCode &status) const { - if (U_FAILURE(status)) { - return 0; - } - int32_t year = get(UCAL_EXTENDED_YEAR, status); - if (U_FAILURE(status)) { - return 0; - } - // modify for calendar type - ECalType type = getCalendarType(getType()); - switch (type) { - case CALTYPE_PERSIAN: - year += 622; break; - case CALTYPE_HEBREW: - year -= 3760; break; - case CALTYPE_CHINESE: - year -= 2637; break; - case CALTYPE_INDIAN: - year += 79; break; - case CALTYPE_COPTIC: - year += 284; break; - case CALTYPE_ETHIOPIC: - year += 8; break; - case CALTYPE_ETHIOPIC_AMETE_ALEM: - year -=5492; break; - case CALTYPE_DANGI: - year -= 2333; break; - case CALTYPE_ISLAMIC_CIVIL: - case CALTYPE_ISLAMIC: - case CALTYPE_ISLAMIC_UMALQURA: - case CALTYPE_ISLAMIC_TBLA: - case CALTYPE_ISLAMIC_RGSA: - year = gregoYearFromIslamicStart(year); break; - default: - // CALTYPE_GREGORIAN - // CALTYPE_JAPANESE - // CALTYPE_BUDDHIST - // CALTYPE_ROC - // CALTYPE_ISO8601 - // do nothing, EXTENDED_YEAR same as Gregorian - break; - } - return year; + return get(UCAL_EXTENDED_YEAR, status); } // ------------------------------------- -// For now the full setRelatedYear implementation is here; -// per #10752 move the non-default implementation to subclasses -// (default implementation will do no year adjustment) - -static int32_t firstIslamicStartYearFromGrego(int32_t year) { - // ad hoc conversion, improve under #10752 - // rough est for now, ok for grego 1846-2138, - // otherwise occasionally wrong (for 3% of years) - int cycle, offset, shift = 0; - if (year >= 1977) { - cycle = (year - 1977) / 65; - offset = (year - 1977) % 65; - shift = 2*cycle + ((offset >= 32)? 1: 0); - } else { - cycle = (year - 1976) / 65 - 1; - offset = -(year - 1976) % 65; - shift = 2*cycle + ((offset <= 32)? 1: 0); - } - return year - 579 + shift; -} void Calendar::setRelatedYear(int32_t year) { - // modify for calendar type - ECalType type = getCalendarType(getType()); - switch (type) { - case CALTYPE_PERSIAN: - year -= 622; break; - case CALTYPE_HEBREW: - year += 3760; break; - case CALTYPE_CHINESE: - year += 2637; break; - case CALTYPE_INDIAN: - year -= 79; break; - case CALTYPE_COPTIC: - year -= 284; break; - case CALTYPE_ETHIOPIC: - year -= 8; break; - case CALTYPE_ETHIOPIC_AMETE_ALEM: - year +=5492; break; - case CALTYPE_DANGI: - year += 2333; break; - case CALTYPE_ISLAMIC_CIVIL: - case CALTYPE_ISLAMIC: - case CALTYPE_ISLAMIC_UMALQURA: - case CALTYPE_ISLAMIC_TBLA: - case CALTYPE_ISLAMIC_RGSA: - year = firstIslamicStartYearFromGrego(year); break; - default: - // CALTYPE_GREGORIAN - // CALTYPE_JAPANESE - // CALTYPE_BUDDHIST - // CALTYPE_ROC - // CALTYPE_ISO8601 - // do nothing, EXTENDED_YEAR same as Gregorian - break; - } // set extended year set(UCAL_EXTENDED_YEAR, year); } diff --git a/icu4c/source/i18n/chnsecal.cpp b/icu4c/source/i18n/chnsecal.cpp index b2b6258820b..c1e131247cb 100644 --- a/icu4c/source/i18n/chnsecal.cpp +++ b/icu4c/source/i18n/chnsecal.cpp @@ -825,6 +825,22 @@ void ChineseCalendar::offsetMonth(int32_t newMoon, int32_t dom, int32_t delta) { } } +constexpr uint32_t kChineseRelatedYearDiff = -2637; + +int32_t ChineseCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kChineseRelatedYearDiff; +} + +void ChineseCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kChineseRelatedYearDiff); +} // default century diff --git a/icu4c/source/i18n/chnsecal.h b/icu4c/source/i18n/chnsecal.h index 016e9422e43..8e45fb20426 100644 --- a/icu4c/source/i18n/chnsecal.h +++ b/icu4c/source/i18n/chnsecal.h @@ -175,6 +175,20 @@ class U_I18N_API ChineseCalendar : public Calendar { virtual void roll(UCalendarDateFields field, int32_t amount, UErrorCode &status) override; virtual void roll(EDateFields field, int32_t amount, UErrorCode &status) override; + /** + * @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; + //---------------------------------------------------------------------- // Internal methods & astronomical calculations //---------------------------------------------------------------------- diff --git a/icu4c/source/i18n/coptccal.cpp b/icu4c/source/i18n/coptccal.cpp index 0be700ca013..59609f63c21 100644 --- a/icu4c/source/i18n/coptccal.cpp +++ b/icu4c/source/i18n/coptccal.cpp @@ -96,6 +96,23 @@ CopticCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*status*/) internalSet(UCAL_DAY_OF_YEAR, (30 * month) + day); } +constexpr uint32_t kCopticRelatedYearDiff = 284; + +int32_t CopticCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kCopticRelatedYearDiff; +} + +void CopticCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kCopticRelatedYearDiff); +} + /** * The system maintains a static default century start date and Year. They are * initialized the first time they are used. Once the system default century date diff --git a/icu4c/source/i18n/coptccal.h b/icu4c/source/i18n/coptccal.h index 5c51af04ca0..46f1d63c0a1 100644 --- a/icu4c/source/i18n/coptccal.h +++ b/icu4c/source/i18n/coptccal.h @@ -154,6 +154,20 @@ public: */ const char * getType() const override; + /** + * @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 diff --git a/icu4c/source/i18n/dangical.cpp b/icu4c/source/i18n/dangical.cpp index 59cdc661dac..dad7bb97e2c 100644 --- a/icu4c/source/i18n/dangical.cpp +++ b/icu4c/source/i18n/dangical.cpp @@ -141,6 +141,23 @@ const TimeZone* DangiCalendar::getDangiCalZoneAstroCalc(UErrorCode &status) cons return gDangiCalendarZoneAstroCalc; } +constexpr uint32_t kDangiRelatedYearDiff = -2333; + +int32_t DangiCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kDangiRelatedYearDiff; +} + +void DangiCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kDangiRelatedYearDiff); +} + UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DangiCalendar) diff --git a/icu4c/source/i18n/dangical.h b/icu4c/source/i18n/dangical.h index 9d0437264ef..3cf4aaff203 100644 --- a/icu4c/source/i18n/dangical.h +++ b/icu4c/source/i18n/dangical.h @@ -72,6 +72,20 @@ class DangiCalendar : public ChineseCalendar { // Internal methods & astronomical calculations //---------------------------------------------------------------------- + /** + * @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; + private: const TimeZone* getDangiCalZoneAstroCalc(UErrorCode &status) const; diff --git a/icu4c/source/i18n/ethpccal.cpp b/icu4c/source/i18n/ethpccal.cpp index 07937872242..9b97b1ca33c 100644 --- a/icu4c/source/i18n/ethpccal.cpp +++ b/icu4c/source/i18n/ethpccal.cpp @@ -136,6 +136,26 @@ EthiopicCalendar::handleGetLimit(UCalendarDateFields field, ELimitType limitType return CECalendar::handleGetLimit(field, limitType); } + +constexpr uint32_t kEthiopicRelatedYearDiff = 8; +constexpr uint32_t kEthiopicAmeteAlemRelatedYearDiff = -5492; + +int32_t EthiopicCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + (isAmeteAlemEra() ? kEthiopicAmeteAlemRelatedYearDiff : kEthiopicRelatedYearDiff); +} + +void EthiopicCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - + (isAmeteAlemEra() ? kEthiopicAmeteAlemRelatedYearDiff : kEthiopicRelatedYearDiff)); +} + /** * The system maintains a static default century start date and Year. They are * initialized the first time they are used. Once the system default century date diff --git a/icu4c/source/i18n/ethpccal.h b/icu4c/source/i18n/ethpccal.h index 0cc5b6c535e..736ae1c565a 100644 --- a/icu4c/source/i18n/ethpccal.h +++ b/icu4c/source/i18n/ethpccal.h @@ -164,6 +164,20 @@ public: */ UBool isAmeteAlemEra() const; + /** + * @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 diff --git a/icu4c/source/i18n/hebrwcal.cpp b/icu4c/source/i18n/hebrwcal.cpp index 07aea83f643..6c47915fbe4 100644 --- a/icu4c/source/i18n/hebrwcal.cpp +++ b/icu4c/source/i18n/hebrwcal.cpp @@ -666,6 +666,23 @@ int32_t HebrewCalendar::handleComputeMonthStart(int32_t eyear, int32_t month, UB return (int) (day + 347997); } +constexpr uint32_t kHebrewRelatedYearDiff = -3760; + +int32_t HebrewCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kHebrewRelatedYearDiff; +} + +void HebrewCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kHebrewRelatedYearDiff); +} + /** * The system maintains a static default century start date and Year. They are * initialized the first time they are used. Once the system default century date diff --git a/icu4c/source/i18n/hebrwcal.h b/icu4c/source/i18n/hebrwcal.h index 34ce8c24c68..40a8f7a8f70 100644 --- a/icu4c/source/i18n/hebrwcal.h +++ b/icu4c/source/i18n/hebrwcal.h @@ -273,6 +273,20 @@ public: */ static UBool isLeapYear(int32_t year) ; + /** + * @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: /** diff --git a/icu4c/source/i18n/indiancal.cpp b/icu4c/source/i18n/indiancal.cpp index 80d38ca9d42..938b74ee0d2 100644 --- a/icu4c/source/i18n/indiancal.cpp +++ b/icu4c/source/i18n/indiancal.cpp @@ -297,6 +297,23 @@ void IndianCalendar::handleComputeFields(int32_t julianDay, UErrorCode& /* stat internalSet(UCAL_DAY_OF_YEAR, yday + 1); // yday is 0-based } +constexpr uint32_t kIndianRelatedYearDiff = 79; + +int32_t IndianCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kIndianRelatedYearDiff; +} + +void IndianCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kIndianRelatedYearDiff); +} + /** * The system maintains a static default century start date and Year. They are * initialized the first time they are used. Once the system default century date diff --git a/icu4c/source/i18n/indiancal.h b/icu4c/source/i18n/indiancal.h index e1c2f99295e..05d427316ca 100644 --- a/icu4c/source/i18n/indiancal.h +++ b/icu4c/source/i18n/indiancal.h @@ -284,6 +284,21 @@ public: */ virtual const char * getType() const override; + /** + * @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; + + private: IndianCalendar() = delete; // default constructor not implemented diff --git a/icu4c/source/i18n/islamcal.cpp b/icu4c/source/i18n/islamcal.cpp index 5b920f46a70..54b2a97ff49 100644 --- a/icu4c/source/i18n/islamcal.cpp +++ b/icu4c/source/i18n/islamcal.cpp @@ -692,6 +692,54 @@ void IslamicCalendar::handleComputeFields(int32_t julianDay, UErrorCode &status) internalSet(UCAL_DAY_OF_YEAR, dayOfYear); } +static int32_t gregoYearFromIslamicStart(int32_t year) { + // ad hoc conversion, improve under #10752 + // rough est for now, ok for grego 1846-2138, + // otherwise occasionally wrong (for 3% of years) + int cycle, offset, shift = 0; + if (year >= 1397) { + cycle = (year - 1397) / 67; + offset = (year - 1397) % 67; + shift = 2*cycle + ((offset >= 33)? 1: 0); + } else { + cycle = (year - 1396) / 67 - 1; + offset = -(year - 1396) % 67; + shift = 2*cycle + ((offset <= 33)? 1: 0); + } + return year + 579 - shift; +} + +int32_t IslamicCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return gregoYearFromIslamicStart(year); +} + +static int32_t firstIslamicStartYearFromGrego(int32_t year) { + // ad hoc conversion, improve under #10752 + // rough est for now, ok for grego 1846-2138, + // otherwise occasionally wrong (for 3% of years) + int cycle, offset, shift = 0; + if (year >= 1977) { + cycle = (year - 1977) / 65; + offset = (year - 1977) % 65; + shift = 2*cycle + ((offset >= 32)? 1: 0); + } else { + cycle = (year - 1976) / 65 - 1; + offset = -(year - 1976) % 65; + shift = 2*cycle + ((offset <= 32)? 1: 0); + } + return year - 579 + shift; +} + +void IslamicCalendar::setRelatedYear(int32_t year) +{ + set(UCAL_EXTENDED_YEAR, firstIslamicStartYearFromGrego(year)); +} + /** * The system maintains a static default century start date and Year. They are * initialized the first time they are used. Once the system default century date diff --git a/icu4c/source/i18n/islamcal.h b/icu4c/source/i18n/islamcal.h index 9e84353e4b0..5ff212485e0 100644 --- a/icu4c/source/i18n/islamcal.h +++ b/icu4c/source/i18n/islamcal.h @@ -376,6 +376,20 @@ class U_I18N_API IslamicCalendar : public Calendar { */ virtual const char * getType() const override; + /** + * @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; + private: IslamicCalendar() = delete; // default constructor not implemented diff --git a/icu4c/source/i18n/persncal.cpp b/icu4c/source/i18n/persncal.cpp index bdbe296d232..a3f5e6ad4f5 100644 --- a/icu4c/source/i18n/persncal.cpp +++ b/icu4c/source/i18n/persncal.cpp @@ -233,6 +233,23 @@ void PersianCalendar::handleComputeFields(int32_t julianDay, UErrorCode &/*statu internalSet(UCAL_DAY_OF_YEAR, dayOfYear); } +constexpr uint32_t kPersianRelatedYearDiff = 622; + +int32_t PersianCalendar::getRelatedYear(UErrorCode &status) const +{ + int32_t year = get(UCAL_EXTENDED_YEAR, status); + if (U_FAILURE(status)) { + return 0; + } + return year + kPersianRelatedYearDiff; +} + +void PersianCalendar::setRelatedYear(int32_t year) +{ + // set extended year + set(UCAL_EXTENDED_YEAR, year - kPersianRelatedYearDiff); +} + // default century static UDate gSystemDefaultCenturyStart = DBL_MIN; diff --git a/icu4c/source/i18n/persncal.h b/icu4c/source/i18n/persncal.h index 5111e01e5a1..0d4c12535c2 100644 --- a/icu4c/source/i18n/persncal.h +++ b/icu4c/source/i18n/persncal.h @@ -278,6 +278,20 @@ class PersianCalendar : public Calendar { */ virtual const char * getType() const override; + /** + * @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; + private: PersianCalendar(); // default constructor not implemented -- 2.40.0