1 /*
2 * Copyright (c) 2021-2022 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
16 #include "core/components/picker/picker_data.h"
17
18 #include "base/i18n/localization.h"
19
20 namespace OHOS::Ace {
21 namespace {
22 constexpr uint32_t MAX_YEAR = 5000;
23 constexpr uint32_t MAX_MONTH = 12;
24 }
25
Current()26 PickerDate PickerDate::Current()
27 {
28 PickerDate date;
29 auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
30 auto local = std::localtime(&now);
31 if (local == nullptr) {
32 LOGE("get localtime failed.");
33 return date;
34 }
35 date.SetYear(local->tm_year + 1900); // local date start from 1900
36 date.SetMonth(local->tm_mon + 1); // local month start from 0 to 11, need add one.
37 date.SetDay(local->tm_mday);
38 date.SetWeek(local->tm_wday);
39 return date;
40 }
41
GetMaxDay(uint32_t year,uint32_t month)42 uint32_t PickerDate::GetMaxDay(uint32_t year, uint32_t month)
43 {
44 if (month == 2) { // days count in february is different between leap year and other.
45 bool leapYear = IsLeapYear(year);
46 return (leapYear ? 29 : 28); // leap year's february has 29 days, other has 28 days.
47 }
48
49 switch (month) {
50 case 1: // january
51 case 3: // march
52 case 5: // may
53 case 7: // july
54 case 8: // august
55 case 10: // october
56 case 12: // december
57 return 31; // upper months has 31 days
58 default:
59 return 30; // other month has 30 days
60 }
61 }
62
IsLeapYear(uint32_t year)63 bool PickerDate::IsLeapYear(uint32_t year)
64 {
65 if (year % 100 == 0) { // special case: year can divided by 100
66 return (year % 400 == 0); // leap year equal that can divided by 400.
67 }
68
69 return (year % 4 == 0); // other case, leap year equal that can divided by 4.
70 }
71
AdjustDateToRange(const PickerDate & date,const PickerDate & start,const PickerDate & end)72 PickerDate PickerDate::AdjustDateToRange(const PickerDate& date, const PickerDate& start, const PickerDate& end)
73 {
74 PickerDate adjustedDate = date;
75 if (start.ToDays() > date.ToDays()) {
76 adjustedDate = start;
77 } else if (end.GetYear() > 0 && end.ToDays() < date.ToDays()) {
78 adjustedDate = end;
79 }
80 return adjustedDate;
81 }
82
IsDateInRange(const PickerDate & date,const PickerDate & start,const PickerDate & end)83 bool PickerDate::IsDateInRange(const PickerDate& date, const PickerDate& start, const PickerDate& end)
84 {
85 if (start.GetYear() > 0 &&
86 (date.GetYear() < start.GetYear() ||
87 (date.GetYear() == start.GetYear() &&
88 (date.GetMonth() < start.GetMonth() ||
89 (date.GetMonth() == start.GetMonth() && date.GetDay() < start.GetDay()))))) {
90 return false;
91 }
92 if (end.GetYear() > 0 &&
93 (date.GetYear() > end.GetYear() ||
94 (date.GetYear() == end.GetYear() &&
95 (date.GetMonth() > end.GetMonth() ||
96 (date.GetMonth() == end.GetMonth() && date.GetDay() > end.GetDay()))))) {
97 return false;
98 }
99 return true;
100 }
101
ToString(bool jsonFormat,int32_t status) const102 std::string PickerDate::ToString(bool jsonFormat, int32_t status) const
103 {
104 if (!jsonFormat) {
105 DateTime date;
106 date.year = year_;
107 date.month = month_ ? month_ - 1 : 0; // W3C's month start from 0 to 11
108 date.day = day_;
109 return Localization::GetInstance()->FormatDateTime(date, DateTimeStyle::FULL, DateTimeStyle::NONE);
110 }
111
112 return std::string("{\"year\":") + std::to_string(year_) + ",\"month\":" + std::to_string(month_) +
113 ",\"day\":" + std::to_string(day_) + ",\"status\":" + std::to_string(status) + "}";
114 }
115
ToDays() const116 uint32_t PickerDate::ToDays() const
117 {
118 uint32_t days = 0;
119 days += day_ ? day_ - 1 : 0; // day start from 1
120 // month start from 1
121 for (uint32_t month = 1; month < month_; ++month) {
122 days += PickerDate::GetMaxDay(year_, month);
123 }
124 // year start from 1900 or 1
125 uint32_t startYear = 1900;
126 if (year_ < startYear) {
127 startYear = 1;
128 }
129 for (uint32_t year = startYear; year < year_; ++year) {
130 // leap year has 366 days, other year has 365 days.
131 days += (PickerDate::IsLeapYear(year) ? 366 : 365);
132 }
133 return days;
134 }
135
FromDays(uint32_t days)136 void PickerDate::FromDays(uint32_t days)
137 {
138 for (year_ = 1900; year_ <= 2100; ++year_) { // year start from 1900 to 2100.
139 // leap year has 366 days, other year has 365 days;
140 uint32_t daysInYear = (PickerDate::IsLeapYear(year_) ? 366 : 365);
141 if (days < daysInYear) {
142 break;
143 } else {
144 days -= daysInYear;
145 }
146 }
147
148 for (month_ = 1; month_ <= 12; ++month_) { // month start from 1 to 12
149 uint32_t daysInMonth = PickerDate::GetMaxDay(year_, month_);
150 if (days < daysInMonth) {
151 break;
152 } else {
153 days -= daysInMonth;
154 }
155 }
156
157 day_ = days + 1; // days is index start form 0 and day start form 1.
158 }
159
comparePickerDateRange(const std::pair<PickerDate,PickerDate> & pair,const std::pair<PickerDate,PickerDate> & other)160 bool comparePickerDateRange(
161 const std::pair<PickerDate, PickerDate>& pair, const std::pair<PickerDate, PickerDate>& other)
162 {
163 if (pair.first == other.first) {
164 return pair.second < other.second;
165 }
166 return pair.first < other.first;
167 }
168
SortAndMergeDisabledDateRange(std::vector<std::pair<PickerDate,PickerDate>> & disableDateRange)169 void PickerDate::SortAndMergeDisabledDateRange(std::vector<std::pair<PickerDate, PickerDate>>& disableDateRange)
170 {
171 if (disableDateRange.empty()) {
172 return;
173 }
174 std::sort(disableDateRange.begin(), disableDateRange.end(), comparePickerDateRange);
175 std::vector<std::pair<PickerDate, PickerDate>> mergedRange;
176 for (const auto& range : disableDateRange) {
177 if (!mergedRange.empty() && range.first.ToDays() <= (mergedRange.back().second.ToDays() + 1)) {
178 mergedRange.back().second =
179 mergedRange.back().second < range.second ? range.second : mergedRange.back().second;
180 } else {
181 mergedRange.push_back(range);
182 }
183 }
184 disableDateRange.clear();
185 disableDateRange = mergedRange;
186 }
187
GetAvailableNextDay(const PickerDate & date,const PickerDate & start,const PickerDate & end,std::vector<std::pair<PickerDate,PickerDate>> & disableDateRange,bool isAdd)188 PickerDate PickerDate::GetAvailableNextDay(const PickerDate& date, const PickerDate& start, const PickerDate& end,
189 std::vector<std::pair<PickerDate, PickerDate>>& disableDateRange, bool isAdd)
190 {
191 PickerDate retDate = PickerDate(0, 0, 0);
192 if ((end.GetYear() != 0 && end < date) || (start.GetYear() != 0 && date < start)) {
193 return retDate;
194 }
195 if (isAdd) {
196 for (const auto& range : disableDateRange) {
197 if (range.first <= date && date <= range.second) {
198 return end.GetYear() != 0 && end < range.second ? retDate : NextDay(range.second);
199 }
200 }
201 return date;
202 } else {
203 for (const auto& range : disableDateRange) {
204 if (range.first <= date && date <= range.second) {
205 return start.GetYear() != 0 && range.first < start ? retDate : PrevDay(range.first);
206 }
207 }
208 return date;
209 }
210 }
211
PrevDay(const PickerDate & dateObj)212 PickerDate PickerDate::PrevDay(const PickerDate& dateObj)
213 {
214 PickerDate date = dateObj;
215 if (date.GetDay() > 1) {
216 date.SetDay(date.GetDay() - 1);
217 return date;
218 }
219 if (date.GetMonth() == 1) {
220 date.SetMonth(MAX_MONTH);
221 auto getYear = date.GetYear();
222 date.SetYear(date.GetYear() == 1 ? MAX_YEAR : (getYear > 0 ? getYear - 1 : 0));
223 } else {
224 auto getMonth = date.GetMonth();
225 date.SetMonth(getMonth > 0 ? getMonth - 1 : 0);
226 }
227 date.SetDay(PickerDate::GetMaxDay(date.GetYear(), date.GetMonth()));
228 return date;
229 }
230
NextDay(const PickerDate & dateObj)231 PickerDate PickerDate::NextDay(const PickerDate& dateObj)
232 {
233 PickerDate date = dateObj;
234 auto maxDay = PickerDate::GetMaxDay(date.GetYear(), date.GetMonth());
235 if (maxDay > date.GetDay()) {
236 date.SetDay(date.GetDay() + 1);
237 return date;
238 }
239 date.SetDay(1);
240 if (date.GetMonth() < MAX_MONTH) {
241 date.SetMonth(date.GetMonth() + 1);
242 return date;
243 }
244 date.SetMonth(1);
245 date.SetYear(date.GetYear() == MAX_YEAR ? 1 : date.GetYear() + 1);
246 return date;
247 }
248
Current()249 PickerTime PickerTime::Current()
250 {
251 PickerTime time;
252 auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
253 auto local = std::localtime(&now);
254 if (local == nullptr) {
255 LOGE("get localtime failed.");
256 return time;
257 }
258 time.SetHour(local->tm_hour);
259 time.SetMinute(local->tm_min);
260 time.SetSecond(local->tm_sec);
261 return time;
262 }
263
ToString(bool jsonFormat,bool hasSecond,int32_t status) const264 std::string PickerTime::ToString(bool jsonFormat, bool hasSecond, int32_t status) const
265 {
266 if (!jsonFormat) {
267 if (!hasSecond) {
268 // use char ':' to split.
269 return std::to_string(hour_) + ":" + std::to_string(minute_);
270 }
271 // use char ':' to split.
272 return std::to_string(hour_) + ":" + std::to_string(minute_) + ":" + std::to_string(second_);
273 }
274
275 if (!hasSecond) {
276 // use json format chars
277 return std::string("{\"hour\":") + std::to_string(hour_) + ",\"minute\":" + std::to_string(minute_) +
278 ",\"status\":" + std::to_string(status) + "}";
279 }
280 // use json format chars
281 return std::string("{\"hour\":") + std::to_string(hour_) + ",\"minute\":" + std::to_string(minute_) +
282 ",\"second\":" + std::to_string(second_) + ",\"status\":" + std::to_string(status) + "}";
283 }
284
ToMinutes() const285 uint32_t PickerTime::ToMinutes() const
286 {
287 constexpr static uint32_t minInHour = 60;
288 return minute_ + hour_ * minInHour;
289 }
290
Current()291 PickerDateTime PickerDateTime::Current()
292 {
293 PickerDateTime dateTime;
294 dateTime.SetDate(PickerDate::Current());
295 dateTime.SetTime(PickerTime::Current());
296 return dateTime;
297 }
298
ToString(bool jsonFormat,int32_t status) const299 std::string PickerDateTime::ToString(bool jsonFormat, int32_t status) const
300 {
301 if (!jsonFormat) {
302 return date_.ToString(jsonFormat);
303 }
304
305 return std::string("{\"year\":") + std::to_string(date_.GetYear()) +
306 ",\"month\":" + std::to_string(date_.GetMonth()) +
307 ",\"day\":" + std::to_string(date_.GetDay()) +
308 ",\"hour\":" + std::to_string(time_.GetHour()) +
309 ",\"minute\":" + std::to_string(time_.GetMinute()) +
310 ",\"status\":" + std::to_string(status) + "}";
311 }
312
313 /*
314 * Lunar information in 200 years from 1900.
315 * <p>
316 * | 0 - 11(bit) | 12 - 15(bit) |
317 * month leap month
318 * If last 4bit is 1111 or 0000 means no leap month.
319 * If the last 4bit in next data is 1111, the days of leap month is 30 days,
320 * otherwise, the days of leap month is 29days.
321 */
322 const uint16_t LunarCalculator::LUNAR_INFO[] = {
323 0x6aa0, 0xbaa3, 0xab50,
324 0x4bd8, 0x4ae0, 0xa570, 0x54d5, 0xd260, 0xd950, 0x5554, 0x56af,
325 0x9ad0, 0x55d2, 0x4ae0, 0xa5b6, 0xa4d0, 0xd250, 0xd295, 0xb54f,
326 0xd6a0, 0xada2, 0x95b0, 0x4977, 0x497f, 0xa4b0, 0xb4b5, 0x6a50,
327 0x6d40, 0xab54, 0x2b6f, 0x9570, 0x52f2, 0x4970, 0x6566, 0xd4a0,
328 0xea50, 0x6a95, 0x5adf, 0x2b60, 0x86e3, 0x92ef, 0xc8d7, 0xc95f,
329 0xd4a0, 0xd8a6, 0xb55f, 0x56a0, 0xa5b4, 0x25df, 0x92d0, 0xd2b2,
330 0xa950, 0xb557, 0x6ca0, 0xb550, 0x5355, 0x4daf, 0xa5b0, 0x4573,
331 0x52bf, 0xa9a8, 0xe950, 0x6aa0, 0xaea6, 0xab50, 0x4b60, 0xaae4,
332 0xa570, 0x5260, 0xf263, 0xd950, 0x5b57, 0x56a0, 0x96d0, 0x4dd5,
333 0x4ad0, 0xa4d0, 0xd4d4, 0xd250, 0xd558, 0xb540, 0xb6a0, 0x95a6,
334 0x95bf, 0x49b0, 0xa974, 0xa4b0, 0xb27a, 0x6a50, 0x6d40, 0xaf46,
335 0xab60, 0x9570, 0x4af5, 0x4970, 0x64b0, 0x74a3, 0xea50, 0x6b58,
336 0x5ac0, 0xab60, 0x96d5, 0x92e0, 0xc960, 0xd954, 0xd4a0, 0xda50,
337 0x7552, 0x56a0, 0xabb7, 0x25d0, 0x92d0, 0xcab5, 0xa950, 0xb4a0,
338 0xbaa4, 0xad50, 0x55d9, 0x4ba0, 0xa5b0, 0x5176, 0x52bf, 0xa930,
339 0x7954, 0x6aa0, 0xad50, 0x5b52, 0x4b60, 0xa6e6, 0xa4e0, 0xd260,
340 0xea65, 0xd530, 0x5aa0, 0x76a3, 0x96d0, 0x4afb, 0x4ad0, 0xa4d0,
341 0xd0b6, 0xd25f, 0xd520, 0xdd45, 0xb5a0, 0x56d0, 0x55b2, 0x49b0,
342 0xa577, 0xa4b0, 0xaa50, 0xb255, 0x6d2f, 0xada0, 0x4b63, 0x937f,
343 0x49f8, 0x4970, 0x64b0, 0x68a6, 0xea5f, 0x6b20, 0xa6c4, 0xaaef,
344 0x92e0, 0xd2e3, 0xc960, 0xd557, 0xd4a0, 0xda50, 0x5d55, 0x56a0,
345 0xa6d0, 0x55d4, 0x52d0, 0xa9b8, 0xa950, 0xb4a0, 0xb6a6, 0xad50,
346 0x55a0, 0xaba4, 0xa5b0, 0x52b0, 0xb273, 0x6930, 0x7337, 0x6aa0,
347 0xad50, 0x4b55, 0x4b6f, 0xa570, 0x54e4, 0xd260, 0xe968, 0xd520,
348 0xdaa0, 0x6aa6, 0x56df, 0x4ae0, 0xa9d4, 0xa4d0, 0xd150, 0xf252,
349 0xd520, 0xdd45, 0xb5a0, 0x56d0
350 };
351
352 bool PickerStringFormatter::inited_ = false;
353 const std::string PickerStringFormatter::empty_;
354 std::vector<std::string> PickerStringFormatter::years_; // year from 1900 to 2100,count is 201
355 std::vector<std::string> PickerStringFormatter::solarMonths_; // solar month from 1 to 12,count is 12
356 std::vector<std::string> PickerStringFormatter::solarDays_; // solar day from 1 to 31, count is 31
357 std::vector<std::string> PickerStringFormatter::lunarMonths_; // lunar month from 1 to 24, count is 24
358 std::vector<std::string> PickerStringFormatter::lunarDays_; // lunar day from 1 to 30, count is 30
359 std::vector<std::string> PickerStringFormatter::tagOrder_; // order of year month day
360
Init()361 void PickerStringFormatter::Init()
362 {
363 if (inited_) {
364 return;
365 }
366 years_.resize(201); // year from 1900 to 2100,count is 201
367 solarMonths_.resize(12); // solar month from 1 to 12,count is 12
368 solarDays_.resize(31); // solar day from 1 to 31, count is 31
369 lunarMonths_.resize(24); // lunar month from 1 to 24, count is 24
370 lunarDays_.resize(30); // lunar day from 1 to 30, count is 30
371 // init year from 1900 to 2100
372 for (uint32_t year = 1900; year <= 2100; ++year) {
373 DateTime date;
374 date.year = year;
375 years_[year - 1900] = Localization::GetInstance()->FormatDateTime(date, "y"); // index start from 0
376 }
377 // init solar month from 1 to 12
378 auto months = Localization::GetInstance()->GetMonths(true);
379 for (uint32_t month = 1; month <= 12; ++month) {
380 if (month - 1 < months.size()) {
381 solarMonths_[month - 1] = months[month - 1];
382 continue;
383 }
384 DateTime date;
385 date.month = month - 1; // W3C's month start from 0 to 11
386 solarMonths_[month - 1] = Localization::GetInstance()->FormatDateTime(date, "M"); // index start from 0
387 }
388 // init solar day from 1 to 31
389 for (uint32_t day = 1; day <= 31; ++day) {
390 DateTime date;
391 date.day = day;
392 solarDays_[day - 1] = Localization::GetInstance()->FormatDateTime(date, "d"); // index start from 0
393 }
394 // init lunar month from 1 to 24 which is 1th, 2th, ... leap 1th, leap 2th ...
395 for (uint32_t index = 1; index <= 24; ++index) {
396 uint32_t month = (index > 12 ? index - 12 : index);
397 bool isLeap = (index > 12);
398 lunarMonths_[index - 1] = Localization::GetInstance()->GetLunarMonth(month, isLeap); // index start from 0
399 }
400 // init lunar day from 1 to 30
401 for (uint32_t day = 1; day <= 30; ++day) {
402 lunarDays_[day - 1] = Localization::GetInstance()->GetLunarDay(day); // index start from 0
403 }
404 inited_ = true;
405 Localization::GetInstance()->SetOnChange([]() { PickerStringFormatter::inited_ = false; });
406 }
407
GetYear(uint32_t year)408 const std::string& PickerStringFormatter::GetYear(uint32_t year)
409 {
410 Init();
411 if (!(1900 <= year && year <= 2100)) { // year in [1900,2100]
412 return empty_;
413 }
414 return years_[year - 1900]; // index in [0, 200]
415 }
416
GetSolarMonth(uint32_t month)417 const std::string& PickerStringFormatter::GetSolarMonth(uint32_t month)
418 {
419 Init();
420 if (!(1 <= month && month <= 12)) { // solar month in [1,12]
421 return empty_;
422 }
423 return solarMonths_[month - 1]; // index in [0,11]
424 }
425
GetSolarDay(uint32_t day)426 const std::string& PickerStringFormatter::GetSolarDay(uint32_t day)
427 {
428 Init();
429 if (!(1 <= day && day <= 31)) { // solar day in [1,31]
430 return empty_;
431 }
432 return solarDays_[day - 1]; // index in [0,30]
433 }
434
GetLunarMonth(uint32_t month,bool isLeap)435 const std::string& PickerStringFormatter::GetLunarMonth(uint32_t month, bool isLeap)
436 {
437 Init();
438 uint32_t index = (isLeap ? month + 12 : month); // leap month is behind 12 index
439 if (!(1 <= index && index <= 24)) { // lunar month need in [1,24]
440 return empty_;
441 }
442 return lunarMonths_[index - 1]; // index in [0,23]
443 }
444
GetLunarDay(uint32_t day)445 const std::string& PickerStringFormatter::GetLunarDay(uint32_t day)
446 {
447 Init();
448 if (!(1 <= day && day <= 30)) { // lunar day need in [1,30]
449 return empty_;
450 }
451 return lunarDays_[day - 1]; // index in [0,29]
452 }
453
GetTagOrder()454 const std::vector<std::string>& PickerStringFormatter::GetTagOrder()
455 {
456 tagOrder_.clear();
457 Localization::GetInstance()->GetDateColumnFormatOrder(tagOrder_);
458 return tagOrder_;
459 }
460
461 } // namespace OHOS::Ace
462