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 
Current()22 PickerDate PickerDate::Current()
23 {
24     PickerDate date;
25     auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
26     auto local = std::localtime(&now);
27     if (local == nullptr) {
28         LOGE("get localtime failed.");
29         return date;
30     }
31     date.SetYear(local->tm_year + 1900); // local date start from 1900
32     date.SetMonth(local->tm_mon + 1);    // local month start from 0 to 11, need add one.
33     date.SetDay(local->tm_mday);
34     date.SetWeek(local->tm_wday);
35     return date;
36 }
37 
GetMaxDay(uint32_t year,uint32_t month)38 uint32_t PickerDate::GetMaxDay(uint32_t year, uint32_t month)
39 {
40     if (month == 2) { // days count in february is different between leap year and other.
41         bool leapYear = IsLeapYear(year);
42         return (leapYear ? 29 : 28); // leap year's february has 29 days, other has 28 days.
43     }
44 
45     switch (month) {
46         case 1:        // january
47         case 3:        // march
48         case 5:        // may
49         case 7:        // july
50         case 8:        // august
51         case 10:       // october
52         case 12:       // december
53             return 31; // upper months has 31 days
54         default:
55             return 30; // other month has 30 days
56     }
57 }
58 
IsLeapYear(uint32_t year)59 bool PickerDate::IsLeapYear(uint32_t year)
60 {
61     if (year % 100 == 0) {        // special case: year can divided by 100
62         return (year % 400 == 0); // leap year equal that can divided by 400.
63     }
64 
65     return (year % 4 == 0); // other case, leap year equal that can divided by 4.
66 }
67 
ToString(bool jsonFormat,int32_t status) const68 std::string PickerDate::ToString(bool jsonFormat, int32_t status) const
69 {
70     if (!jsonFormat) {
71         DateTime date;
72         date.year = year_;
73         date.month = month_ ? month_ - 1 : 0; // W3C's month start from 0 to 11
74         date.day = day_;
75         return Localization::GetInstance()->FormatDateTime(date, DateTimeStyle::FULL, DateTimeStyle::NONE);
76     }
77 
78     return std::string("{\"year\":") + std::to_string(year_) + ",\"month\":" + std::to_string(month_) +
79            ",\"day\":" + std::to_string(day_) + ",\"status\":" + std::to_string(status) + "}";
80 }
81 
ToDays() const82 uint32_t PickerDate::ToDays() const
83 {
84     uint32_t days = 0;
85     days += day_ ? day_ - 1 : 0; // day start from 1
86     // month start from 1
87     for (uint32_t month = 1; month < month_; ++month) {
88         days += PickerDate::GetMaxDay(year_, month);
89     }
90     // year start from 1900 or 1
91     uint32_t startYear = 1900;
92     if (year_ < startYear) {
93         startYear = 1;
94     }
95     for (uint32_t year = startYear; year < year_; ++year) {
96         // leap year has 366 days, other year has 365 days.
97         days += (PickerDate::IsLeapYear(year) ? 366 : 365);
98     }
99     return days;
100 }
101 
FromDays(uint32_t days)102 void PickerDate::FromDays(uint32_t days)
103 {
104     for (year_ = 1900; year_ <= 2100; ++year_) { // year start from 1900 to 2100.
105         // leap year has 366 days, other year has 365 days;
106         uint32_t daysInYear = (PickerDate::IsLeapYear(year_) ? 366 : 365);
107         if (days < daysInYear) {
108             break;
109         } else {
110             days -= daysInYear;
111         }
112     }
113 
114     for (month_ = 1; month_ <= 12; ++month_) { // month start from 1 to 12
115         uint32_t daysInMonth = PickerDate::GetMaxDay(year_, month_);
116         if (days < daysInMonth) {
117             break;
118         } else {
119             days -= daysInMonth;
120         }
121     }
122 
123     day_ = days + 1; // days is index start form 0 and day start form 1.
124 }
125 
Current()126 PickerTime PickerTime::Current()
127 {
128     PickerTime time;
129     auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
130     auto local = std::localtime(&now);
131     if (local == nullptr) {
132         LOGE("get localtime failed.");
133         return time;
134     }
135     time.SetHour(local->tm_hour);
136     time.SetMinute(local->tm_min);
137     time.SetSecond(local->tm_sec);
138     return time;
139 }
140 
ToString(bool jsonFormat,bool hasSecond,int32_t status) const141 std::string PickerTime::ToString(bool jsonFormat, bool hasSecond, int32_t status) const
142 {
143     if (!jsonFormat) {
144         if (!hasSecond) {
145             // use char ':' to split.
146             return std::to_string(hour_) + ":" + std::to_string(minute_);
147         }
148         // use char ':' to split.
149         return std::to_string(hour_) + ":" + std::to_string(minute_) + ":" + std::to_string(second_);
150     }
151 
152     if (!hasSecond) {
153         // use json format chars
154         return std::string("{\"hour\":") + std::to_string(hour_) + ",\"minute\":" + std::to_string(minute_) +
155             ",\"status\":" + std::to_string(status) + "}";
156     }
157     // use json format chars
158     return std::string("{\"hour\":") + std::to_string(hour_) + ",\"minute\":" + std::to_string(minute_) +
159             ",\"second\":" + std::to_string(second_) + ",\"status\":" + std::to_string(status) + "}";
160 }
161 
Current()162 PickerDateTime PickerDateTime::Current()
163 {
164     PickerDateTime dateTime;
165     dateTime.SetDate(PickerDate::Current());
166     dateTime.SetTime(PickerTime::Current());
167     return dateTime;
168 }
169 
ToString(bool jsonFormat,int32_t status) const170 std::string PickerDateTime::ToString(bool jsonFormat, int32_t status) const
171 {
172     if (!jsonFormat) {
173         return date_.ToString(jsonFormat);
174     }
175 
176     return std::string("{\"year\":") + std::to_string(date_.GetYear()) +
177            ",\"month\":" + std::to_string(date_.GetMonth()) +
178            ",\"day\":" + std::to_string(date_.GetDay()) +
179            ",\"hour\":" + std::to_string(time_.GetHour()) +
180            ",\"minute\":" + std::to_string(time_.GetMinute()) +
181            ",\"status\":" + std::to_string(status) + "}";
182 }
183 
184 /*
185  * Lunar information in 200 years from 1900.
186  * <p>
187  * | 0 - 11(bit) | 12 - 15(bit) |
188  * month      leap month
189  * If last 4bit is 1111 or 0000 means no leap month.
190  * If the last 4bit in next data is 1111, the days of leap month is 30 days,
191  * otherwise, the days of leap month is 29days.
192  */
193 const uint16_t LunarCalculator::LUNAR_INFO[] = {
194     0x6aa0, 0xbaa3, 0xab50,
195     0x4bd8, 0x4ae0, 0xa570, 0x54d5, 0xd260, 0xd950, 0x5554, 0x56af,
196     0x9ad0, 0x55d2, 0x4ae0, 0xa5b6, 0xa4d0, 0xd250, 0xd295, 0xb54f,
197     0xd6a0, 0xada2, 0x95b0, 0x4977, 0x497f, 0xa4b0, 0xb4b5, 0x6a50,
198     0x6d40, 0xab54, 0x2b6f, 0x9570, 0x52f2, 0x4970, 0x6566, 0xd4a0,
199     0xea50, 0x6a95, 0x5adf, 0x2b60, 0x86e3, 0x92ef, 0xc8d7, 0xc95f,
200     0xd4a0, 0xd8a6, 0xb55f, 0x56a0, 0xa5b4, 0x25df, 0x92d0, 0xd2b2,
201     0xa950, 0xb557, 0x6ca0, 0xb550, 0x5355, 0x4daf, 0xa5b0, 0x4573,
202     0x52bf, 0xa9a8, 0xe950, 0x6aa0, 0xaea6, 0xab50, 0x4b60, 0xaae4,
203     0xa570, 0x5260, 0xf263, 0xd950, 0x5b57, 0x56a0, 0x96d0, 0x4dd5,
204     0x4ad0, 0xa4d0, 0xd4d4, 0xd250, 0xd558, 0xb540, 0xb6a0, 0x95a6,
205     0x95bf, 0x49b0, 0xa974, 0xa4b0, 0xb27a, 0x6a50, 0x6d40, 0xaf46,
206     0xab60, 0x9570, 0x4af5, 0x4970, 0x64b0, 0x74a3, 0xea50, 0x6b58,
207     0x5ac0, 0xab60, 0x96d5, 0x92e0, 0xc960, 0xd954, 0xd4a0, 0xda50,
208     0x7552, 0x56a0, 0xabb7, 0x25d0, 0x92d0, 0xcab5, 0xa950, 0xb4a0,
209     0xbaa4, 0xad50, 0x55d9, 0x4ba0, 0xa5b0, 0x5176, 0x52bf, 0xa930,
210     0x7954, 0x6aa0, 0xad50, 0x5b52, 0x4b60, 0xa6e6, 0xa4e0, 0xd260,
211     0xea65, 0xd530, 0x5aa0, 0x76a3, 0x96d0, 0x4afb, 0x4ad0, 0xa4d0,
212     0xd0b6, 0xd25f, 0xd520, 0xdd45, 0xb5a0, 0x56d0, 0x55b2, 0x49b0,
213     0xa577, 0xa4b0, 0xaa50, 0xb255, 0x6d2f, 0xada0, 0x4b63, 0x937f,
214     0x49f8, 0x4970, 0x64b0, 0x68a6, 0xea5f, 0x6b20, 0xa6c4, 0xaaef,
215     0x92e0, 0xd2e3, 0xc960, 0xd557, 0xd4a0, 0xda50, 0x5d55, 0x56a0,
216     0xa6d0, 0x55d4, 0x52d0, 0xa9b8, 0xa950, 0xb4a0, 0xb6a6, 0xad50,
217     0x55a0, 0xaba4, 0xa5b0, 0x52b0, 0xb273, 0x6930, 0x7337, 0x6aa0,
218     0xad50, 0x4b55, 0x4b6f, 0xa570, 0x54e4, 0xd260, 0xe968, 0xd520,
219     0xdaa0, 0x6aa6, 0x56df, 0x4ae0, 0xa9d4, 0xa4d0, 0xd150, 0xf252,
220     0xd520, 0xdd45, 0xb5a0, 0x56d0
221 };
222 
223 bool PickerStringFormatter::inited_ = false;
224 const std::string PickerStringFormatter::empty_;
225 std::vector<std::string> PickerStringFormatter::years_; // year from 1900 to 2100,count is 201
226 std::vector<std::string> PickerStringFormatter::solarMonths_; // solar month from 1 to 12,count is 12
227 std::vector<std::string> PickerStringFormatter::solarDays_; // solar day from 1 to 31, count is 31
228 std::vector<std::string> PickerStringFormatter::lunarMonths_; // lunar month from 1 to 24, count is 24
229 std::vector<std::string> PickerStringFormatter::lunarDays_; // lunar day from 1 to 30, count is 30
230 std::vector<std::string> PickerStringFormatter::tagOrder_; // order of year month day
231 
Init()232 void PickerStringFormatter::Init()
233 {
234     if (inited_) {
235         return;
236     }
237     years_.resize(201); // year from 1900 to 2100,count is 201
238     solarMonths_.resize(12); // solar month from 1 to 12,count is 12
239     solarDays_.resize(31); // solar day from 1 to 31, count is 31
240     lunarMonths_.resize(24); // lunar month from 1 to 24, count is 24
241     lunarDays_.resize(30); // lunar day from 1 to 30, count is 30
242     // init year from 1900 to 2100
243     for (uint32_t year = 1900; year <= 2100; ++year) {
244         DateTime date;
245         date.year = year;
246         years_[year - 1900] = Localization::GetInstance()->FormatDateTime(date, "y"); // index start from 0
247     }
248     // init solar month from 1 to 12
249     auto months = Localization::GetInstance()->GetMonths(true);
250     for (uint32_t month = 1; month <= 12; ++month) {
251         if (month - 1 < months.size()) {
252             solarMonths_[month - 1] = months[month - 1];
253             continue;
254         }
255         DateTime date;
256         date.month = month - 1; // W3C's month start from 0 to 11
257         solarMonths_[month - 1] = Localization::GetInstance()->FormatDateTime(date, "M"); // index start from 0
258     }
259     // init solar day from 1 to 31
260     for (uint32_t day = 1; day <= 31; ++day) {
261         DateTime date;
262         date.day = day;
263         solarDays_[day - 1] = Localization::GetInstance()->FormatDateTime(date, "d"); // index start from 0
264     }
265     // init lunar month from 1 to 24 which is 1th, 2th, ... leap 1th, leap 2th ...
266     for (uint32_t index = 1; index <= 24; ++index) {
267         uint32_t month = (index > 12 ? index - 12 : index);
268         bool isLeap = (index > 12);
269         lunarMonths_[index - 1] = Localization::GetInstance()->GetLunarMonth(month, isLeap); // index start from 0
270     }
271     // init lunar day from 1 to 30
272     for (uint32_t day = 1; day <= 30; ++day) {
273         lunarDays_[day - 1] = Localization::GetInstance()->GetLunarDay(day); // index start from 0
274     }
275     inited_ = true;
276     Localization::GetInstance()->SetOnChange([]() { PickerStringFormatter::inited_ = false; });
277 }
278 
GetYear(uint32_t year)279 const std::string& PickerStringFormatter::GetYear(uint32_t year)
280 {
281     Init();
282     if (!(1900 <= year && year <= 2100)) { // year in [1900,2100]
283         return empty_;
284     }
285     return years_[year - 1900]; // index in [0, 200]
286 }
287 
GetSolarMonth(uint32_t month)288 const std::string& PickerStringFormatter::GetSolarMonth(uint32_t month)
289 {
290     Init();
291     if (!(1 <= month && month <= 12)) { // solar month in [1,12]
292         return empty_;
293     }
294     return solarMonths_[month - 1]; // index in [0,11]
295 }
296 
GetSolarDay(uint32_t day)297 const std::string& PickerStringFormatter::GetSolarDay(uint32_t day)
298 {
299     Init();
300     if (!(1 <= day && day <= 31)) { // solar day in [1,31]
301         return empty_;
302     }
303     return solarDays_[day - 1]; // index in [0,30]
304 }
305 
GetLunarMonth(uint32_t month,bool isLeap)306 const std::string& PickerStringFormatter::GetLunarMonth(uint32_t month, bool isLeap)
307 {
308     Init();
309     uint32_t index = (isLeap ? month + 12 : month); // leap month is behind 12 index
310     if (!(1 <= index && index <= 24)) { // lunar month need in [1,24]
311         return empty_;
312     }
313     return lunarMonths_[index - 1]; // index in [0,23]
314 }
315 
GetLunarDay(uint32_t day)316 const std::string& PickerStringFormatter::GetLunarDay(uint32_t day)
317 {
318     Init();
319     if (!(1 <= day && day <= 30)) { // lunar day need in [1,30]
320         return empty_;
321     }
322     return lunarDays_[day - 1]; // index in [0,29]
323 }
324 
GetTagOrder()325 const std::vector<std::string>& PickerStringFormatter::GetTagOrder()
326 {
327     tagOrder_.clear();
328     Localization::GetInstance()->GetDateColumnFormatOrder(tagOrder_);
329     return tagOrder_;
330 }
331 
332 } // namespace OHOS::Ace
333