• 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     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