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