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
16 #include "ecmascript/intl/locale_helper.h"
17 #include "ecmascript/global_env.h"
18 #include "ecmascript/js_date.h"
19 #include "ecmascript/js_date_time_format.h"
20 #include "ecmascript/js_locale.h"
21 #include "ecmascript/object_factory-inl.h"
22 #include "ecmascript/tests/ecma_test_common.h"
23
24 using namespace panda;
25 using namespace panda::ecmascript;
26 using namespace panda::ecmascript::base;
27 using LocaleHelper = panda::ecmascript::intl::LocaleHelper;
28
29 namespace panda::test {
30 class JSDateTimeFormatTest : public BaseTestWithScope<true> {
31 };
32
33 /**
34 * @tc.name: GetIcuLocale & SetIcuLocale
35 * @tc.desc: Set and obtain localization labels compatible with ICU Libraries.
36 * @tc.type: FUNC
37 * @tc.require:
38 */
HWTEST_F_L0(JSDateTimeFormatTest,Set_Get_IcuLocale)39 HWTEST_F_L0(JSDateTimeFormatTest, Set_Get_IcuLocale)
40 {
41 auto vm = thread->GetEcmaVM();
42 auto factory = vm->GetFactory();
43 auto env = vm->GetGlobalEnv();
44 JSHandle<JSTaggedValue> ctor = env->GetDateTimeFormatFunction();
45 JSHandle<JSDateTimeFormat> dtf =
46 JSHandle<JSDateTimeFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), ctor));
47
48 icu::Locale locale1("ko", "Kore", "KR");
49 JSDateTimeFormat::SetIcuLocale(thread, dtf, locale1, JSDateTimeFormat::FreeIcuLocale);
50 icu::Locale *resLocale1 = dtf->GetIcuLocale();
51 EXPECT_STREQ(resLocale1->getBaseName(), "ko_Kore_KR");
52
53 icu::Locale locale2("zh", "Hans", "Cn");
54 JSDateTimeFormat::SetIcuLocale(thread, dtf, locale2, JSDateTimeFormat::FreeIcuLocale);
55 icu::Locale *resLocale2 = dtf->GetIcuLocale();
56 EXPECT_STREQ(resLocale2->getBaseName(), "zh_Hans_CN");
57 }
58
59 /**
60 * @tc.name: SetIcuSimpleDateFormat & GetIcuSimpleDateFormat
61 * @tc.desc: Set and obtain a simple time and date format compatible with ICU Libraries.
62 * @tc.type: FUNC
63 * @tc.require:
64 */
HWTEST_F_L0(JSDateTimeFormatTest,Set_Get_IcuSimpleDateFormat)65 HWTEST_F_L0(JSDateTimeFormatTest, Set_Get_IcuSimpleDateFormat)
66 {
67 auto vm = thread->GetEcmaVM();
68 auto factory = vm->GetFactory();
69 auto env = vm->GetGlobalEnv();
70 const icu::UnicodeString timeZoneId("Asia/Shanghai");
71 icu::TimeZone *tz = icu::TimeZone::createTimeZone(timeZoneId);
72 icu::TimeZone::adoptDefault(tz);
73 JSHandle<JSTaggedValue> ctor = env->GetDateTimeFormatFunction();
74 JSHandle<JSDateTimeFormat> dtf =
75 JSHandle<JSDateTimeFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor), ctor));
76 UErrorCode status = UErrorCode::U_ZERO_ERROR;
77 icu::UnicodeString dateTime1("2022.05.25 11:09:34");
78 icu::UnicodeString dateTime2("2022.May.25 11:09:34");
79 icu::UnicodeString dateTime3("2022.May.25 AD 11:09:34 AM");
80
81 icu::UnicodeString pattern("yyyy.MM.dd HH:mm:ss");
82 icu::SimpleDateFormat sdf(pattern, status);
83 JSDateTimeFormat::SetIcuSimpleDateFormat(thread, dtf, sdf, JSDateTimeFormat::FreeSimpleDateFormat);
84 icu::SimpleDateFormat *resSdf = dtf->GetIcuSimpleDateFormat();
85 UDate timeStamp = resSdf->parse(dateTime1, status);
86 EXPECT_EQ(timeStamp, 1653448174000);
87 status = UErrorCode::U_ZERO_ERROR;
88 timeStamp = resSdf->parse(dateTime2, status);
89 EXPECT_EQ(timeStamp, 1653448174000);
90 status = UErrorCode::U_ZERO_ERROR;
91 timeStamp = resSdf->parse(dateTime3, status);
92 EXPECT_EQ(timeStamp, 0);
93
94 status = UErrorCode::U_ZERO_ERROR;
95 icu::UnicodeString pattern2("yyyyy.MMMMM.dd GGG hh:mm::ss aaa");
96 icu::SimpleDateFormat sdf2(pattern2, status);
97 JSDateTimeFormat::SetIcuSimpleDateFormat(thread, dtf, sdf2, JSDateTimeFormat::FreeSimpleDateFormat);
98 icu::SimpleDateFormat *resSdf2 = dtf->GetIcuSimpleDateFormat();
99 timeStamp = resSdf2->parse(dateTime1, status);
100 EXPECT_EQ(timeStamp, 0);
101 status = UErrorCode::U_ZERO_ERROR;
102 timeStamp = resSdf2->parse(dateTime2, status);
103 EXPECT_EQ(timeStamp, 0);
104 status = UErrorCode::U_ZERO_ERROR;
105 timeStamp = resSdf2->parse(dateTime3, status);
106 EXPECT_EQ(timeStamp, 1653448174000);
107 }
108
109 /**
110 * @tc.name: InitializeDateTimeFormat
111 * @tc.desc: Initialize the time and date format through localization label locales and options.
112 * Options can include year, month, day, hour, minute, second, time zone and weekday, etc.
113 * @tc.type: FUNC
114 * @tc.require:
115 */
HWTEST_F_L0(JSDateTimeFormatTest,InitializeDateTimeFormat)116 HWTEST_F_L0(JSDateTimeFormatTest, InitializeDateTimeFormat)
117 {
118 auto vm = thread->GetEcmaVM();
119 auto factory = vm->GetFactory();
120 auto env = vm->GetGlobalEnv();
121 // Create locales.
122 JSHandle<JSTaggedValue> localeCtor = env->GetLocaleFunction();
123 JSHandle<JSLocale> locales =
124 JSHandle<JSLocale>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(localeCtor), localeCtor));
125 icu::Locale icuLocale("zh", "Hans", "Cn", "calendar=chinese");
126 factory->NewJSIntlIcuData(locales, icuLocale, JSLocale::FreeIcuLocale);
127 // Create options.
128 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
129 JSHandle<JSObject> options = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
130 options = JSDateTimeFormat::ToDateTimeOptions(
131 thread, JSHandle<JSTaggedValue>::Cast(options), RequiredOption::ANY, DefaultsOption::ALL);
132 // Initialize DateTimeFormat.
133 JSHandle<JSTaggedValue> dtfCtor = env->GetDateTimeFormatFunction();
134 JSHandle<JSDateTimeFormat> dtf =
135 JSHandle<JSDateTimeFormat>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(dtfCtor), dtfCtor));
136 dtf = JSDateTimeFormat::InitializeDateTimeFormat(
137 thread, dtf, JSHandle<JSTaggedValue>::Cast(locales), JSHandle<JSTaggedValue>::Cast(options));
138
139 JSHandle<JSTaggedValue> localeTagVal(thread, dtf->GetLocale());
140 JSHandle<EcmaString> localeEcmaStr = JSHandle<EcmaString>::Cast(localeTagVal);
141 std::string localeStr = LocaleHelper::ConvertToStdString(localeEcmaStr);
142 EXPECT_STREQ(localeStr.c_str(), "zh-Hans-CN-u-ca-chinese");
143 }
144
145 /**
146 * @tc.name: ToDateTimeOptions
147 * @tc.desc: Empty or incomplete option objects are supplemented according to the required option and default option.
148 * @tc.type: FUNC
149 * @tc.require:
150 */
HWTEST_F_L0(JSDateTimeFormatTest,ToDateTimeOptions_001)151 HWTEST_F_L0(JSDateTimeFormatTest, ToDateTimeOptions_001)
152 {
153 auto vm = thread->GetEcmaVM();
154 auto factory = vm->GetFactory();
155 auto env = vm->GetGlobalEnv();
156 auto globalConst = thread->GlobalConstants();
157
158 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
159 JSHandle<JSTaggedValue> yearKey = globalConst->GetHandledYearString();
160 JSHandle<JSTaggedValue> monthKey = globalConst->GetHandledMonthString();
161 JSHandle<JSTaggedValue> dayKey = globalConst->GetHandledDayString();
162 JSHandle<JSTaggedValue> hourKey = globalConst->GetHandledHourString();
163 JSHandle<JSTaggedValue> minuteKey = globalConst->GetHandledMinuteString();
164 JSHandle<JSTaggedValue> secondKey = globalConst->GetHandledSecondString();
165
166 // When the option value is blank, it will be set according to the default option,
167 // including the year, month, day, hour, minute and second, and the values are all numeric.
168 JSHandle<JSObject> options = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
169 options = JSDateTimeFormat::ToDateTimeOptions(
170 thread, JSHandle<JSTaggedValue>::Cast(options), RequiredOption::ANY, DefaultsOption::ALL); // test "ALL"
171 auto yearEcmaStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, yearKey).GetValue());
172 EXPECT_STREQ(LocaleHelper::ConvertToStdString(yearEcmaStr).c_str(), "numeric");
173 auto monthEcmaStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, monthKey).GetValue());
174 EXPECT_STREQ(LocaleHelper::ConvertToStdString(monthEcmaStr).c_str(), "numeric");
175 auto dayEcmaStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, dayKey).GetValue());
176 EXPECT_STREQ(LocaleHelper::ConvertToStdString(dayEcmaStr).c_str(), "numeric");
177 auto hourEcmaStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, hourKey).GetValue());
178 EXPECT_STREQ(LocaleHelper::ConvertToStdString(hourEcmaStr).c_str(), "numeric");
179 auto minuteEcmaStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, minuteKey).GetValue());
180 EXPECT_STREQ(LocaleHelper::ConvertToStdString(minuteEcmaStr).c_str(), "numeric");
181 auto secondEcmaStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, secondKey).GetValue());
182 EXPECT_STREQ(LocaleHelper::ConvertToStdString(secondEcmaStr).c_str(), "numeric");
183 }
184
HWTEST_F_L0(JSDateTimeFormatTest,ToDateTimeOptions_002)185 HWTEST_F_L0(JSDateTimeFormatTest, ToDateTimeOptions_002)
186 {
187 auto vm = thread->GetEcmaVM();
188 auto factory = vm->GetFactory();
189 auto env = vm->GetGlobalEnv();
190 auto globalConst = thread->GlobalConstants();
191
192 JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
193 JSHandle<JSTaggedValue> weekdayKey = globalConst->GetHandledWeekdayString();
194 JSHandle<JSTaggedValue> yearKey = globalConst->GetHandledYearString();
195 JSHandle<JSTaggedValue> monthKey = globalConst->GetHandledMonthString();
196 JSHandle<JSTaggedValue> dayKey = globalConst->GetHandledDayString();
197 JSHandle<JSTaggedValue> dayPeriodKey = globalConst->GetHandledDayPeriodString();
198 JSHandle<JSTaggedValue> hourKey = globalConst->GetHandledHourString();
199 JSHandle<JSTaggedValue> minuteKey = globalConst->GetHandledMinuteString();
200 JSHandle<JSTaggedValue> secondKey = globalConst->GetHandledSecondString();
201 JSHandle<JSTaggedValue> fracSecKey = globalConst->GetHandledFractionalSecondDigitsString();
202
203 // When the option value is not empty, it will be set according to the required options.
204 JSHandle<JSObject> options = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
205 std::map<std::string, std::string> dateOptionsMap {
206 { "weekday", "narrow" },
207 { "year", "2-digit" },
208 { "month", "2-digit" },
209 { "day", "2-digit" }
210 };
211 std::map<std::string, std::string> timeOptionsMap {
212 { "dayPeriod", "narrow" },
213 { "hour", "2-digit" },
214 { "minute", "2-digit" },
215 { "second", "2-digit" },
216 { "fractionalSecond", "1" }
217 };
218 EcmaTestCommon::SetDateOptionsTest(thread, options, dateOptionsMap);
219 EcmaTestCommon::SetTimeOptionsTest(thread, options, timeOptionsMap);
220 options = JSDateTimeFormat::ToDateTimeOptions(
221 thread, JSHandle<JSTaggedValue>::Cast(options), RequiredOption::ANY, DefaultsOption::ALL); // test "ANY"
222 auto weekdayStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, weekdayKey).GetValue());
223 EXPECT_STREQ(LocaleHelper::ConvertToStdString(weekdayStr).c_str(), "narrow");
224 auto yearStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, yearKey).GetValue());
225 EXPECT_STREQ(LocaleHelper::ConvertToStdString(yearStr).c_str(), "2-digit");
226 auto monthStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, monthKey).GetValue());
227 EXPECT_STREQ(LocaleHelper::ConvertToStdString(monthStr).c_str(), "2-digit");
228 auto dayStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, dayKey).GetValue());
229 EXPECT_STREQ(LocaleHelper::ConvertToStdString(dayStr).c_str(), "2-digit");
230
231 auto dayPeriodStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, dayPeriodKey).GetValue());
232 EXPECT_STREQ(LocaleHelper::ConvertToStdString(dayPeriodStr).c_str(), "narrow");
233 auto hourStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, hourKey).GetValue());
234 EXPECT_STREQ(LocaleHelper::ConvertToStdString(hourStr).c_str(), "2-digit");
235 auto minuteStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, minuteKey).GetValue());
236 EXPECT_STREQ(LocaleHelper::ConvertToStdString(minuteStr).c_str(), "2-digit");
237 auto secondStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, secondKey).GetValue());
238 EXPECT_STREQ(LocaleHelper::ConvertToStdString(secondStr).c_str(), "2-digit");
239 auto fracSecStr = JSHandle<EcmaString>::Cast(JSObject::GetProperty(thread, options, fracSecKey).GetValue());
240 EXPECT_STREQ(LocaleHelper::ConvertToStdString(fracSecStr).c_str(), "1");
241 }
242
243
244 /**
245 * @tc.name: FormatDateTime
246 * @tc.desc: Convert floating-point timestamp to fixed format time date through time date format.
247 * @tc.type: FUNC
248 * @tc.require:
249 */
HWTEST_F_L0(JSDateTimeFormatTest,FormatDateTime_001)250 HWTEST_F_L0(JSDateTimeFormatTest, FormatDateTime_001)
251 {
252 icu::Locale icuLocale("zh", "Hans", "Cn");
253 std::string cycle("h12");
254 std::string zone("ETC/GMT-8");
255 auto options = EcmaTestCommon::SetHourCycleKeyValue(thread, cycle, zone);
256 JSHandle<JSDateTimeFormat> dtf = EcmaTestCommon::CreateDateTimeFormatTest(thread, icuLocale, options);
257
258 double timeStamp1 = 1653448174000; // test "2022-05-25 11:09:34.000"
259 double timeStamp2 = 1653921012999; // test "2022-05-30 22:30:12.999"
260
261 // When the option is blank, the default format is "yyyy/MM/dd", the year, month and day are all numeric,
262 // because the default option in initialization is "DefaultsOption::DATE".
263 JSHandle<EcmaString> dateTimeEcamStr1 = JSDateTimeFormat::FormatDateTime(thread, dtf, timeStamp1);
264 EXPECT_STREQ(LocaleHelper::ConvertToStdString(dateTimeEcamStr1).c_str(), "2022/5/25");
265 JSHandle<EcmaString> dateTimeEcamStr2 = JSDateTimeFormat::FormatDateTime(thread, dtf, timeStamp2);
266 EXPECT_STREQ(LocaleHelper::ConvertToStdString(dateTimeEcamStr2).c_str(), "2022/5/30");
267 }
268 } // namespace panda::test
269