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