1 /*
2 * Copyright (c) 2021-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
16 #include <filesystem>
17 #include <sys/stat.h>
18 #include "i18n_hilog.h"
19 #include "i18n_timezone.h"
20 #include "ohos/init_data.h"
21 #include "phonenumbers/phonenumberutil.h"
22 #include "unicode/strenum.h"
23 #include "unicode/timezone.h"
24 #include "utils.h"
25 #include "zone_util.h"
26
27 using namespace OHOS::Global::I18n;
28 using namespace icu;
29 using namespace std;
30
31 const char *ZoneUtil::COUNTRY_ZONE_DATA_PATH = "/system/usr/ohos_timezone/tzlookup.xml";
32 const char *ZoneUtil::DISTRO_COUNTRY_ZONE_DATA_PATH = "/system/etc/tzdata_distro/hos/tzlookup.xml";
33 const char *ZoneUtil::DEFAULT_TIMEZONE = "GMT";
34 const char *ZoneUtil::TIMEZONES_TAG = "timezones";
35 const char *ZoneUtil::ID_TAG = "id";
36 const char *ZoneUtil::DEFAULT_TAG = "default";
37 const char *ZoneUtil::BOOSTED_TAG = "defaultBoost";
38 const char *ZoneUtil::ROOT_TAG = "countryzones";
39 const char *ZoneUtil::SECOND_TAG = "country";
40 const char *ZoneUtil::CODE_TAG = "code";
41 const char *ZoneUtil::TIMEZONE_KEY = "persist.time.timezone";
42 const char *ZoneUtil::TZLOOKUP_FILE_PATH = ZoneUtil::GetTZLookupDataPath();
43
44 unordered_map<string, string> ZoneUtil::defaultMap = {
45 {"AQ", "Antarctica/McMurdo"},
46 {"AR", "America/Argentina/Buenos_Aires"},
47 {"AU", "Australia/Sydney"},
48 {"BR", "America/Noronha"},
49 {"BT", "Asia/Thimbu"}, // alias Asia/Thimphu
50 {"CA", "America/St_Johns"},
51 {"CD", "Africa/Kinshasa"},
52 {"CL", "America/Santiago"},
53 {"CN", "Asia/Shanghai"},
54 {"CY", "Asia/Nicosia"},
55 {"DE", "Europe/Berlin"},
56 {"DG", "Indian/Chagos"},
57 {"EA", "Africa/Ceuta"},
58 {"EC", "America/Guayaquil"},
59 {"ER", "Africa/Asmara"}, // alias Africa/Asmera
60 {"ES", "Europe/Madrid"},
61 {"FM", "Pacific/Pohnpei"},
62 {"FO", "Atlantic/Faroe"}, // alias Atlantic/Faeroe
63 {"GL", "America/Godthab"},
64 {"IC", "Atlantic/Canary"},
65 {"ID", "Asia/Jakarta"},
66 {"IN", "Asia/Kolkata"}, // alias Asia/Calcutta
67 {"KI", "Pacific/Tarawa"},
68 {"KZ", "Asia/Almaty"},
69 {"MH", "Pacific/Majuro"},
70 {"MN", "Asia/Ulaanbaatar"},
71 {"MX", "America/Mexico_City"},
72 {"MY", "Asia/Kuala_Lumpur"},
73 {"NP", "Asia/Kathmandu"}, // alias Asia/Katmandu
74 {"NZ", "Pacific/Auckland"},
75 {"PF", "Pacific/Tahiti"},
76 {"PG", "Pacific/Port_Moresby"},
77 {"PS", "Asia/Gaza"},
78 {"PT", "Europe/Lisbon"},
79 {"RU", "Europe/Moscow"},
80 {"UA", "Europe/Kiev"},
81 {"UM", "Pacific/Wake"},
82 {"US", "America/New_York"},
83 {"UZ", "Asia/Tashkent"},
84 {"VN", "Asia/Ho_Chi_Minh"}, // alias Asia/Saigon
85 };
86
87 bool ZoneUtil::icuInitialized = ZoneUtil::Init();
88
GetDefaultZone(const std::string & country)89 std::string ZoneUtil::GetDefaultZone(const std::string& country)
90 {
91 string temp(country);
92 for (size_t i = 0; i < temp.size(); i++) {
93 temp[i] = (char)toupper(temp[i]);
94 }
95 if (defaultMap.find(temp) != defaultMap.end()) {
96 return defaultMap[temp];
97 }
98 string ret;
99 StringEnumeration *strEnum = TimeZone::createEnumeration(temp.c_str());
100 GetString(strEnum, ret);
101 if (strEnum != nullptr) {
102 delete strEnum;
103 }
104 return ret;
105 }
106
GetDefaultZone(int32_t number)107 std::string ZoneUtil::GetDefaultZone(int32_t number)
108 {
109 using i18n::phonenumbers::PhoneNumberUtil;
110 PhoneNumberUtil* phoneUtil = PhoneNumberUtil::GetInstance();
111 if (phoneUtil == nullptr) {
112 HILOG_ERROR_I18N("ZoneUtil::GetDefaultZone: Get phone number util failed.");
113 return "";
114 }
115 std::string regionCode;
116 phoneUtil->GetRegionCodeForCountryCode(number, ®ionCode);
117 return GetDefaultZone(regionCode);
118 }
119
GetDefaultZone(const std::string & country,int32_t offset)120 std::string ZoneUtil::GetDefaultZone(const std::string& country, int32_t offset)
121 {
122 UErrorCode status = U_ZERO_ERROR;
123 StringEnumeration *strEnum =
124 TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, country.c_str(), &offset, status);
125 if (U_FAILURE(status)) {
126 return "";
127 }
128 string ret;
129 GetString(strEnum, ret);
130 if (strEnum != nullptr) {
131 delete strEnum;
132 strEnum = nullptr;
133 }
134 return ret;
135 }
136
GetDefaultZone(int32_t number,int32_t offset)137 std::string ZoneUtil::GetDefaultZone(int32_t number, int32_t offset)
138 {
139 using i18n::phonenumbers::PhoneNumberUtil;
140 PhoneNumberUtil* phoneUtil = PhoneNumberUtil::GetInstance();
141 if (phoneUtil == nullptr) {
142 HILOG_ERROR_I18N("ZoneUtil::GetDefaultZone: Get phone number util failed.");
143 return "";
144 }
145 std::string regionCode;
146 phoneUtil->GetRegionCodeForCountryCode(number, ®ionCode);
147 return GetDefaultZone(regionCode, offset);
148 }
149
GetZoneList(const std::string & country,std::vector<std::string> & retVec)150 void ZoneUtil::GetZoneList(const std::string& country, std::vector<std::string>& retVec)
151 {
152 StringEnumeration *strEnum = TimeZone::createEnumeration(country.c_str());
153 GetList(strEnum, retVec);
154 if (strEnum != nullptr) {
155 delete strEnum;
156 strEnum = nullptr;
157 }
158 }
159
GetZoneList(const std::string & country,int32_t offset,std::vector<std::string> & retVec)160 void ZoneUtil::GetZoneList(const std::string& country, int32_t offset, std::vector<std::string>& retVec)
161 {
162 UErrorCode status = U_ZERO_ERROR;
163 StringEnumeration *strEnum =
164 TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, country.c_str(), &offset, status);
165 if (U_FAILURE(status)) {
166 delete strEnum;
167 strEnum = nullptr;
168 return;
169 }
170 GetList(strEnum, retVec);
171 if (strEnum != nullptr) {
172 delete strEnum;
173 strEnum = nullptr;
174 }
175 }
176
GetString(StringEnumeration * strEnum,string & ret)177 void ZoneUtil::GetString(StringEnumeration *strEnum, string& ret)
178 {
179 UErrorCode status = U_ZERO_ERROR;
180 if (!strEnum) {
181 return;
182 }
183 int32_t count = strEnum->count(status);
184 if (U_FAILURE(status) || count <= 0) {
185 return;
186 }
187 const UnicodeString *uniStr = strEnum->snext(status);
188 if (U_FAILURE(status) || (!uniStr)) {
189 return;
190 }
191 UnicodeString canonicalUnistring;
192 TimeZone::getCanonicalID(*uniStr, canonicalUnistring, status);
193 if (U_FAILURE(status)) {
194 return;
195 }
196 canonicalUnistring.toUTF8String(ret);
197 return;
198 }
199
GetList(StringEnumeration * strEnum,vector<string> & retVec)200 void ZoneUtil::GetList(StringEnumeration *strEnum, vector<string> &retVec)
201 {
202 if (!strEnum) {
203 return;
204 }
205 UErrorCode status = U_ZERO_ERROR;
206 int32_t count = strEnum->count(status);
207 if (count <= 0 || U_FAILURE(status)) {
208 return;
209 }
210 while (count > 0) {
211 const UnicodeString *uniStr = strEnum->snext(status);
212 if ((!uniStr) || U_FAILURE(status)) {
213 retVec.clear();
214 break;
215 }
216 UnicodeString canonicalUnistring;
217 TimeZone::getCanonicalID(*uniStr, canonicalUnistring, status);
218 if (U_FAILURE(status)) {
219 retVec.clear();
220 break;
221 }
222 string canonicalString = "";
223 canonicalUnistring.toUTF8String(canonicalString);
224 if ((canonicalString != "") && (find(retVec.begin(), retVec.end(), canonicalString) == retVec.end())) {
225 retVec.push_back(canonicalString);
226 }
227 --count;
228 }
229 return;
230 }
231
Init()232 bool ZoneUtil::Init()
233 {
234 SetHwIcuDirectory();
235 return true;
236 }
237
LookupTimezoneByCountryAndNITZ(std::string & region,NITZData & nitzData)238 CountryResult ZoneUtil::LookupTimezoneByCountryAndNITZ(std::string ®ion, NITZData &nitzData)
239 {
240 std::vector<std::string> zones;
241 std::string defaultTimezone;
242 std::string systemTimezone = ReadSystemParameter(TIMEZONE_KEY, SYS_PARAM_LEN);
243 if (systemTimezone.length() == 0) {
244 systemTimezone = DEFAULT_TIMEZONE;
245 }
246 if (TZLOOKUP_FILE_PATH != nullptr) {
247 bool isBoosted = false;
248 HILOG_INFO_I18N("ZoneUtil::LookupTimezoneByCountryAndNITZ use tzlookup.xml");
249 GetCountryZones(region, defaultTimezone, isBoosted, zones);
250 } else {
251 HILOG_INFO_I18N("ZoneUtil::LookupTimezoneByCountryAndNITZ use icu data");
252 GetICUCountryZones(region, zones, defaultTimezone);
253 }
254 return Match(zones, nitzData, systemTimezone);
255 }
256
LookupTimezoneByNITZ(NITZData & nitzData)257 CountryResult ZoneUtil::LookupTimezoneByNITZ(NITZData &nitzData)
258 {
259 std::string systemTimezone = ReadSystemParameter(TIMEZONE_KEY, SYS_PARAM_LEN);
260 if (systemTimezone.length() == 0) {
261 systemTimezone = DEFAULT_TIMEZONE;
262 }
263 I18nErrorCode status = I18nErrorCode::SUCCESS;
264 std::set<std::string> icuTimezones = I18nTimeZone::GetAvailableIDs(status);
265 if (status != I18nErrorCode::SUCCESS) {
266 HILOG_ERROR_I18N("ZoneUtil::LookupTimezoneByNITZ can not get icu data");
267 }
268 std::vector<std::string> validZones;
269 for (auto it = icuTimezones.begin(); it != icuTimezones.end(); ++it) {
270 validZones.push_back(*it);
271 }
272
273 CountryResult result = Match(validZones, nitzData, systemTimezone);
274 if (result.timezoneId.empty() && nitzData.isDST >= 0) {
275 NITZData newNITZData = { -1, nitzData.totalOffset, nitzData.currentMillis }; // -1 means not consider DST
276 result = Match(validZones, newNITZData, systemTimezone);
277 }
278 return result;
279 }
280
LookupTimezoneByCountry(std::string & region,int64_t currentMillis)281 CountryResult ZoneUtil::LookupTimezoneByCountry(std::string ®ion, int64_t currentMillis)
282 {
283 std::vector<std::string> zones;
284 bool isBoosted = false;
285 std::string defaultTimezone;
286 CountryResult result = { true, MatchQuality::DEFAULT_BOOSTED, defaultTimezone };
287 if (TZLOOKUP_FILE_PATH != nullptr) {
288 HILOG_INFO_I18N("ZoneUtil::LookupTimezoneByCountry use tzlookup.xml");
289 GetCountryZones(region, defaultTimezone, isBoosted, zones);
290 if (defaultTimezone.empty()) {
291 HILOG_ERROR_I18N("ZoneUtil::LookupTimezoneByCountry can't find default timezone for region %{public}s",
292 region.c_str());
293 }
294 } else {
295 HILOG_INFO_I18N("ZoneUtil::LookupTimezoneByCountry use icu data");
296 GetICUCountryZones(region, zones, defaultTimezone);
297 }
298 result.timezoneId = defaultTimezone;
299 if (isBoosted) {
300 return result;
301 }
302 if (zones.size() == 0) {
303 result.quality = MatchQuality::MULTIPLE_ZONES_DIFFERENT_OFFSET;
304 } else if (zones.size() == 1) {
305 result.quality = MatchQuality::SINGLE_ZONE;
306 } else if (CheckSameDstOffset(zones, defaultTimezone, currentMillis)) {
307 result.quality = MatchQuality::MULTIPLE_ZONES_SAME_OFFSET;
308 } else {
309 result.quality = MatchQuality::MULTIPLE_ZONES_DIFFERENT_OFFSET;
310 }
311 return result;
312 }
313
GetTZLookupDataPath()314 const char *ZoneUtil::GetTZLookupDataPath()
315 {
316 using std::filesystem::directory_iterator;
317 struct stat s;
318 if (stat(DISTRO_COUNTRY_ZONE_DATA_PATH, &s) == 0) {
319 return DISTRO_COUNTRY_ZONE_DATA_PATH;
320 }
321 if (stat(COUNTRY_ZONE_DATA_PATH, &s) == 0) {
322 return COUNTRY_ZONE_DATA_PATH;
323 } else {
324 return nullptr;
325 }
326 }
327
IsFindCountry(xmlDocPtr & doc,xmlNodePtr & cur,xmlNodePtr & value,std::string & region)328 bool ZoneUtil::IsFindCountry(xmlDocPtr &doc, xmlNodePtr &cur, xmlNodePtr &value,
329 std::string ®ion)
330 {
331 while (cur != nullptr && xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(SECOND_TAG)) == 0) {
332 value = cur->xmlChildrenNode;
333 if (value == nullptr) {
334 cur = cur->next;
335 continue;
336 }
337 if (xmlStrcmp(value->name, reinterpret_cast<const xmlChar*>(CODE_TAG)) != 0) {
338 xmlFreeDoc(doc);
339 HILOG_ERROR_I18N("ZoneUtil::GetCountryZones invalid code_tag");
340 return false;
341 }
342 xmlChar *codePtr = xmlNodeGetContent(value);
343 if (codePtr == nullptr) {
344 cur = cur->next;
345 continue;
346 }
347 const char* xmlRegion = reinterpret_cast<const char*>(codePtr);
348 if (region.compare(xmlRegion) == 0) {
349 xmlFree(codePtr);
350 return true;
351 } else {
352 xmlFree(codePtr);
353 cur = cur->next;
354 continue;
355 }
356 }
357 return false;
358 }
359
GetCountryZones(std::string & region,std::string & defaultTimzone,bool & isBoosted,std::vector<std::string> & zones)360 void ZoneUtil::GetCountryZones(std::string ®ion, std::string &defaultTimzone, bool &isBoosted,
361 std::vector<std::string> &zones)
362 {
363 xmlKeepBlanksDefault(0);
364 xmlDocPtr doc = xmlParseFile(TZLOOKUP_FILE_PATH);
365 if (!doc) {
366 HILOG_ERROR_I18N("ZoneUtil::GetCountryZones can not open tzlookup.xml");
367 return;
368 }
369 xmlNodePtr cur = xmlDocGetRootElement(doc);
370 if (!cur || xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(ROOT_TAG)) != 0) {
371 xmlFreeDoc(doc);
372 HILOG_ERROR_I18N("ZoneUtil::GetCountryZones invalid Root_tag");
373 return;
374 }
375 cur = cur->xmlChildrenNode;
376 xmlNodePtr value;
377 bool findCountry = IsFindCountry(doc, cur, value, region);
378 if (findCountry) {
379 value = value->next;
380 GetDefaultAndBoost(value, defaultTimzone, isBoosted, zones);
381 }
382 xmlUnlinkNode(cur);
383 xmlFreeNode(cur);
384 xmlFreeDoc(doc);
385 return;
386 }
387
GetDefaultAndBoost(xmlNodePtr & value,std::string & defaultTimezone,bool & isBoosted,std::vector<std::string> & zones)388 void ZoneUtil::GetDefaultAndBoost(xmlNodePtr &value, std::string &defaultTimezone, bool &isBoosted,
389 std::vector<std::string> &zones)
390 {
391 if (value == nullptr || xmlStrcmp(value->name, reinterpret_cast<const xmlChar*>(DEFAULT_TAG)) != 0) {
392 HILOG_ERROR_I18N("ZoneUtil::GetDefaultAndBoost invalid default_tag");
393 return;
394 }
395 xmlChar *defaultPtr = xmlNodeGetContent(value);
396 if (defaultPtr != nullptr) {
397 defaultTimezone = reinterpret_cast<const char*>(defaultPtr);
398 xmlFree(defaultPtr);
399 }
400 value = value->next;
401 if (value == nullptr) {
402 HILOG_ERROR_I18N("ZoneUtil::GetDefaultAndBoost doesn't contains id");
403 return;
404 }
405 if (xmlStrcmp(value->name, reinterpret_cast<const xmlChar *>(BOOSTED_TAG)) == 0) {
406 isBoosted = true;
407 value = value->next;
408 } else {
409 isBoosted = false;
410 }
411 GetTimezones(value, zones);
412 }
413
GetTimezones(xmlNodePtr & value,std::vector<std::string> & zones)414 void ZoneUtil::GetTimezones(xmlNodePtr &value, std::vector<std::string> &zones)
415 {
416 if (xmlStrcmp(value->name, reinterpret_cast<const xmlChar *>(TIMEZONES_TAG)) != 0) {
417 HILOG_ERROR_I18N("ZoneUtil::GetTimezones invalid timezones_tag");
418 return;
419 }
420 value = value->xmlChildrenNode;
421 while (value != nullptr) {
422 if (xmlStrcmp(value->name, reinterpret_cast<const xmlChar *>(ID_TAG)) != 0) {
423 HILOG_ERROR_I18N("ZoneUtil::GetTimezones invalid id_tag");
424 return;
425 }
426 xmlChar *idPtr = xmlNodeGetContent(value);
427 if (idPtr != nullptr) {
428 zones.push_back(reinterpret_cast<const char*>(idPtr));
429 xmlFree(idPtr);
430 }
431 value = value->next;
432 }
433 }
434
GetICUCountryZones(std::string & region,std::vector<std::string> & zones,std::string & defaultTimezone)435 void ZoneUtil::GetICUCountryZones(std::string ®ion, std::vector<std::string> &zones, std::string &defaultTimezone)
436 {
437 I18nErrorCode errorCode = I18nErrorCode::SUCCESS;
438 std::set<std::string> validZoneIds = I18nTimeZone::GetAvailableIDs(errorCode);
439 if (errorCode != I18nErrorCode::SUCCESS) {
440 HILOG_ERROR_I18N("ZoneUtil::GetICUCountryZones can not get icu data");
441 }
442 std::set<std::string> countryZoneIds;
443 StringEnumeration *strEnum = TimeZone::createEnumeration(region.c_str());
444 UErrorCode status = U_ZERO_ERROR;
445 const UnicodeString *timezoneIdUStr = strEnum == nullptr ? nullptr : strEnum->snext(status);
446 while (timezoneIdUStr != nullptr && U_SUCCESS(status)) {
447 UnicodeString canonicalUnistring;
448 TimeZone::getCanonicalID(*timezoneIdUStr, canonicalUnistring, status);
449 std::string timezoneId;
450 canonicalUnistring.toUTF8String(timezoneId);
451 if (validZoneIds.find(timezoneId) != validZoneIds.end()) {
452 countryZoneIds.insert(timezoneId);
453 }
454 timezoneIdUStr = strEnum->snext(status);
455 }
456 for (auto it = countryZoneIds.begin(); it != countryZoneIds.end(); ++it) {
457 zones.push_back(*it);
458 }
459 if (defaultMap.find(region) != defaultMap.end()) {
460 defaultTimezone = defaultMap[region];
461 } else {
462 if (zones.size() > 0) {
463 defaultTimezone = zones[0];
464 }
465 }
466 delete strEnum;
467 }
468
Match(std::vector<std::string> & zones,NITZData & nitzData,std::string & systemTimezone)469 CountryResult ZoneUtil::Match(std::vector<std::string> &zones, NITZData &nitzData, std::string &systemTimezone)
470 {
471 bool isOnlyMatch = true;
472 std::string matchedZoneId;
473 bool local = false;
474 bool useSystemTimezone = false;
475 for (size_t i = 0; i < zones.size(); i++) {
476 std::string zoneId = zones[i];
477 UnicodeString unicodeZoneID(zoneId.data(), zoneId.length());
478 TimeZone *timezone = TimeZone::createTimeZone(unicodeZoneID);
479 if (timezone == nullptr) {
480 HILOG_ERROR_I18N("ZoneUtil::Match: Create time zone failed.");
481 continue;
482 }
483 int32_t rawOffset;
484 int32_t dstOffset;
485 UErrorCode status = UErrorCode::U_ZERO_ERROR;
486 timezone->getOffset(nitzData.currentMillis, static_cast<UBool>(local), rawOffset, dstOffset, status);
487 if (U_FAILURE(status)) {
488 HILOG_ERROR_I18N("ZoneUtil::Match: Time zone get offset failed.");
489 delete timezone;
490 continue;
491 }
492 if ((nitzData.totalOffset - rawOffset == dstOffset) &&
493 (nitzData.isDST < 0 || nitzData.isDST == (dstOffset != 0))) {
494 if (matchedZoneId.empty()) {
495 matchedZoneId = zoneId;
496 } else {
497 isOnlyMatch = false;
498 }
499 if (strcmp(zoneId.c_str(), systemTimezone.c_str()) == 0) {
500 matchedZoneId = systemTimezone;
501 useSystemTimezone = true;
502 }
503 if (!isOnlyMatch && useSystemTimezone) {
504 delete timezone;
505 break;
506 }
507 }
508 delete timezone;
509 }
510 CountryResult result = {isOnlyMatch, MatchQuality::DEFAULT_BOOSTED, matchedZoneId};
511 return result;
512 }
513
CheckSameDstOffset(std::vector<std::string> & zones,std::string & defaultTimezoneId,int64_t currentMillis)514 bool ZoneUtil::CheckSameDstOffset(std::vector<std::string> &zones, std::string &defaultTimezoneId,
515 int64_t currentMillis)
516 {
517 UnicodeString defaultID(defaultTimezoneId.data(), defaultTimezoneId.length());
518 TimeZone *defaultTimezone = TimeZone::createTimeZone(defaultID);
519 if (defaultTimezone == nullptr) {
520 HILOG_ERROR_I18N("ZoneUtil::CheckSameDstOffset: Create default time zone failed.");
521 return false;
522 }
523 int32_t rawOffset = 0;
524 int32_t dstOffset = 0;
525 bool local = false;
526 UErrorCode status = U_ZERO_ERROR;
527 defaultTimezone->getOffset(currentMillis, (UBool)local, rawOffset, dstOffset, status);
528 if (U_FAILURE(status)) {
529 delete defaultTimezone;
530 HILOG_ERROR_I18N("ZoneUtil::CheckSameDstOffset can not get timezone defaultID offset");
531 return false;
532 }
533 delete defaultTimezone;
534 int32_t totalOffset = rawOffset + dstOffset;
535 for (size_t i = 0; i < zones.size(); i++) {
536 UnicodeString unicodeZoneID(zones[i].data(), zones[i].length());
537 TimeZone *timezone = TimeZone::createTimeZone(unicodeZoneID);
538 if (timezone == nullptr) {
539 HILOG_ERROR_I18N("ZoneUtil::CheckSameDstOffset: Create time zone failed.");
540 return false;
541 }
542 timezone->getOffset(currentMillis, (UBool)local, rawOffset, dstOffset, status);
543 if (U_FAILURE(status)) {
544 delete timezone;
545 HILOG_ERROR_I18N("ZoneUtil::CheckSameDstOffset can not get timezone unicodeZoneID offset");
546 return false;
547 }
548 if (totalOffset - rawOffset != dstOffset) {
549 delete timezone;
550 return false;
551 }
552 delete timezone;
553 }
554 return true;
555 }
556