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 "base/i18n/date_time_sequence.h"
16 #include "unicode/dtptngen.h"
17 #include "unicode/smpdtfmt.h"
18 #include <regex>
19 
20 namespace OHOS::Ace {
21 
22 using namespace icu;
23 
GetDateOrder(const std::string & locale)24 OrderResult DateTimeSequence::GetDateOrder(const std::string& locale)
25 {
26     UErrorCode icuStatus = U_ZERO_ERROR;
27     OrderResult orderResult;
28     icu::Locale localeObj = icu::Locale::forLanguageTag(locale.data(), icuStatus);
29     if (U_FAILURE(icuStatus)) {
30         return orderResult;
31     }
32     const char* language = localeObj.getBaseName();
33     if (language == nullptr) {
34         return orderResult;
35     }
36     const std::unordered_map<std::string, std::string> DATE_ORDER_MAP = {
37         { "ug", "M-d-y" },
38         { "ar", "y-M-d" },
39         { "fa", "y-M-d" },
40         { "ur", "y-M-d" },
41         { "iw", "y-M-d" },
42         { "he", "y-M-d" },
43     };
44     std::string languageTag = language;
45     if (DATE_ORDER_MAP.find(languageTag) != DATE_ORDER_MAP.end()) {
46         orderResult.dateOrder = DATE_ORDER_MAP.find(languageTag)->second;
47         return orderResult;
48     }
49     icu::SimpleDateFormat* formatter = static_cast<icu::SimpleDateFormat*>
50         (icu::DateFormat::createDateInstance(icu::DateFormat::EStyle::kDefault, localeObj));
51     if (U_FAILURE(icuStatus) || formatter == nullptr) {
52         return orderResult;
53     }
54     std::string tempValue;
55     icu::UnicodeString unistr;
56     formatter->toPattern(unistr);
57     unistr.toUTF8String<std::string>(tempValue);
58     std::string value = ModifyOrder(tempValue);
59     std::regex pattern("d+");
60     std::regex reg("M+");
61     value = regex_replace(value, pattern, "d");
62     value = regex_replace(value, reg, "M");
63     orderResult = {value};
64     delete formatter;
65     return orderResult;
66 }
67 
GetDateTimeOrder(const std::string & locale)68 OrderResult DateTimeSequence::GetDateTimeOrder(const std::string& locale)
69 {
70     UErrorCode status = U_ZERO_ERROR;
71     OrderResult orderResult;
72     icu::Locale localeObj = icu::Locale::forLanguageTag(locale.data(), status);
73     if (U_FAILURE(status)) {
74         orderResult.dateTimeOrder = "-1";
75         return orderResult;
76     }
77     const std::unordered_map<std::string, std::string> DATETIME_ORDER_MAP = {
78         { "ug", "01" },
79         { "ar", "10" },
80         { "fa", "01" },
81         { "ur", "10" },
82         { "iw", "10" },
83         { "he", "10" },
84     };
85     const char* language = localeObj.getBaseName();
86     std::string languageTag = language == nullptr ? "" : language;
87     if (DATETIME_ORDER_MAP.find(languageTag) != DATETIME_ORDER_MAP.end()) {
88         orderResult.dateTimeOrder = DATETIME_ORDER_MAP.find(languageTag)->second;
89         return orderResult;
90     }
91     icu::SimpleDateFormat* formatter = static_cast<icu::SimpleDateFormat*>
92         (icu::DateFormat::createDateInstance(icu::DateFormat::EStyle::kDateTime, localeObj));
93     if (status != U_ZERO_ERROR || formatter == nullptr) {
94         orderResult.dateTimeOrder = "-1";
95         return orderResult;
96     }
97     icu::UnicodeString pattern;
98     formatter->toPattern(pattern);
99     std::string result;
100     pattern.toUTF8String<std::string>(result);
101 
102     std::regex pattrn("0([^01]+)1");
103     std::smatch match;
104     bool found = std::regex_search(result, match, pattrn);
105     // 1 represent date position, 0 represent time position
106     std::string dateTimeOrder = found ? "01" : "10";
107     orderResult = {result, dateTimeOrder};
108     delete formatter;
109     return orderResult;
110 }
111 
GetAmPmTimeOrder(const std::string & locale)112 OrderResult DateTimeSequence::GetAmPmTimeOrder(const std::string& locale)
113 {
114     UErrorCode status = U_ZERO_ERROR;
115     OrderResult orderResult;
116     icu::Locale localeObj = icu::Locale::forLanguageTag(locale.data(), status);
117     if (status != U_ZERO_ERROR) {
118         orderResult.amPmTimeOrder = "-1";
119         return orderResult;
120     }
121     const char* language = localeObj.getBaseName();
122     const std::unordered_map<std::string, std::string> AM_PM_TIME_ORDER_MAP = {
123         { "ug", "10" },
124         { "ar", "01" },
125         { "fa", "01" },
126         { "ur", "10" },
127         { "iw", "01" },
128         { "he", "01" },
129         { "bo", "10" },
130     };
131     std::string languageTag = language == nullptr ? "" : language;
132     if (AM_PM_TIME_ORDER_MAP.find(languageTag) != AM_PM_TIME_ORDER_MAP.end()) {
133         orderResult.amPmTimeOrder = AM_PM_TIME_ORDER_MAP.find(languageTag)->second;
134         return orderResult;
135     }
136     icu::DateTimePatternGenerator* gen = icu::DateTimePatternGenerator::createInstance(localeObj, status);
137     icu::UnicodeString skeleton("h");
138     if (gen == nullptr) {
139         orderResult.amPmTimeOrder = "-1";
140         return orderResult;
141     }
142     icu::UnicodeString resultStr = gen->getBestPattern(skeleton, status);
143     if (U_FAILURE(status)) {
144         orderResult.amPmTimeOrder = "-1";
145         return orderResult;
146     }
147     std::string result;
148     resultStr.toUTF8String<std::string>(result);
149     std::regex pattrn("a[^ah]*h");
150     std::smatch match;
151     bool found = std::regex_search(result, match, pattrn);
152     // 0 represent am/pm position
153     std::string amPmTimeOrder = found ? "10" : "01"; // 1 represent time position
154     orderResult = {result, "", amPmTimeOrder};
155     delete gen;
156     return orderResult;
157 }
158 
ModifyOrder(std::string & pattern)159 std::string DateTimeSequence::ModifyOrder(std::string& pattern)
160 {
161     int order[3] = { 0 }; // total 3 elements 'y', 'M'/'L', 'd'
162     int lengths[4] = { 0 }; // first elements is the currently found elememnts, thus 4 elements totally.
163     bool flag = true;
164     for (size_t i = 0; i < pattern.length(); ++i) {
165         char ch = pattern[i];
166         if (flag && std::isalpha(ch)) {
167             ProcessNormal(ch, order, 3, lengths, 4); // 3, 4 are lengths of these arrays
168         } else if (ch == '\'') {
169             flag = !flag;
170         }
171     }
172     std::unordered_map<char, int> pattern2index = {
173         { 'y', 1 },
174         { 'M', 2 },
175         { 'd', 3 },
176     };
177     std::string ret;
178     for (int i = 0; i < 3; ++i) { // 3 is the size of orders
179         auto it = pattern2index.find(order[i]);
180         if (it == pattern2index.end()) {
181             continue;
182         }
183         int index = it->second;
184         if ((lengths[index] > 0) && (lengths[index] <= 6)) { // 6 is the max length of a filed
185             ret.append(lengths[index], order[i]);
186         }
187         if (i < 2) { // 2 is the size of the order minus one
188             ret.append(1, '-');
189         }
190     }
191     return ret;
192 }
193 
ProcessNormal(char ch,int * order,size_t orderSize,int * lengths,size_t lengsSize)194 void DateTimeSequence::ProcessNormal(char ch, int* order, size_t orderSize, int* lengths, size_t lengsSize)
195 {
196     char adjust;
197     int index = -1;
198     if (ch == 'd') {
199         adjust = 'd';
200         index = 3; // 3 is the index of 'd'
201     } else if ((ch == 'L') || (ch == 'M')) {
202         adjust = 'M';
203         index = 2; // 2 is the index of 'M'
204     } else if (ch == 'y') {
205         adjust = 'y';
206         index = 1;
207     } else {
208         return;
209     }
210     if ((index < 0) || (index >= static_cast<int>(lengsSize))) {
211         return;
212     }
213     if (lengths[index] == 0) {
214         if (lengths[0] >= 3) { // 3 is the index of order
215             return;
216         }
217         order[lengths[0]] = static_cast<int>(adjust);
218         ++lengths[0];
219         lengths[index] = 1;
220     } else {
221         ++lengths[index];
222     }
223 }
224 
225 } // namespace OHOS::Ace