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
19 namespace OHOS {
20 namespace Global {
21 namespace I18n {
22 std::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
37 std::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
52 std::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
LunarCalendar()76 LunarCalendar::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
~LunarCalendar()89 LunarCalendar::~LunarCalendar()
90 {
91 if (calendar_ != nullptr) {
92 delete calendar_;
93 }
94 calendar_ = nullptr;
95 }
96
SetGregorianDate(int32_t year,int32_t month,int32_t day)97 bool 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
ConvertDate(int32_t & year,int32_t & month,int32_t & day)113 void 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
CalcDaysFromBaseDate()140 void 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
SolorDateToLunarDate()158 void 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
AdjustLeapMonth(int32_t & i,int32_t tempDaysCounts,int32_t leapMonth)203 void 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
GetDaysPerLunarYear(int32_t lunarYear)215 int32_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
VerifyDate(int32_t year,int32_t month,int32_t day)233 bool 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
IsGregorianLeapYear(int32_t year)258 bool 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
GetLunarYear()272 int32_t LunarCalendar::GetLunarYear()
273 {
274 if (!isValidDate) {
275 HILOG_ERROR_I18N("GetLunarYear: invalid date");
276 return -1;
277 }
278 return lunarYear;
279 }
280
GetLunarMonth()281 int32_t LunarCalendar::GetLunarMonth()
282 {
283 if (!isValidDate) {
284 HILOG_ERROR_I18N("GetLunarYear: invalid date");
285 return -1;
286 }
287 return lunarMonth;
288 }
289
GetLunarDay()290 int32_t LunarCalendar::GetLunarDay()
291 {
292 if (!isValidDate) {
293 HILOG_ERROR_I18N("GetLunarYear: invalid date");
294 return -1;
295 }
296 return lunarDay;
297 }
298
IsLeapMonth()299 bool 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