1 /*
2 * Copyright (c) 2022 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 "i18n_timezone.h"
16
17 #include "libxml/globals.h"
18 #include "libxml/tree.h"
19 #include "libxml/xmlstring.h"
20 #include "locale_config.h"
21 #include "locale_info.h"
22 #include "map"
23 #include "set"
24 #include "string"
25 #include "type_traits"
26 #include "umachine.h"
27 #include "utility"
28 #include "utils.h"
29 #include "utypes.h"
30 #include "vector"
31 #include "unicode/locid.h"
32 #include "unicode/unistr.h"
33
34 namespace OHOS {
35 namespace Global {
36 namespace I18n {
37 const char *I18nTimeZone::TIMEZONE_KEY = "persist.time.timezone";
38 const char *I18nTimeZone::DEFAULT_TIMEZONE = "GMT";
39 const char *I18nTimeZone::DEFAULT_LANGUAGE = "/system/usr/ohos_timezone/en-Latn.xml";
40 const char *I18nTimeZone::DEFAULT_LOCALE = "en-Latn";
41 const char *I18nTimeZone::TIMEZONES_PATH = "/system/usr/ohos_timezone/";
42 const char *I18nTimeZone::SUPPORT_LOCALES_PATH = "/system/usr/ohos_timezone/supported_locales.xml";
43 const char *I18nTimeZone::rootTag = "timezones";
44 const char *I18nTimeZone::secondRootTag = "timezone";
45 const char *I18nTimeZone::supportLocalesTag = "supported_locales";
46 std::string I18nTimeZone::displayLocale = "";
47 bool I18nTimeZone::isInitialized = false;
48
49 std::map<std::string, std::string> I18nTimeZone::supportLocales {};
50 std::set<std::string> I18nTimeZone::availableIDs {};
51 std::vector<std::string> I18nTimeZone::availableZoneCityIDs {};
52 std::map<std::string, std::string> I18nTimeZone::city2DisplayName {};
53 std::map<std::string, std::string> I18nTimeZone::city2TimeZoneID {};
54
I18nTimeZone(std::string & id,bool isZoneID)55 I18nTimeZone::I18nTimeZone(std::string &id, bool isZoneID)
56 {
57 if (isZoneID) {
58 if (id.empty()) {
59 std::string systemTimezone = ReadSystemParameter(TIMEZONE_KEY, SYS_PARAM_LEN);
60 if (systemTimezone.length() == 0) {
61 systemTimezone = DEFAULT_TIMEZONE;
62 }
63 icu::UnicodeString unicodeZoneID(systemTimezone.data(), systemTimezone.length());
64 timezone = icu::TimeZone::createTimeZone(unicodeZoneID);
65 } else {
66 icu::UnicodeString unicodeZoneID(id.data(), id.length());
67 timezone = icu::TimeZone::createTimeZone(unicodeZoneID);
68 }
69 } else {
70 if (!isInitialized) {
71 ReadTimeZoneData(DEFAULT_LANGUAGE);
72 isInitialized = true;
73 }
74 if (city2TimeZoneID.find(id) == city2TimeZoneID.end()) {
75 timezone = icu::TimeZone::createDefault();
76 } else {
77 std::string timezoneID = city2TimeZoneID.at(id);
78 icu::UnicodeString unicodeZoneID(timezoneID.data(), timezoneID.length());
79 timezone = icu::TimeZone::createTimeZone(unicodeZoneID);
80 }
81 }
82 }
83
~I18nTimeZone()84 I18nTimeZone::~I18nTimeZone()
85 {
86 if (timezone != nullptr) {
87 delete timezone;
88 timezone = nullptr;
89 }
90 }
91
GetTimeZone()92 icu::TimeZone* I18nTimeZone::GetTimeZone()
93 {
94 return timezone;
95 }
96
CreateInstance(std::string & id,bool isZoneID)97 std::unique_ptr<I18nTimeZone> I18nTimeZone::CreateInstance(std::string &id, bool isZoneID)
98 {
99 std::unique_ptr<I18nTimeZone> i18nTimeZone = std::make_unique<I18nTimeZone>(id, isZoneID);
100 if (i18nTimeZone->GetTimeZone() == nullptr) {
101 return nullptr;
102 }
103 return i18nTimeZone;
104 }
105
GetOffset(double date)106 int32_t I18nTimeZone::GetOffset(double date)
107 {
108 int32_t rawOffset = 0;
109 int32_t dstOffset = 0;
110 bool local = false;
111 UErrorCode status = U_ZERO_ERROR;
112 if (timezone == nullptr) {
113 return 0;
114 }
115 timezone->getOffset(date, (UBool)local, rawOffset, dstOffset, status);
116 if (status != U_ZERO_ERROR) {
117 return 0;
118 }
119 return rawOffset + dstOffset;
120 }
121
GetRawOffset()122 int32_t I18nTimeZone::GetRawOffset()
123 {
124 if (timezone == nullptr) {
125 return 0;
126 }
127 return timezone->getRawOffset();
128 }
129
GetID()130 std::string I18nTimeZone::GetID()
131 {
132 if (timezone == nullptr) {
133 return "";
134 }
135 icu::UnicodeString zoneID;
136 timezone->getID(zoneID);
137 std::string result;
138 zoneID.toUTF8String(result);
139 return result;
140 }
141
GetDisplayName()142 std::string I18nTimeZone::GetDisplayName()
143 {
144 if (timezone == nullptr) {
145 return "";
146 }
147 std::string localeStr = LocaleConfig::GetSystemLocale();
148 return GetDisplayName(localeStr, false);
149 }
150
GetDisplayName(bool isDST)151 std::string I18nTimeZone::GetDisplayName(bool isDST)
152 {
153 std::string localeStr = LocaleConfig::GetSystemLocale();
154 return GetDisplayName(localeStr, isDST);
155 }
156
GetDisplayName(std::string localeStr)157 std::string I18nTimeZone::GetDisplayName(std::string localeStr)
158 {
159 return GetDisplayName(localeStr, false);
160 }
161
GetDisplayName(std::string localeStr,bool isDST)162 std::string I18nTimeZone::GetDisplayName(std::string localeStr, bool isDST)
163 {
164 icu::TimeZone::EDisplayType style = icu::TimeZone::EDisplayType::LONG_GENERIC;
165 icu::Locale locale(localeStr.data());
166 icu::UnicodeString name;
167 timezone->getDisplayName((UBool)isDST, style, locale, name);
168 std::string result;
169 name.toUTF8String(result);
170 return result;
171 }
172
ReadTimeZoneData(const char * xmlPath)173 void I18nTimeZone::ReadTimeZoneData(const char *xmlPath)
174 {
175 xmlKeepBlanksDefault(0);
176 if (xmlPath == nullptr) {
177 return;
178 }
179 xmlDocPtr doc = xmlParseFile(xmlPath);
180 if (!doc) {
181 return;
182 }
183 xmlNodePtr cur = xmlDocGetRootElement(doc);
184 if (!cur || xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(rootTag))) {
185 xmlFreeDoc(doc);
186 return;
187 }
188 cur = cur->xmlChildrenNode;
189 while (cur != nullptr && !xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(secondRootTag))) {
190 xmlNodePtr value = cur->xmlChildrenNode;
191 xmlChar *contents[ELEMENT_NUM] = { 0 }; // 3 represent cityid, zoneid, displayname;
192 for (size_t i = 0; i < ELEMENT_NUM; i++) {
193 if (value != nullptr) {
194 contents[i] = xmlNodeGetContent(value);
195 value = value->next;
196 } else {
197 break;
198 }
199 }
200 if (!isInitialized) {
201 // 0 represents cityid index, 1 represents zoneid index
202 availableZoneCityIDs.insert(availableZoneCityIDs.end(), reinterpret_cast<const char *>(contents[0]));
203 availableIDs.insert(reinterpret_cast<const char *>(contents[1]));
204 city2TimeZoneID.insert(
205 std::make_pair<std::string, std::string>(reinterpret_cast<const char *>(contents[0]),
206 reinterpret_cast<const char *>(contents[1])));
207 }
208 // 0 represents cityid index, 2 represents displayname index
209 city2DisplayName.insert(
210 std::make_pair<std::string, std::string>(reinterpret_cast<const char *>(contents[0]),
211 reinterpret_cast<const char *>(contents[2])));
212 for (size_t i = 0; i < ELEMENT_NUM; i++) {
213 if (contents[i] != nullptr) {
214 xmlFree(contents[i]);
215 }
216 }
217 cur = cur->next;
218 }
219 xmlFreeDoc(doc);
220 }
221
GetAvailableIDs()222 std::set<std::string> I18nTimeZone::GetAvailableIDs()
223 {
224 if (!isInitialized) {
225 ReadTimeZoneData(DEFAULT_LANGUAGE);
226 isInitialized = true;
227 }
228 return availableIDs;
229 }
230
GetAvailableZoneCityIDs()231 std::vector<std::string> I18nTimeZone::GetAvailableZoneCityIDs()
232 {
233 if (!isInitialized) {
234 ReadTimeZoneData(DEFAULT_LANGUAGE);
235 isInitialized = true;
236 }
237 return availableZoneCityIDs;
238 }
239
ComputeLocale(std::string & locale)240 std::string I18nTimeZone::ComputeLocale(std::string &locale)
241 {
242 if (supportLocales.size() == 0) {
243 xmlKeepBlanksDefault(0);
244 xmlDocPtr doc = xmlParseFile(SUPPORT_LOCALES_PATH);
245 if (!doc) {
246 return DEFAULT_LOCALE;
247 }
248 xmlNodePtr cur = xmlDocGetRootElement(doc);
249 if (!cur || xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(supportLocalesTag))) {
250 xmlFreeDoc(doc);
251 return DEFAULT_LOCALE;
252 }
253 cur = cur->xmlChildrenNode;
254 while (cur != nullptr) {
255 xmlChar *content = xmlNodeGetContent(cur);
256 if (content == nullptr) {
257 break;
258 }
259 std::map<std::string, std::string> configs = {};
260 LocaleInfo localeinfo(reinterpret_cast<const char*>(content), configs);
261 std::string language = localeinfo.GetLanguage();
262 std::string script = localeinfo.GetScript();
263 std::string languageAndScript = (script.length() == 0) ? language : language + "-" + script;
264 LocaleInfo newLocaleInfo(languageAndScript, configs);
265 std::string maximizeLocale = newLocaleInfo.Maximize();
266 supportLocales.insert(
267 std::make_pair<std::string, std::string>(maximizeLocale.c_str(),
268 reinterpret_cast<const char*>(content)));
269 xmlFree(content);
270 cur = cur->next;
271 }
272 }
273 std::map<std::string, std::string> configs = {};
274 LocaleInfo localeinfo(locale, configs);
275 std::string language = localeinfo.GetLanguage();
276 std::string script = localeinfo.GetScript();
277 std::string languageAndScript = (script.length() == 0) ? language : language + "-" + script;
278 LocaleInfo newLocaleInfo(languageAndScript, configs);
279 std::string maximizeLocale = newLocaleInfo.Maximize();
280 if (supportLocales.find(maximizeLocale) != supportLocales.end()) {
281 return supportLocales.at(maximizeLocale);
282 }
283 return DEFAULT_LOCALE;
284 }
285
GetCityDisplayName(std::string & cityID,std::string & locale)286 std::string I18nTimeZone::GetCityDisplayName(std::string &cityID, std::string &locale)
287 {
288 std::string finalLocale = ComputeLocale(locale);
289 if (finalLocale.compare(displayLocale) != 0) {
290 std::string xmlPath = TIMEZONES_PATH + finalLocale + ".xml";
291 city2DisplayName.clear();
292 ReadTimeZoneData(xmlPath.c_str());
293 displayLocale = finalLocale;
294 }
295 if (city2DisplayName.find(cityID) == city2DisplayName.end()) {
296 return "";
297 }
298 return city2DisplayName.at(cityID);
299 }
300 }
301 }
302 }
303