1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15#include "i18n_hilog.h" 16#include "unicode/gregocal.h" 17#include "lunar_calendar.h" 18 19namespace OHOS { 20namespace Global { 21namespace I18n { 22std::unordered_map<int32_t, int32_t> LunarCalendar::daysOfMonth { 23 { 1, 31 }, 24 { 2, 28 }, 25 { 3, 31 }, 26 { 4, 30 }, 27 { 5, 31 }, 28 { 6, 30 }, 29 { 7, 31 }, 30 { 8, 31 }, 31 { 9, 30 }, 32 { 10, 31 }, 33 { 11, 30 }, 34 { 12, 31 } 35}; 36 37std::unordered_map<int32_t, int32_t> LunarCalendar::accDaysOfMonth { 38 { 1, 0 }, 39 { 2, 31 }, 40 { 3, 59 }, 41 { 4, 90 }, 42 { 5, 120 }, 43 { 6, 151 }, 44 { 7, 181 }, 45 { 8, 212 }, 46 { 9, 243 }, 47 { 10, 273 }, 48 { 11, 304 }, 49 { 12, 334 } 50}; 51 52std::vector<uint32_t> LunarCalendar::lunarDateInfo { 53 0x4bd8, 0x4ae0, 0xa570, 0x54d5, 0xd260, 0xd950, 0x5554, 0x56af, 0x9ad0, 0x55d2, 54 0x4ae0, 0xa5b6, 0xa4d0, 0xd250, 0xd295, 0xb54f, 0xd6a0, 0xada2, 0x95b0, 0x4977, 55 0x497f, 0xa4b0, 0xb4b5, 0x6a50, 0x6d40, 0xab54, 0x2b6f, 0x9570, 0x52f2, 0x4970, 56 0x6566, 0xd4a0, 0xea50, 0x6a95, 0x5adf, 0x2b60, 0x86e3, 0x92ef, 0xc8d7, 0xc95f, 57 0xd4a0, 0xd8a6, 0xb55f, 0x56a0, 0xa5b4, 0x25df, 0x92d0, 0xd2b2, 0xa950, 0xb557, 58 0x6ca0, 0xb550, 0x5355, 0x4daf, 0xa5b0, 0x4573, 0x52bf, 0xa9a8, 0xe950, 0x6aa0, 59 0xaea6, 0xab50, 0x4b60, 0xaae4, 0xa570, 0x5260, 0xf263, 0xd950, 0x5b57, 0x56a0, 60 0x96d0, 0x4dd5, 0x4ad0, 0xa4d0, 0xd4d4, 0xd250, 0xd558, 0xb540, 0xb6a0, 0x95a6, 61 0x95bf, 0x49b0, 0xa974, 0xa4b0, 0xb27a, 0x6a50, 0x6d40, 0xaf46, 0xab60, 0x9570, 62 0x4af5, 0x4970, 0x64b0, 0x74a3, 0xea50, 0x6b58, 0x5ac0, 0xab60, 0x96d5, 0x92e0, 63 0xc960, 0xd954, 0xd4a0, 0xda50, 0x7552, 0x56a0, 0xabb7, 0x25d0, 0x92d0, 0xcab5, 64 0xa950, 0xb4a0, 0xbaa4, 0xad50, 0x55d9, 0x4ba0, 0xa5b0, 0x5176, 0x52bf, 0xa930, 65 0x7954, 0x6aa0, 0xad50, 0x5b52, 0x4b60, 0xa6e6, 0xa4e0, 0xd260, 0xea65, 0xd530, 66 0x5aa0, 0x76a3, 0x96d0, 0x4afb, 0x4ad0, 0xa4d0, 0xd0b6, 0xd25f, 0xd520, 0xdd45, 67 0xb5a0, 0x56d0, 0x55b2, 0x49b0, 0xa577, 0xa4b0, 0xaa50, 0xb255, 0x6d2f, 0xada0, 68 0x4b63, 0x937f, 0x49f8, 0x4970, 0x64b0, 0x68a6, 0xea5f, 0x6b20, 0xa6c4, 0xaaef, 69 0x92e0, 0xd2e3, 0xc960, 0xd557, 0xd4a0, 0xda50, 0x5d55, 0x56a0, 0xa6d0, 0x55d4, 70 0x52d0, 0xa9b8, 0xa950, 0xb4a0, 0xb6a6, 0xad50, 0x55a0, 0xaba4, 0xa5b0, 0x52b0, 71 0xb273, 0x6930, 0x7337, 0x6aa0, 0xad50, 0x4b55, 0x4b6f, 0xa570, 0x54e4, 0xd260, 72 0xe968, 0xd520, 0xdaa0, 0x6aa6, 0x56df, 0x4ae0, 0xa9d4, 0xa4d0, 0xd150, 0xf252, 73 0xd520, 74}; 75 76LunarCalendar::LunarCalendar() 77{ 78 UErrorCode status = U_ZERO_ERROR; 79 calendar_ = new icu::GregorianCalendar(status); 80 if (U_FAILURE(status)) { 81 HILOG_ERROR_I18N("LunarCalendar: create GregorianCalendar failed"); 82 if (calendar_ != nullptr) { 83 delete calendar_; 84 } 85 calendar_ = nullptr; 86 } 87} 88 89LunarCalendar::~LunarCalendar() 90{ 91 if (calendar_ != nullptr) { 92 delete calendar_; 93 } 94 calendar_ = nullptr; 95} 96 97bool LunarCalendar::SetGregorianDate(int32_t year, int32_t month, int32_t day) 98{ 99 ConvertDate(year, month, day); 100 isGregorianLeapYear = false; 101 isValidDate = VerifyDate(year, month, day); 102 if (!isValidDate) { 103 return false; 104 } 105 solorYear = year; 106 solorMonth = month; 107 solorDay = day; 108 CalcDaysFromBaseDate(); 109 SolorDateToLunarDate(); 110 return true; 111} 112 113void LunarCalendar::ConvertDate(int32_t& year, int32_t& month, int32_t& day) 114{ 115 if (calendar_ == nullptr) { 116 return; 117 } 118 calendar_->set(year, month - 1, day); 119 UErrorCode status = U_ZERO_ERROR; 120 int32_t tempYear = calendar_->get(UCAL_YEAR, status); 121 if (U_FAILURE(status)) { 122 HILOG_ERROR_I18N("ConvertDate: get year failed"); 123 return; 124 } 125 int32_t tempMonth = calendar_->get(UCAL_MONTH, status) + 1; 126 if (U_FAILURE(status)) { 127 HILOG_ERROR_I18N("ConvertDate: get month failed"); 128 return; 129 } 130 int32_t tempDay = calendar_->get(UCAL_DATE, status); 131 if (U_FAILURE(status)) { 132 HILOG_ERROR_I18N("ConvertDate: get day failed"); 133 return; 134 } 135 year = tempYear; 136 month = tempMonth; 137 day = tempDay; 138} 139 140void LunarCalendar::CalcDaysFromBaseDate() 141{ 142 daysCounts = DAYS_OF_YEAR * (solorYear - START_YEAR); 143 daysCounts += accDaysOfMonth[solorMonth]; 144 if (isGregorianLeapYear && solorMonth > MONTH_FEB) { 145 daysCounts++; 146 } 147 daysCounts--; 148 daysCounts += solorDay; 149 daysCounts += (solorYear - START_YEAR) / FREQ_LEAP_YEAR; 150 if (isGregorianLeapYear) { 151 daysCounts--; 152 } 153 if (solorYear >= VALID_END_YEAR) { 154 daysCounts--; 155 } 156} 157 158void LunarCalendar::SolorDateToLunarDate() 159{ 160 int32_t daysInPerLunarYear = 0; 161 int32_t daysInPerLunarMonth = 0; 162 int32_t leapMonth = 0xf; 163 int tempDaysCounts = daysCounts; 164 tempDaysCounts -= DAYS_FROM_SOLAR_TO_LUNAR; 165 int32_t i = 0; 166 for (i = START_YEAR; (tempDaysCounts > 0) && (i < END_YEAR); i++) { 167 daysInPerLunarYear = GetDaysPerLunarYear(i); 168 tempDaysCounts -= daysInPerLunarYear; 169 } 170 if (tempDaysCounts < 0) { 171 tempDaysCounts += daysInPerLunarYear; 172 --i; 173 } 174 lunarYear = i; 175 176 leapMonth = lunarDateInfo[lunarYear - START_YEAR] & 0xf; 177 leapMonth = (leapMonth == 0xf) ? 0 : leapMonth; 178 isLeapMonth = false; 179 for (i = 1; i <= VALID_END_MONTH && tempDaysCounts > 0; i++) { 180 if (leapMonth > 0 && (leapMonth + 1) == i && !isLeapMonth) { 181 --i; 182 isLeapMonth = true; 183 daysInPerLunarMonth = ((lunarDateInfo[lunarYear - START_YEAR + 1] & 0xf) == 0xf) ? DAYS_IN_BIG_MONTH : 184 DAYS_IN_SMALL_MONTH; 185 } else { 186 daysInPerLunarMonth = ((lunarDateInfo[lunarYear - START_YEAR] & 187 (0x8000 >> (i - 1))) == (0x8000 >> (i - 1))) ? DAYS_IN_BIG_MONTH : DAYS_IN_SMALL_MONTH; 188 } 189 if (isLeapMonth && (leapMonth + 1) == i) { 190 isLeapMonth = false; 191 } 192 tempDaysCounts -= daysInPerLunarMonth; 193 } 194 AdjustLeapMonth(i, tempDaysCounts, leapMonth); 195 if (tempDaysCounts < 0) { 196 tempDaysCounts += daysInPerLunarMonth; 197 --i; 198 } 199 lunarMonth = i; 200 lunarDay = tempDaysCounts + 1; 201} 202 203void LunarCalendar::AdjustLeapMonth(int32_t& i, int32_t tempDaysCounts, int32_t leapMonth) 204{ 205 if (tempDaysCounts == 0 && leapMonth > 0 && i == leapMonth + 1) { 206 if (isLeapMonth) { 207 isLeapMonth = false; 208 } else { 209 isLeapMonth = true; 210 --i; 211 } 212 } 213} 214 215int32_t LunarCalendar::GetDaysPerLunarYear(int32_t lunarYear) 216{ 217 int32_t daysPerLunarYear = 0; 218 if ((lunarYear < START_YEAR) || (lunarYear > END_YEAR)) { 219 return 0; 220 } 221 daysPerLunarYear += BASE_DAYS_PER_LUNAR_YEAR; 222 for (uint32_t i = 0x8000; i > 0x8; i = i >> 1) { 223 daysPerLunarYear += ((lunarDateInfo[lunarYear - START_YEAR] & i) == i) ? 1 : 0; 224 } 225 if (((lunarDateInfo[lunarYear - START_YEAR] & 0xf) != 0) && 226 ((lunarDateInfo[lunarYear - START_YEAR] & 0xf) != 0xf)) { 227 daysPerLunarYear += ((lunarDateInfo[lunarYear - START_YEAR + 1] & 0xf) == 0xf) ? DAYS_IN_BIG_MONTH : 228 DAYS_IN_SMALL_MONTH; 229 } 230 return daysPerLunarYear; 231} 232 233bool LunarCalendar::VerifyDate(int32_t year, int32_t month, int32_t day) 234{ 235 if ((year < VALID_START_YEAR) || (year > VALID_END_YEAR)) { 236 HILOG_ERROR_I18N("VerifyDate: %{public}d is an invalid year", year); 237 return false; 238 } 239 240 if ((month < VALID_START_MONTH) || (month > VALID_END_MONTH)) { 241 HILOG_ERROR_I18N("VerifyDate: %{public}d is an invalid month", month); 242 return false; 243 } 244 245 int32_t validEndDay = daysOfMonth[month]; 246 isGregorianLeapYear = IsGregorianLeapYear(year); 247 if (month == MONTH_FEB) { 248 validEndDay = isGregorianLeapYear ? validEndDay + 1 : validEndDay; 249 } 250 251 if ((month < VALID_START_DAY) || (month > validEndDay)) { 252 HILOG_ERROR_I18N("VerifyDate: %{public}d is an invalid day", day); 253 return false; 254 } 255 return true; 256} 257 258bool LunarCalendar::IsGregorianLeapYear(int32_t year) 259{ 260 if (year % YEAR_ERA == 0) { 261 if (year % (YEAR_ERA * FREQ_LEAP_YEAR) == 0) { 262 return true; 263 } 264 return false; 265 } 266 if (year % FREQ_LEAP_YEAR == 0) { 267 return true; 268 } 269 return false; 270} 271 272int32_t LunarCalendar::GetLunarYear() 273{ 274 if (!isValidDate) { 275 HILOG_ERROR_I18N("GetLunarYear: invalid date"); 276 return -1; 277 } 278 return lunarYear; 279} 280 281int32_t LunarCalendar::GetLunarMonth() 282{ 283 if (!isValidDate) { 284 HILOG_ERROR_I18N("GetLunarYear: invalid date"); 285 return -1; 286 } 287 return lunarMonth; 288} 289 290int32_t LunarCalendar::GetLunarDay() 291{ 292 if (!isValidDate) { 293 HILOG_ERROR_I18N("GetLunarYear: invalid date"); 294 return -1; 295 } 296 return lunarDay; 297} 298 299bool LunarCalendar::IsLeapMonth() 300{ 301 if (!isValidDate) { 302 HILOG_ERROR_I18N("GetLunarYear: invalid date"); 303 return false; 304 } 305 return isLeapMonth; 306} 307} // namespace I18n 308} // namespace Global 309} // namespace OHOS