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
16 #include "i18n_hilog.h"
17 #include "locale_config.h"
18 #include "ohos/init_data.h"
19 #include "unicode/datefmt.h"
20 #include "utils.h"
21 #include "simple_date_time_format.h"
22
23 namespace OHOS {
24 namespace Global {
25 namespace I18n {
26 const char SimpleDateTimeFormat::SEPARATOR = '\'';
27 const std::string SimpleDateTimeFormat::VALID_PATTERN = "aAbBcCdDeEFgGhHjJkKLmMOqQrsSuUvVwWxXyYzZ";
28 bool SimpleDateTimeFormat::icuInitialized = SimpleDateTimeFormat::Init();
29
SimpleDateTimeFormat(const std::string & skeletonOrPattern,std::shared_ptr<LocaleInfo> localeInfo,bool isBestPattern,I18nErrorCode & errCode)30 SimpleDateTimeFormat::SimpleDateTimeFormat(const std::string& skeletonOrPattern, std::shared_ptr<LocaleInfo> localeInfo,
31 bool isBestPattern, I18nErrorCode& errCode)
32 {
33 std::string locale;
34 if (localeInfo == nullptr) {
35 locale = LocaleConfig::GetEffectiveLocale();
36 } else {
37 locale = localeInfo->ToString();
38 }
39 InitSimpleDateTimeFormat(skeletonOrPattern, locale, isBestPattern, errCode);
40 }
41
SimpleDateTimeFormat(const std::string & skeletonOrPattern,const std::string & localeTag,bool isBestPattern,I18nErrorCode & errCode)42 SimpleDateTimeFormat::SimpleDateTimeFormat(const std::string& skeletonOrPattern, const std::string& localeTag,
43 bool isBestPattern, I18nErrorCode& errCode)
44 {
45 std::string locale = localeTag;
46 if (locale.empty()) {
47 locale = LocaleConfig::GetEffectiveLocale();
48 }
49 InitSimpleDateTimeFormat(skeletonOrPattern, locale, isBestPattern, errCode);
50 }
51
InitSimpleDateTimeFormat(const std::string & skeletonOrPattern,const std::string & locale,bool isBestPattern,I18nErrorCode & errCode)52 void SimpleDateTimeFormat::InitSimpleDateTimeFormat(const std::string& skeletonOrPattern, const std::string& locale,
53 bool isBestPattern, I18nErrorCode& errCode)
54 {
55 if (skeletonOrPattern.empty()) {
56 HILOG_ERROR_I18N("SimpleDateTimeFormat::InitSimpleDateTimeFormat: skeleton or pattern is empty.");
57 errCode = isBestPattern ? I18nErrorCode::INVALID_DATE_TIME_FORMAT_PATTERN :
58 I18nErrorCode::INVALID_DATE_TIME_FORMAT_SKELETON;
59 return;
60 }
61
62 if (locale.empty() || !LocaleConfig::IsValidTag(locale)) {
63 HILOG_ERROR_I18N("SimpleDateTimeFormat::InitSimpleDateTimeFormat: locale %{public}s is invalid.",
64 locale.c_str());
65 errCode = I18nErrorCode::INVALID_LOCALE_TAG;
66 return;
67 }
68 UErrorCode status = U_ZERO_ERROR;
69 icu::Locale icuLocale = icu::Locale::forLanguageTag(icu::StringPiece(locale), status);
70 if (U_FAILURE(status)) {
71 HILOG_ERROR_I18N("SimpleDateTimeFormat::InitSimpleDateTimeFormat: create icu::Locale failed.");
72 errCode = I18nErrorCode::FAILED;
73 return;
74 }
75
76 errCode = isBestPattern ? ParsePattern(skeletonOrPattern) : ParseSkeleton(skeletonOrPattern, icuLocale);
77 if (errCode != I18nErrorCode::SUCCESS) {
78 HILOG_ERROR_I18N("SimpleDateTimeFormat::InitSimpleDateTimeFormat: Parse pattern or skeleton failed.");
79 return;
80 }
81 errCode = InitFormatters(icuLocale, isBestPattern);
82 if (errCode != I18nErrorCode::SUCCESS) {
83 HILOG_ERROR_I18N("SimpleDateTimeFormat::InitSimpleDateTimeFormat: Init formatters failed.");
84 return;
85 }
86 initSuccess = true;
87 }
88
Format(int64_t milliseconds)89 std::string SimpleDateTimeFormat::Format(int64_t milliseconds)
90 {
91 if (!initSuccess) {
92 HILOG_ERROR_I18N("SimpleDateTimeFormat::Format: init failed.");
93 return PseudoLocalizationProcessor("");
94 }
95 std::string systemTimeZone = LocaleConfig::GetSystemTimezone();
96 auto timeZone = std::unique_ptr<icu::TimeZone>(icu::TimeZone::createTimeZone(systemTimeZone.c_str()));
97 UErrorCode status = U_ZERO_ERROR;
98 UDate date = static_cast<UDate>(milliseconds);
99 icu::UnicodeString formatResult;
100 for (size_t pos = 0; pos < formatters.size(); pos++) {
101 std::string pattern = patterns[pos];
102 if (!isPatterns[pos]) {
103 formatResult.append(pattern.c_str());
104 continue;
105 }
106 auto formatter = formatters[pos];
107 if (formatter == nullptr) {
108 HILOG_ERROR_I18N("SimpleDateTimeFormat::Format: formatter is nullptr.");
109 return PseudoLocalizationProcessor("");
110 }
111 if (timeZone != nullptr) {
112 formatter->setTimeZone(*timeZone);
113 }
114 formatter->format(date, formatResult, status);
115 if (U_FAILURE(status)) {
116 HILOG_ERROR_I18N("SimpleDateTimeFormat::Format: simpleDateFormat format failed.");
117 return PseudoLocalizationProcessor("");
118 }
119 }
120 std::string result;
121 formatResult.toUTF8String(result);
122 return PseudoLocalizationProcessor(result);
123 }
124
ParsePattern(const std::string & pattern)125 I18nErrorCode SimpleDateTimeFormat::ParsePattern(const std::string& pattern)
126 {
127 bool isPattern = true;
128 std::string content;
129 int32_t numberOfSeparator = 0;
130 size_t pos = 0;
131 size_t len = pattern.length();
132 while (pos < len) {
133 char c = pattern[pos];
134 // ' represents the separator
135 if (c == SEPARATOR) {
136 pos++;
137 // '' represents the character '
138 if (pos < len && pattern[pos] == SEPARATOR) {
139 content.push_back(SEPARATOR);
140 pos++;
141 continue;
142 }
143 numberOfSeparator++;
144 if (!content.empty()) {
145 patterns.push_back(content);
146 isPatterns.push_back(isPattern);
147 content.clear();
148 }
149 isPattern = !isPattern;
150 } else {
151 content.push_back(c);
152 pos++;
153 }
154 }
155 if (!content.empty()) {
156 patterns.push_back(content);
157 isPatterns.push_back(isPattern);
158 }
159 int32_t validNumber = 2;
160 if (numberOfSeparator % validNumber == 1) {
161 HILOG_ERROR_I18N("SimpleDateTimeFormat::ParsePattern: Number of separator is invalid.");
162 return I18nErrorCode::INVALID_DATE_TIME_FORMAT_PATTERN;
163 }
164 return I18nErrorCode::SUCCESS;
165 }
166
ParseSkeleton(const std::string & skeleton,const icu::Locale & locale)167 I18nErrorCode SimpleDateTimeFormat::ParseSkeleton(const std::string& skeleton, const icu::Locale& locale)
168 {
169 if (!IsValidPattern(skeleton)) {
170 return I18nErrorCode::INVALID_DATE_TIME_FORMAT_SKELETON;
171 }
172 UErrorCode status = U_ZERO_ERROR;
173 icu::UnicodeString bestPattern = icu::DateFormat::getBestPattern(locale, skeleton.c_str(), status);
174 if (U_FAILURE(status)) {
175 HILOG_ERROR_I18N("ParseSkeleton: Get best pattern failed.");
176 return I18nErrorCode::FAILED;
177 }
178 std::string pattern;
179 bestPattern.toUTF8String(pattern);
180 patterns.push_back(pattern);
181 isPatterns.push_back(true);
182 return I18nErrorCode::SUCCESS;
183 }
184
InitFormatters(const icu::Locale & locale,bool isBestPattern)185 I18nErrorCode SimpleDateTimeFormat::InitFormatters(const icu::Locale& locale, bool isBestPattern)
186 {
187 if (patterns.size() != isPatterns.size()) {
188 return I18nErrorCode::FAILED;
189 }
190 size_t sizeOfPatterns = patterns.size();
191 formatters.assign(sizeOfPatterns, nullptr);
192 UErrorCode status = U_ZERO_ERROR;
193 for (size_t pos = 0; pos < sizeOfPatterns; pos++) {
194 if (!isPatterns[pos]) {
195 continue;
196 }
197 std::string pattern = patterns[pos];
198 if (isBestPattern && !IsValidPattern(pattern)) {
199 return I18nErrorCode::INVALID_DATE_TIME_FORMAT_PATTERN;
200 }
201 formatters[pos] = std::make_shared<icu::SimpleDateFormat>(pattern.c_str(), locale, status);
202 if (U_FAILURE(status)) {
203 HILOG_ERROR_I18N("InitFormatters: Pattern %{public}s create SimpleDateFormat failed.",
204 pattern.c_str());
205 return I18nErrorCode::FAILED;
206 }
207 }
208 return I18nErrorCode::SUCCESS;
209 }
210
IsValidPattern(const std::string & pattern)211 bool SimpleDateTimeFormat::IsValidPattern(const std::string& pattern)
212 {
213 if (pattern.empty()) {
214 HILOG_ERROR_I18N("SimpleDateTimeFormat::IsValidPattern: Pattern is empty.");
215 return false;
216 }
217 for (const char c : pattern) {
218 if (VALID_PATTERN.find(c) == std::string::npos) {
219 HILOG_ERROR_I18N("SimpleDateTimeFormat::IsValidPattern: %{public}s is an invalid pattern.",
220 pattern.c_str());
221 return false;
222 }
223 }
224 return true;
225 }
226
Init()227 bool SimpleDateTimeFormat::Init()
228 {
229 SetHwIcuDirectory();
230 return true;
231 }
232 } // namespace I18n
233 } // namespace Global
234 } // namespace OHOS
235