• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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