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#include "character.h" 16#include "error_util.h" 17#include "i18n_hilog.h" 18#include "i18n_timezone_addon.h" 19#include "js_utils.h" 20#include "variable_convertor.h" 21 22namespace OHOS { 23namespace Global { 24namespace I18n { 25static thread_local napi_ref* g_timezoneConstructor = nullptr; 26 27I18nTimeZoneAddon::I18nTimeZoneAddon() {} 28 29I18nTimeZoneAddon::~I18nTimeZoneAddon() {} 30 31void I18nTimeZoneAddon::Destructor(napi_env env, void *nativeObject, void *hint) 32{ 33 if (!nativeObject) { 34 return; 35 } 36 delete reinterpret_cast<I18nTimeZoneAddon *>(nativeObject); 37 nativeObject = nullptr; 38} 39 40napi_value I18nTimeZoneAddon::GetI18nTimeZone(napi_env env, napi_callback_info info) 41{ 42 size_t argc = 1; 43 napi_value argv[1] = { nullptr }; 44 napi_value thisVar = nullptr; 45 void *data = nullptr; 46 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); 47 if (!VariableConvertor::CheckNapiValueType(env, argv[0])) { 48 napi_create_string_utf8(env, "", NAPI_AUTO_LENGTH, &argv[0]); 49 } 50 return StaticGetTimeZone(env, argv, true); 51} 52 53napi_value I18nTimeZoneAddon::InitI18nTimeZone(napi_env env, napi_value exports) 54{ 55 napi_property_descriptor properties[] = { 56 DECLARE_NAPI_FUNCTION("getID", GetID), 57 DECLARE_NAPI_FUNCTION("getDisplayName", GetTimeZoneDisplayName), 58 DECLARE_NAPI_FUNCTION("getRawOffset", GetRawOffset), 59 DECLARE_NAPI_FUNCTION("getOffset", GetOffset), 60 }; 61 napi_value constructor = nullptr; 62 napi_status status = napi_define_class(env, "TimeZone", NAPI_AUTO_LENGTH, I18nTimeZoneConstructor, nullptr, 63 sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor); 64 if (status != napi_ok) { 65 HILOG_ERROR_I18N("InitI18nTimeZone: Failed to define class TimeZone at Init"); 66 return nullptr; 67 } 68 exports = I18nTimeZoneAddon::InitTimeZone(env, exports); 69 g_timezoneConstructor = new (std::nothrow) napi_ref; 70 if (!g_timezoneConstructor) { 71 HILOG_ERROR_I18N("InitI18nTimeZone: Failed to create TimeZone ref at init"); 72 return nullptr; 73 } 74 status = napi_create_reference(env, constructor, 1, g_timezoneConstructor); 75 if (status != napi_ok) { 76 HILOG_ERROR_I18N("InitI18nTimeZone: Failed to create reference g_timezoneConstructor at init"); 77 return nullptr; 78 } 79 return exports; 80} 81 82napi_value I18nTimeZoneAddon::InitTimeZone(napi_env env, napi_value exports) 83{ 84 napi_property_descriptor properties[] = { 85 DECLARE_NAPI_STATIC_FUNCTION("getAvailableIDs", GetAvailableTimezoneIDs), 86 DECLARE_NAPI_STATIC_FUNCTION("getAvailableZoneCityIDs", GetAvailableZoneCityIDs), 87 DECLARE_NAPI_STATIC_FUNCTION("getCityDisplayName", GetCityDisplayName), 88 DECLARE_NAPI_STATIC_FUNCTION("getTimezoneFromCity", GetTimezoneFromCity), 89 DECLARE_NAPI_STATIC_FUNCTION("getTimezonesByLocation", GetTimezonesByLocation) 90 }; 91 napi_value constructor = nullptr; 92 napi_status status = napi_define_class(env, "I18nTimeZone", NAPI_AUTO_LENGTH, JSUtils::DefaultConstructor, nullptr, 93 sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor); 94 if (status != napi_ok) { 95 HILOG_ERROR_I18N("InitTimeZone: Failed to define class TimeZone."); 96 return nullptr; 97 } 98 status = napi_set_named_property(env, exports, "TimeZone", constructor); 99 if (status != napi_ok) { 100 HILOG_ERROR_I18N("InitTimeZone: Set property failed When InitTimeZone."); 101 return nullptr; 102 } 103 return exports; 104} 105 106napi_value I18nTimeZoneAddon::I18nTimeZoneConstructor(napi_env env, napi_callback_info info) 107{ 108 size_t argc = 2; 109 napi_value argv[2] = { nullptr }; 110 napi_value thisVar = nullptr; 111 void *data = nullptr; 112 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); 113 if (status != napi_ok) { 114 return nullptr; 115 } 116 std::string zoneID = ""; 117 napi_valuetype valueType = napi_valuetype::napi_undefined; 118 if (argc > 0) { 119 napi_typeof(env, argv[0], &valueType); 120 if (valueType != napi_valuetype::napi_string) { 121 return nullptr; 122 } 123 int32_t code = 0; 124 zoneID = VariableConvertor::GetString(env, argv[0], code); 125 if (code != 0) { 126 return nullptr; 127 } 128 } 129 if (argc < FUNC_ARGS_COUNT) { 130 return nullptr; 131 } 132 napi_typeof(env, argv[1], &valueType); 133 if (valueType != napi_valuetype::napi_boolean) { 134 return nullptr; 135 } 136 bool isZoneID = false; 137 status = napi_get_value_bool(env, argv[1], &isZoneID); 138 if (status != napi_ok) { 139 return nullptr; 140 } 141 std::unique_ptr<I18nTimeZoneAddon> obj = std::make_unique<I18nTimeZoneAddon>(); 142 status = 143 napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), I18nTimeZoneAddon::Destructor, nullptr, nullptr); 144 if (status != napi_ok) { 145 return nullptr; 146 } 147 obj->timezone_ = I18nTimeZone::CreateInstance(zoneID, isZoneID); 148 if (!obj->timezone_) { 149 return nullptr; 150 } 151 obj.release(); 152 return thisVar; 153} 154 155napi_value I18nTimeZoneAddon::GetAvailableTimezoneIDs(napi_env env, napi_callback_info info) 156{ 157 I18nErrorCode errorCode = I18nErrorCode::SUCCESS; 158 std::set<std::string> timezoneIDs = I18nTimeZone::GetAvailableIDs(errorCode); 159 if (errorCode != I18nErrorCode::SUCCESS) { 160 return nullptr; 161 } 162 napi_value result = nullptr; 163 napi_status status = napi_create_array_with_length(env, timezoneIDs.size(), &result); 164 if (status != napi_ok) { 165 HILOG_ERROR_I18N("GetAvailableTimezoneIDs: Failed to create array"); 166 return nullptr; 167 } 168 size_t index = 0; 169 for (std::set<std::string>::iterator it = timezoneIDs.begin(); it != timezoneIDs.end(); ++it) { 170 napi_value value = nullptr; 171 status = napi_create_string_utf8(env, (*it).c_str(), NAPI_AUTO_LENGTH, &value); 172 if (status != napi_ok) { 173 HILOG_ERROR_I18N("Failed to create string item"); 174 return nullptr; 175 } 176 status = napi_set_element(env, result, index, value); 177 if (status != napi_ok) { 178 HILOG_ERROR_I18N("Failed to set array item"); 179 return nullptr; 180 } 181 ++index; 182 } 183 return result; 184} 185 186napi_value I18nTimeZoneAddon::GetAvailableZoneCityIDs(napi_env env, napi_callback_info info) 187{ 188 std::set<std::string> cityIDs = I18nTimeZone::GetAvailableZoneCityIDs(); 189 napi_value result = nullptr; 190 napi_status status = napi_create_array_with_length(env, cityIDs.size(), &result); 191 if (status != napi_ok) { 192 HILOG_ERROR_I18N("GetAvailableZoneCityIDs: Failed to create array"); 193 return nullptr; 194 } 195 size_t index = 0; 196 for (auto it = cityIDs.begin(); it != cityIDs.end(); ++it) { 197 napi_value value = nullptr; 198 status = napi_create_string_utf8(env, (*it).c_str(), NAPI_AUTO_LENGTH, &value); 199 if (status != napi_ok) { 200 HILOG_ERROR_I18N("GetAvailableZoneCityIDs: Failed to create string item"); 201 return nullptr; 202 } 203 status = napi_set_element(env, result, index, value); 204 if (status != napi_ok) { 205 HILOG_ERROR_I18N("GetAvailableZoneCityIDs: Failed to set array item"); 206 return nullptr; 207 } 208 ++index; 209 } 210 return result; 211} 212 213napi_value I18nTimeZoneAddon::GetCityDisplayName(napi_env env, napi_callback_info info) 214{ 215 size_t argc = 2; 216 napi_value argv[2] = { 0 }; 217 napi_value thisVar = nullptr; 218 void *data = nullptr; 219 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); 220 if (status != napi_ok) { 221 return nullptr; 222 } 223 if (argc < FUNC_ARGS_COUNT) { 224 return nullptr; 225 } 226 napi_valuetype valueType = napi_valuetype::napi_undefined; 227 napi_typeof(env, argv[0], &valueType); 228 if (valueType != napi_valuetype::napi_string) { 229 HILOG_ERROR_I18N("GetCityDisplayName: Invalid parameter type"); 230 return nullptr; 231 } 232 int32_t code = 0; 233 std::string cityID = VariableConvertor::GetString(env, argv[0], code); 234 if (code != 0) { 235 return nullptr; 236 } 237 std::string locale = VariableConvertor::GetString(env, argv[1], code); 238 if (code != 0) { 239 return nullptr; 240 } 241 std::string name = I18nTimeZone::GetCityDisplayName(cityID, locale); 242 napi_value result = nullptr; 243 status = napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &result); 244 if (status != napi_ok) { 245 return nullptr; 246 } 247 return result; 248} 249 250napi_value I18nTimeZoneAddon::GetTimezoneFromCity(napi_env env, napi_callback_info info) 251{ 252 size_t argc = 1; 253 napi_value argv[1] = { nullptr }; 254 napi_value thisVar = nullptr; 255 void *data = nullptr; 256 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); 257 return StaticGetTimeZone(env, argv, false); 258} 259 260napi_value I18nTimeZoneAddon::GetTimezonesByLocation(napi_env env, napi_callback_info info) 261{ 262 size_t argc = 2; 263 napi_value argv[2] = {0, 0}; 264 napi_value thisVar = nullptr; 265 void *data = nullptr; 266 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); 267 if (status != napi_ok) { 268 return nullptr; 269 } 270 if (argc < FUNC_ARGS_COUNT) { 271 HILOG_ERROR_I18N("GetTimezonesByLocation: Missing parameter"); 272 ErrorUtil::NapiThrow(env, I18N_NOT_FOUND, "longitude or latitude", "", true); 273 return nullptr; 274 } 275 double x; 276 double y; 277 VariableConvertor::VerifyType(env, "longitude", "number", argv[0]); 278 VariableConvertor::VerifyType(env, "latitude", "number", argv[1]); 279 if (!CheckLongitudeTypeAndScope(env, argv[0], x) || 280 !CheckLatitudeTypeAndScope(env, argv[1], y)) { 281 ErrorUtil::NapiThrow(env, I18N_NOT_VALID, "longitude or latitude", "a valid value", true); 282 return nullptr; 283 } 284 napi_value timezoneList = nullptr; 285 napi_create_array(env, &timezoneList); 286 std::vector<std::string> tempList = I18nTimeZone::GetTimezoneIdByLocation(x, y); 287 for (size_t i = 0; i < tempList.size(); i++) { 288 napi_value timezoneId = nullptr; 289 status = napi_create_string_utf8(env, tempList[i].c_str(), NAPI_AUTO_LENGTH, &timezoneId); 290 if (status != napi_ok) { 291 return nullptr; 292 } 293 napi_value argTimeZoneId[1] = { timezoneId }; 294 napi_value timezone = StaticGetTimeZone(env, argTimeZoneId, true); 295 status = napi_set_element(env, timezoneList, i, timezone); 296 if (status != napi_ok) { 297 return nullptr; 298 } 299 } 300 301 return timezoneList; 302} 303 304bool I18nTimeZoneAddon::CheckLongitudeTypeAndScope(napi_env env, napi_value argv, double &x) 305{ 306 napi_status status = napi_get_value_double(env, argv, &x); 307 if (status != napi_ok) { 308 HILOG_ERROR_I18N("GetTimezonesByLocation: Parse first argument x failed"); 309 return false; 310 } 311 // -180 and 179.9 is the scope of longitude 312 if (x < -180 || x > 179.9) { 313 HILOG_ERROR_I18N("GetTimezonesByLocation: Args x exceed it's scope."); 314 return false; 315 } 316 return true; 317} 318 319bool I18nTimeZoneAddon::CheckLatitudeTypeAndScope(napi_env env, napi_value argv, double &y) 320{ 321 napi_status status = napi_get_value_double(env, argv, &y); 322 if (status != napi_ok) { 323 HILOG_ERROR_I18N("GetTimezonesByLocation: Parse second argument y failed"); 324 return false; 325 } 326 // -90 and 89.9 is the scope of latitude 327 if (y < -90 || y > 89.9) { 328 HILOG_ERROR_I18N("GetTimezonesByLocation: Args y exceed it's scope."); 329 return false; 330 } 331 return true; 332} 333 334napi_value I18nTimeZoneAddon::GetID(napi_env env, napi_callback_info info) 335{ 336 size_t argc = 0; 337 napi_value *argv = nullptr; 338 napi_value thisVar = nullptr; 339 void *data = nullptr; 340 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); 341 I18nTimeZoneAddon *obj = nullptr; 342 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj)); 343 if (status != napi_ok || !obj || !obj->timezone_) { 344 HILOG_ERROR_I18N("GetID: Get TimeZone object failed"); 345 return nullptr; 346 } 347 std::string result = obj->timezone_->GetID(); 348 napi_value value = nullptr; 349 status = napi_create_string_utf8(env, result.c_str(), NAPI_AUTO_LENGTH, &value); 350 if (status != napi_ok) { 351 HILOG_ERROR_I18N("GetID: Create result failed"); 352 return nullptr; 353 } 354 return value; 355} 356 357napi_value I18nTimeZoneAddon::GetTimeZoneDisplayName(napi_env env, napi_callback_info info) 358{ 359 size_t argc = 2; 360 napi_value argv[2] = { 0 }; 361 napi_value thisVar = nullptr; 362 void *data = nullptr; 363 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); 364 if (status != napi_ok) { 365 return nullptr; 366 } 367 368 I18nTimeZoneAddon *obj = nullptr; 369 status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj)); 370 if (status != napi_ok || !obj || !obj->timezone_) { 371 HILOG_ERROR_I18N("GetTimeZoneDisplayName: Get TimeZone object failed"); 372 return nullptr; 373 } 374 375 std::string locale; 376 bool isDST = false; 377 int32_t parameterStatus = GetParameter(env, argv, locale, isDST); 378 379 std::string result; 380 if (parameterStatus == -1) { // -1 represents Invalid parameter. 381 HILOG_ERROR_I18N("GetTimeZoneDisplayName: Parameter type does not match"); 382 return nullptr; 383 } else if (parameterStatus == 0) { 384 result = obj->timezone_->GetDisplayName(); 385 } else if (parameterStatus == 1) { // 1 represents one string parameter. 386 result = obj->timezone_->GetDisplayName(locale); 387 } else if (parameterStatus == 2) { // 2 represents one boolean parameter. 388 result = obj->timezone_->GetDisplayName(isDST); 389 } else { 390 result = obj->timezone_->GetDisplayName(locale, isDST); 391 } 392 393 napi_value value = nullptr; 394 status = napi_create_string_utf8(env, result.c_str(), NAPI_AUTO_LENGTH, &value); 395 if (status != napi_ok) { 396 HILOG_ERROR_I18N("GetTimeZoneDisplayName: Create result failed"); 397 return nullptr; 398 } 399 return value; 400} 401 402napi_value I18nTimeZoneAddon::GetRawOffset(napi_env env, napi_callback_info info) 403{ 404 size_t argc = 0; 405 napi_value *argv = nullptr; 406 napi_value thisVar = nullptr; 407 void *data = nullptr; 408 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); 409 I18nTimeZoneAddon *obj = nullptr; 410 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj)); 411 if (status != napi_ok || !obj || !obj->timezone_) { 412 HILOG_ERROR_I18N("GetRawOffset: Get TimeZone object failed"); 413 return nullptr; 414 } 415 int32_t result = obj->timezone_->GetRawOffset(); 416 napi_value value = nullptr; 417 status = napi_create_int32(env, result, &value); 418 if (status != napi_ok) { 419 HILOG_ERROR_I18N("GetRawOffset: Create result failed"); 420 return nullptr; 421 } 422 return value; 423} 424 425napi_value I18nTimeZoneAddon::GetOffset(napi_env env, napi_callback_info info) 426{ 427 size_t argc = 1; 428 napi_value argv[1] = { 0 }; 429 napi_value thisVar = nullptr; 430 void *data = nullptr; 431 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); 432 if (status != napi_ok) { 433 return nullptr; 434 } 435 436 double date = 0; 437 if (VariableConvertor::CheckNapiValueType(env, argv[0])) { 438 napi_valuetype valueType = napi_valuetype::napi_undefined; 439 napi_typeof(env, argv[0], &valueType); 440 if (valueType != napi_valuetype::napi_number) { 441 HILOG_ERROR_I18N("GetOffset: Invalid parameter type"); 442 return nullptr; 443 } 444 status = napi_get_value_double(env, argv[0], &date); 445 if (status != napi_ok) { 446 HILOG_ERROR_I18N("Get parameter date failed"); 447 return nullptr; 448 } 449 } else { 450 auto time = std::chrono::system_clock::now(); 451 auto since_epoch = time.time_since_epoch(); 452 auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(since_epoch); 453 date = (double)millis.count(); 454 } 455 456 I18nTimeZoneAddon *obj = nullptr; 457 status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj)); 458 if (status != napi_ok || !obj || !obj->timezone_) { 459 HILOG_ERROR_I18N("GetOffset: Get TimeZone object failed"); 460 return nullptr; 461 } 462 int32_t result = obj->timezone_->GetOffset(date); 463 napi_value value = nullptr; 464 status = napi_create_int32(env, result, &value); 465 if (status != napi_ok) { 466 HILOG_ERROR_I18N("GetOffset: Create result failed"); 467 return nullptr; 468 } 469 return value; 470} 471 472napi_value I18nTimeZoneAddon::StaticGetTimeZone(napi_env env, napi_value *argv, bool isZoneID) 473{ 474 napi_value constructor = nullptr; 475 napi_status status = napi_get_reference_value(env, *g_timezoneConstructor, &constructor); 476 if (status != napi_ok) { 477 HILOG_ERROR_I18N("Failed to create reference at StaticGetTimeZone"); 478 return nullptr; 479 } 480 napi_value newArgv[2] = { 0 }; 481 newArgv[0] = argv[0]; 482 status = napi_get_boolean(env, isZoneID, &newArgv[1]); 483 if (status != napi_ok) { 484 return nullptr; 485 } 486 napi_value result = nullptr; 487 status = napi_new_instance(env, constructor, 2, newArgv, &result); // 2 is parameter num 488 if (status != napi_ok) { 489 HILOG_ERROR_I18N("StaticGetTimeZone create instance failed"); 490 return nullptr; 491 } 492 return result; 493} 494 495int32_t I18nTimeZoneAddon::GetParameter(napi_env env, napi_value *argv, std::string &localeStr, bool &isDST) 496{ 497 napi_status status = napi_ok; 498 if (VariableConvertor::CheckNapiValueType(env, argv[1])) { 499 napi_valuetype valueType0 = napi_valuetype::napi_undefined; 500 napi_valuetype valueType1 = napi_valuetype::napi_undefined; 501 napi_typeof(env, argv[0], &valueType0); // 0 represents first parameter 502 napi_typeof(env, argv[1], &valueType1); // 1 represents second parameter 503 bool firstParamFlag = VariableConvertor::CheckNapiValueType(env, argv[0]); 504 if (valueType1 == napi_valuetype::napi_boolean) { 505 status = napi_get_value_bool(env, argv[1], &isDST); 506 if (status != napi_ok) { 507 return -1; // -1 represents Invalid parameter. 508 } else if (!firstParamFlag) { 509 return 2; // 2 represents one boolean parameter. 510 } 511 if (valueType0 == napi_valuetype::napi_string && 512 GetStringFromJS(env, argv[0], localeStr)) { 513 return 3; // 3 represents one string parameter and one bool parameter. 514 } 515 } 516 return -1; // -1 represents Invalid parameter. 517 } 518 return GetFirstParameter(env, argv[0], localeStr, isDST); 519} 520 521bool I18nTimeZoneAddon::GetStringFromJS(napi_env env, napi_value argv, std::string &jsString) 522{ 523 size_t len = 0; 524 napi_status status = napi_get_value_string_utf8(env, argv, nullptr, 0, &len); 525 if (status != napi_ok) { 526 HILOG_ERROR_I18N("Failed to get string length"); 527 return false; 528 } 529 std::vector<char> argvBuf(len + 1); 530 status = napi_get_value_string_utf8(env, argv, argvBuf.data(), len + 1, &len); 531 if (status != napi_ok) { 532 HILOG_ERROR_I18N("Failed to get string item"); 533 return false; 534 } 535 jsString = argvBuf.data(); 536 return true; 537} 538 539int32_t I18nTimeZoneAddon::GetFirstParameter(napi_env env, napi_value value, std::string &localeStr, bool &isDST) 540{ 541 if (!VariableConvertor::CheckNapiValueType(env, value)) { 542 return 0; // 0 represents no parameter. 543 } else { 544 napi_status status = napi_ok; 545 napi_valuetype valueType = napi_valuetype::napi_undefined; 546 napi_typeof(env, value, &valueType); 547 if (valueType == napi_valuetype::napi_string) { 548 bool valid = GetStringFromJS(env, value, localeStr); 549 // -1 represents Invalid parameter. 550 // 1 represents one string parameter. 551 return !valid ? -1 : 1; 552 } else if (valueType == napi_valuetype::napi_boolean) { 553 status = napi_get_value_bool(env, value, &isDST); 554 // -1 represents Invalid parameter. 555 // 2 represents one boolean parameter. 556 return (status != napi_ok) ? -1 : 2; 557 } 558 return -1; // -1 represents Invalid parameter. 559 } 560} 561} // namespace I18n 562} // namespace Global 563} // namespace OHOS