Bug Summary

File:out/../deps/v8/src/objects/js-date-time-format.cc
Warning:line 2173, column 9
Value stored to 'previous_end_pos' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name js-date-time-format.cc -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/home/maurizio/node-v18.6.0/out -resource-dir /usr/local/lib/clang/16.0.0 -D _GLIBCXX_USE_CXX11_ABI=1 -D NODE_OPENSSL_CONF_NAME=nodejs_conf -D NODE_OPENSSL_HAS_QUIC -D V8_GYP_BUILD -D V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP=64 -D __STDC_FORMAT_MACROS -D OPENSSL_NO_PINSHARED -D OPENSSL_THREADS -D V8_TARGET_ARCH_X64 -D V8_HAVE_TARGET_OS -D V8_TARGET_OS_LINUX -D V8_EMBEDDER_STRING="-node.8" -D ENABLE_DISASSEMBLER -D V8_PROMISE_INTERNAL_FIELD_COUNT=1 -D V8_SHORT_BUILTIN_CALLS -D OBJECT_PRINT -D V8_INTL_SUPPORT -D V8_ATOMIC_OBJECT_FIELD_WRITES -D V8_ENABLE_LAZY_SOURCE_POSITIONS -D V8_USE_SIPHASH -D V8_SHARED_RO_HEAP -D V8_WIN64_UNWINDING_INFO -D V8_ENABLE_REGEXP_INTERPRETER_THREADED_DISPATCH -D V8_SNAPSHOT_COMPRESSION -D V8_ENABLE_WEBASSEMBLY -D V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS -D V8_ALLOCATION_FOLDING -D V8_ALLOCATION_SITE_TRACKING -D V8_SCRIPTORMODULE_LEGACY_LIFETIME -D V8_ADVANCED_BIGINT_ALGORITHMS -D ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_STATIC -D UCONFIG_NO_SERVICE=1 -D U_ENABLE_DYLOAD=0 -D U_STATIC_IMPLEMENTATION=1 -D U_HAVE_STD_STRING=1 -D UCONFIG_NO_BREAK_ITERATION=0 -I ../deps/v8 -I ../deps/v8/include -I /home/maurizio/node-v18.6.0/out/Release/obj/gen/inspector-generated-output-root -I ../deps/v8/third_party/inspector_protocol -I /home/maurizio/node-v18.6.0/out/Release/obj/gen -I /home/maurizio/node-v18.6.0/out/Release/obj/gen/generate-bytecode-output-root -I ../deps/icu-small/source/i18n -I ../deps/icu-small/source/common -I ../deps/v8/third_party/zlib -I ../deps/v8/third_party/zlib/google -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/x86_64-redhat-linux -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/backward -internal-isystem /usr/local/lib/clang/16.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-unused-parameter -Wno-return-type -std=gnu++17 -fdeprecated-macro -fdebug-compilation-dir=/home/maurizio/node-v18.6.0/out -ferror-limit 19 -fno-rtti -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-08-22-142216-507842-1 -x c++ ../deps/v8/src/objects/js-date-time-format.cc
1// Copyright 2018 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_INTL_SUPPORT1
6#error Internationalization is expected to be enabled.
7#endif // V8_INTL_SUPPORT
8
9#include "src/objects/js-date-time-format.h"
10
11#include <algorithm>
12#include <map>
13#include <memory>
14#include <string>
15#include <utility>
16#include <vector>
17
18#include "src/date/date.h"
19#include "src/execution/isolate.h"
20#include "src/heap/factory.h"
21#include "src/objects/intl-objects.h"
22#include "src/objects/js-date-time-format-inl.h"
23#include "src/objects/managed-inl.h"
24#include "src/objects/option-utils.h"
25#include "unicode/calendar.h"
26#include "unicode/dtitvfmt.h"
27#include "unicode/dtptngen.h"
28#include "unicode/fieldpos.h"
29#include "unicode/gregocal.h"
30#include "unicode/smpdtfmt.h"
31#include "unicode/unistr.h"
32
33namespace v8 {
34namespace internal {
35
36namespace {
37
38std::string ToHourCycleString(JSDateTimeFormat::HourCycle hc) {
39 switch (hc) {
40 case JSDateTimeFormat::HourCycle::kH11:
41 return "h11";
42 case JSDateTimeFormat::HourCycle::kH12:
43 return "h12";
44 case JSDateTimeFormat::HourCycle::kH23:
45 return "h23";
46 case JSDateTimeFormat::HourCycle::kH24:
47 return "h24";
48 case JSDateTimeFormat::HourCycle::kUndefined:
49 return "";
50 default:
51 UNREACHABLE()V8_Fatal("unreachable code");
52 }
53}
54
55JSDateTimeFormat::HourCycle ToHourCycle(const std::string& hc) {
56 if (hc == "h11") return JSDateTimeFormat::HourCycle::kH11;
57 if (hc == "h12") return JSDateTimeFormat::HourCycle::kH12;
58 if (hc == "h23") return JSDateTimeFormat::HourCycle::kH23;
59 if (hc == "h24") return JSDateTimeFormat::HourCycle::kH24;
60 return JSDateTimeFormat::HourCycle::kUndefined;
61}
62
63JSDateTimeFormat::HourCycle ToHourCycle(UDateFormatHourCycle hc) {
64 switch (hc) {
65 case UDAT_HOUR_CYCLE_11:
66 return JSDateTimeFormat::HourCycle::kH11;
67 case UDAT_HOUR_CYCLE_12:
68 return JSDateTimeFormat::HourCycle::kH12;
69 case UDAT_HOUR_CYCLE_23:
70 return JSDateTimeFormat::HourCycle::kH23;
71 case UDAT_HOUR_CYCLE_24:
72 return JSDateTimeFormat::HourCycle::kH24;
73 default:
74 return JSDateTimeFormat::HourCycle::kUndefined;
75 }
76}
77
78Maybe<JSDateTimeFormat::HourCycle> GetHourCycle(Isolate* isolate,
79 Handle<JSReceiver> options,
80 const char* method_name) {
81 return GetStringOption<JSDateTimeFormat::HourCycle>(
82 isolate, options, "hourCycle", method_name, {"h11", "h12", "h23", "h24"},
83 {JSDateTimeFormat::HourCycle::kH11, JSDateTimeFormat::HourCycle::kH12,
84 JSDateTimeFormat::HourCycle::kH23, JSDateTimeFormat::HourCycle::kH24},
85 JSDateTimeFormat::HourCycle::kUndefined);
86}
87
88class PatternMap {
89 public:
90 PatternMap(std::string pattern, std::string value)
91 : pattern(std::move(pattern)), value(std::move(value)) {}
92 virtual ~PatternMap() = default;
93 std::string pattern;
94 std::string value;
95};
96
97class PatternItem {
98 public:
99 PatternItem(const std::string property, std::vector<PatternMap> pairs,
100 std::vector<const char*> allowed_values)
101 : property(std::move(property)),
102 pairs(std::move(pairs)),
103 allowed_values(allowed_values) {}
104 virtual ~PatternItem() = default;
105
106 const std::string property;
107 // It is important for the pattern in the pairs from longer one to shorter one
108 // if the longer one contains substring of an shorter one.
109 std::vector<PatternMap> pairs;
110 std::vector<const char*> allowed_values;
111};
112
113static std::vector<PatternItem> BuildPatternItems() {
114 const std::vector<const char*> kLongShort = {"long", "short"};
115 const std::vector<const char*> kNarrowLongShort = {"narrow", "long", "short"};
116 const std::vector<const char*> k2DigitNumeric = {"2-digit", "numeric"};
117 const std::vector<const char*> kNarrowLongShort2DigitNumeric = {
118 "narrow", "long", "short", "2-digit", "numeric"};
119 std::vector<PatternItem> items = {
120 PatternItem("weekday",
121 {{"EEEEE", "narrow"},
122 {"EEEE", "long"},
123 {"EEE", "short"},
124 {"ccccc", "narrow"},
125 {"cccc", "long"},
126 {"ccc", "short"}},
127 kNarrowLongShort),
128 PatternItem("era",
129 {{"GGGGG", "narrow"}, {"GGGG", "long"}, {"GGG", "short"}},
130 kNarrowLongShort),
131 PatternItem("year", {{"yy", "2-digit"}, {"y", "numeric"}},
132 k2DigitNumeric)};
133 // Sometimes we get L instead of M for month - standalone name.
134 items.push_back(PatternItem("month",
135 {{"MMMMM", "narrow"},
136 {"MMMM", "long"},
137 {"MMM", "short"},
138 {"MM", "2-digit"},
139 {"M", "numeric"},
140 {"LLLLL", "narrow"},
141 {"LLLL", "long"},
142 {"LLL", "short"},
143 {"LL", "2-digit"},
144 {"L", "numeric"}},
145 kNarrowLongShort2DigitNumeric));
146 items.push_back(PatternItem("day", {{"dd", "2-digit"}, {"d", "numeric"}},
147 k2DigitNumeric));
148 items.push_back(PatternItem("dayPeriod",
149 {{"BBBBB", "narrow"},
150 {"bbbbb", "narrow"},
151 {"BBBB", "long"},
152 {"bbbb", "long"},
153 {"B", "short"},
154 {"b", "short"}},
155 kNarrowLongShort));
156 items.push_back(PatternItem("hour",
157 {{"HH", "2-digit"},
158 {"H", "numeric"},
159 {"hh", "2-digit"},
160 {"h", "numeric"},
161 {"kk", "2-digit"},
162 {"k", "numeric"},
163 {"KK", "2-digit"},
164 {"K", "numeric"}},
165 k2DigitNumeric));
166 items.push_back(PatternItem("minute", {{"mm", "2-digit"}, {"m", "numeric"}},
167 k2DigitNumeric));
168 items.push_back(PatternItem("second", {{"ss", "2-digit"}, {"s", "numeric"}},
169 k2DigitNumeric));
170
171 const std::vector<const char*> kTimezone = {"long", "short",
172 "longOffset", "shortOffset",
173 "longGeneric", "shortGeneric"};
174 items.push_back(PatternItem("timeZoneName",
175 {{"zzzz", "long"},
176 {"z", "short"},
177 {"OOOO", "longOffset"},
178 {"O", "shortOffset"},
179 {"vvvv", "longGeneric"},
180 {"v", "shortGeneric"}},
181 kTimezone));
182 return items;
183}
184
185class PatternItems {
186 public:
187 PatternItems() : data(BuildPatternItems()) {}
188 virtual ~PatternItems() = default;
189 const std::vector<PatternItem>& Get() const { return data; }
190
191 private:
192 const std::vector<PatternItem> data;
193};
194
195static const std::vector<PatternItem>& GetPatternItems() {
196 static base::LazyInstance<PatternItems>::type items =
197 LAZY_INSTANCE_INITIALIZER{ { 0 }, { {} } };
198 return items.Pointer()->Get();
199}
200
201class PatternData {
202 public:
203 PatternData(const std::string property, std::vector<PatternMap> pairs,
204 std::vector<const char*> allowed_values)
205 : property(std::move(property)), allowed_values(allowed_values) {
206 for (const auto& pair : pairs) {
207 map.insert(std::make_pair(pair.value, pair.pattern));
208 }
209 }
210 virtual ~PatternData() = default;
211
212 const std::string property;
213 std::map<const std::string, const std::string> map;
214 std::vector<const char*> allowed_values;
215};
216
217const std::vector<PatternData> CreateCommonData(const PatternData& hour_data) {
218 std::vector<PatternData> build;
219 for (const PatternItem& item : GetPatternItems()) {
220 if (item.property == "hour") {
221 build.push_back(hour_data);
222 } else {
223 build.push_back(
224 PatternData(item.property, item.pairs, item.allowed_values));
225 }
226 }
227 return build;
228}
229
230const std::vector<PatternData> CreateData(const char* digit2,
231 const char* numeric) {
232 return CreateCommonData(
233 PatternData("hour", {{digit2, "2-digit"}, {numeric, "numeric"}},
234 {"2-digit", "numeric"}));
235}
236
237// According to "Date Field Symbol Table" in
238// http://userguide.icu-project.org/formatparse/datetime
239// Symbol | Meaning | Example(s)
240// h hour in am/pm (1~12) h 7
241// hh 07
242// H hour in day (0~23) H 0
243// HH 00
244// k hour in day (1~24) k 24
245// kk 24
246// K hour in am/pm (0~11) K 0
247// KK 00
248
249class Pattern {
250 public:
251 Pattern(const char* d1, const char* d2) : data(CreateData(d1, d2)) {}
252 virtual ~Pattern() = default;
253 virtual const std::vector<PatternData>& Get() const { return data; }
254
255 private:
256 std::vector<PatternData> data;
257};
258
259#define DEFFINE_TRAIT(name, d1, d2) \
260 struct name { \
261 static void Construct(void* allocated_ptr) { \
262 new (allocated_ptr) Pattern(d1, d2); \
263 } \
264 };
265DEFFINE_TRAIT(H11Trait, "KK", "K")
266DEFFINE_TRAIT(H12Trait, "hh", "h")
267DEFFINE_TRAIT(H23Trait, "HH", "H")
268DEFFINE_TRAIT(H24Trait, "kk", "k")
269DEFFINE_TRAIT(HDefaultTrait, "jj", "j")
270#undef DEFFINE_TRAIT
271
272const std::vector<PatternData>& GetPatternData(
273 JSDateTimeFormat::HourCycle hour_cycle) {
274 switch (hour_cycle) {
275 case JSDateTimeFormat::HourCycle::kH11: {
276 static base::LazyInstance<Pattern, H11Trait>::type h11 =
277 LAZY_INSTANCE_INITIALIZER{ { 0 }, { {} } };
278 return h11.Pointer()->Get();
279 }
280 case JSDateTimeFormat::HourCycle::kH12: {
281 static base::LazyInstance<Pattern, H12Trait>::type h12 =
282 LAZY_INSTANCE_INITIALIZER{ { 0 }, { {} } };
283 return h12.Pointer()->Get();
284 }
285 case JSDateTimeFormat::HourCycle::kH23: {
286 static base::LazyInstance<Pattern, H23Trait>::type h23 =
287 LAZY_INSTANCE_INITIALIZER{ { 0 }, { {} } };
288 return h23.Pointer()->Get();
289 }
290 case JSDateTimeFormat::HourCycle::kH24: {
291 static base::LazyInstance<Pattern, H24Trait>::type h24 =
292 LAZY_INSTANCE_INITIALIZER{ { 0 }, { {} } };
293 return h24.Pointer()->Get();
294 }
295 case JSDateTimeFormat::HourCycle::kUndefined: {
296 static base::LazyInstance<Pattern, HDefaultTrait>::type hDefault =
297 LAZY_INSTANCE_INITIALIZER{ { 0 }, { {} } };
298 return hDefault.Pointer()->Get();
299 }
300 default:
301 UNREACHABLE()V8_Fatal("unreachable code");
302 }
303}
304
305std::string GetGMTTzID(const std::string& input) {
306 std::string ret = "Etc/GMT";
307 switch (input.length()) {
308 case 8:
309 if (input[7] == '0') return ret + '0';
310 break;
311 case 9:
312 if ((input[7] == '+' || input[7] == '-') &&
313 base::IsInRange(input[8], '0', '9')) {
314 return ret + input[7] + input[8];
315 }
316 break;
317 case 10:
318 if ((input[7] == '+' || input[7] == '-') && (input[8] == '1') &&
319 base::IsInRange(input[9], '0', '4')) {
320 return ret + input[7] + input[8] + input[9];
321 }
322 break;
323 }
324 return "";
325}
326
327// Locale independenty version of isalpha for ascii range. This will return
328// false if the ch is alpha but not in ascii range.
329bool IsAsciiAlpha(char ch) {
330 return base::IsInRange(ch, 'A', 'Z') || base::IsInRange(ch, 'a', 'z');
331}
332
333// Locale independent toupper for ascii range. This will not return İ (dotted I)
334// for i under Turkish locale while std::toupper may.
335char LocaleIndependentAsciiToUpper(char ch) {
336 return (base::IsInRange(ch, 'a', 'z')) ? (ch - 'a' + 'A') : ch;
337}
338
339// Locale independent tolower for ascii range.
340char LocaleIndependentAsciiToLower(char ch) {
341 return (base::IsInRange(ch, 'A', 'Z')) ? (ch - 'A' + 'a') : ch;
342}
343
344// Returns titlecased location, bueNos_airES -> Buenos_Aires
345// or ho_cHi_minH -> Ho_Chi_Minh. It is locale-agnostic and only
346// deals with ASCII only characters.
347// 'of', 'au' and 'es' are special-cased and lowercased.
348// ICU's timezone parsing is case sensitive, but ECMAScript is case insensitive
349std::string ToTitleCaseTimezoneLocation(const std::string& input) {
350 std::string title_cased;
351 int word_length = 0;
352 for (char ch : input) {
353 // Convert first char to upper case, the rest to lower case
354 if (IsAsciiAlpha(ch)) {
355 title_cased += word_length == 0 ? LocaleIndependentAsciiToUpper(ch)
356 : LocaleIndependentAsciiToLower(ch);
357 word_length++;
358 } else if (ch == '_' || ch == '-' || ch == '/') {
359 // Special case Au/Es/Of to be lower case.
360 if (word_length == 2) {
361 size_t pos = title_cased.length() - 2;
362 std::string substr = title_cased.substr(pos, 2);
363 if (substr == "Of" || substr == "Es" || substr == "Au") {
364 title_cased[pos] = LocaleIndependentAsciiToLower(title_cased[pos]);
365 }
366 }
367 title_cased += ch;
368 word_length = 0;
369 } else {
370 // Invalid input
371 return std::string();
372 }
373 }
374
375 return title_cased;
376}
377
378class SpecialTimeZoneMap {
379 public:
380 SpecialTimeZoneMap() {
381 Add("America/Argentina/ComodRivadavia");
382 Add("America/Knox_IN");
383 Add("Antarctica/DumontDUrville");
384 Add("Antarctica/McMurdo");
385 Add("Australia/ACT");
386 Add("Australia/LHI");
387 Add("Australia/NSW");
388 Add("Brazil/DeNoronha");
389 Add("Chile/EasterIsland");
390 Add("GB");
391 Add("GB-Eire");
392 Add("Mexico/BajaNorte");
393 Add("Mexico/BajaSur");
394 Add("NZ");
395 Add("NZ-CHAT");
396 Add("W-SU");
397 }
398
399 std::string Find(const std::string& id) {
400 auto it = map_.find(id);
401 if (it != map_.end()) {
402 return it->second;
403 }
404 return "";
405 }
406
407 private:
408 void Add(const char* id) {
409 std::string upper(id);
410 transform(upper.begin(), upper.end(), upper.begin(),
411 LocaleIndependentAsciiToUpper);
412 map_.insert({upper, id});
413 }
414 std::map<std::string, std::string> map_;
415};
416
417} // namespace
418
419// Return the time zone id which match ICU's expectation of title casing
420// return empty string when error.
421std::string JSDateTimeFormat::CanonicalizeTimeZoneID(const std::string& input) {
422 std::string upper = input;
423 transform(upper.begin(), upper.end(), upper.begin(),
424 LocaleIndependentAsciiToUpper);
425 if (upper.length() == 3) {
426 if (upper == "GMT") return "UTC";
427 // For id such as "CET", return upper case.
428 return upper;
429 } else if (upper.length() == 7 && '0' <= upper[3] && upper[3] <= '9') {
430 // For id such as "CST6CDT", return upper case.
431 return upper;
432 } else if (upper.length() > 3) {
433 if (memcmp(upper.c_str(), "ETC", 3) == 0) {
434 if (upper == "ETC/UTC" || upper == "ETC/GMT" || upper == "ETC/UCT") {
435 return "UTC";
436 }
437 if (strncmp(upper.c_str(), "ETC/GMT", 7) == 0) {
438 return GetGMTTzID(input);
439 }
440 } else if (memcmp(upper.c_str(), "GMT", 3) == 0) {
441 if (upper == "GMT0" || upper == "GMT+0" || upper == "GMT-0") {
442 return "UTC";
443 }
444 } else if (memcmp(upper.c_str(), "US/", 3) == 0) {
445 std::string title = ToTitleCaseTimezoneLocation(input);
446 if (title.length() >= 2) {
447 // Change "Us/" to "US/"
448 title[1] = 'S';
449 }
450 return title;
451 } else if (strncmp(upper.c_str(), "SYSTEMV/", 8) == 0) {
452 upper.replace(0, 8, "SystemV/");
453 return upper;
454 }
455 }
456 // We expect only _, '-' and / beside ASCII letters.
457
458 static base::LazyInstance<SpecialTimeZoneMap>::type special_time_zone_map =
459 LAZY_INSTANCE_INITIALIZER{ { 0 }, { {} } };
460
461 std::string special_case = special_time_zone_map.Pointer()->Find(upper);
462 if (!special_case.empty()) {
463 return special_case;
464 }
465 return ToTitleCaseTimezoneLocation(input);
466}
467
468namespace {
469Handle<String> DateTimeStyleAsString(Isolate* isolate,
470 JSDateTimeFormat::DateTimeStyle style) {
471 switch (style) {
472 case JSDateTimeFormat::DateTimeStyle::kFull:
473 return ReadOnlyRoots(isolate).full_string_handle();
474 case JSDateTimeFormat::DateTimeStyle::kLong:
475 return ReadOnlyRoots(isolate).long_string_handle();
476 case JSDateTimeFormat::DateTimeStyle::kMedium:
477 return ReadOnlyRoots(isolate).medium_string_handle();
478 case JSDateTimeFormat::DateTimeStyle::kShort:
479 return ReadOnlyRoots(isolate).short_string_handle();
480 case JSDateTimeFormat::DateTimeStyle::kUndefined:
481 UNREACHABLE()V8_Fatal("unreachable code");
482 }
483}
484
485int FractionalSecondDigitsFromPattern(const std::string& pattern) {
486 int result = 0;
487 for (size_t i = 0; i < pattern.length() && result < 3; i++) {
488 if (pattern[i] == 'S') {
489 result++;
490 }
491 }
492 return result;
493}
494
495} // namespace
496
497Handle<Object> JSDateTimeFormat::TimeZoneId(Isolate* isolate,
498 const icu::TimeZone& tz) {
499 Factory* factory = isolate->factory();
500 icu::UnicodeString time_zone;
501 tz.getID(time_zone);
502 UErrorCode status = U_ZERO_ERROR;
503 icu::UnicodeString canonical_time_zone;
504 icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
505 Handle<Object> timezone_value;
506 if (U_SUCCESS(status)) {
507 // In CLDR (http://unicode.org/cldr/trac/ticket/9943), Etc/UTC is made
508 // a separate timezone ID from Etc/GMT even though they're still the same
509 // timezone. We have Etc/UTC because 'UTC', 'Etc/Universal',
510 // 'Etc/Zulu' and others are turned to 'Etc/UTC' by ICU. Etc/GMT comes
511 // from Etc/GMT0, Etc/GMT+0, Etc/GMT-0, Etc/Greenwich.
512 // ecma402#sec-canonicalizetimezonename step 3
513 if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/UTC")icu::UnicodeString(true, u"Etc/UTC", -1) ||
514 canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")icu::UnicodeString(true, u"Etc/GMT", -1)) {
515 timezone_value = factory->UTC_string();
516 } else {
517 ASSIGN_RETURN_ON_EXCEPTION_VALUE(do { if (!(Intl::ToString(isolate, canonical_time_zone)).ToHandle
(&timezone_value)) { ((void) 0); return Handle<Object>
(); } } while (false)
518 isolate, timezone_value, Intl::ToString(isolate, canonical_time_zone),do { if (!(Intl::ToString(isolate, canonical_time_zone)).ToHandle
(&timezone_value)) { ((void) 0); return Handle<Object>
(); } } while (false)
519 Handle<Object>())do { if (!(Intl::ToString(isolate, canonical_time_zone)).ToHandle
(&timezone_value)) { ((void) 0); return Handle<Object>
(); } } while (false)
;
520 }
521 } else {
522 // Somehow on Windows we will reach here.
523 timezone_value = factory->undefined_value();
524 }
525 return timezone_value;
526}
527
528namespace {
529Handle<String> GetCalendar(Isolate* isolate,
530 const icu::SimpleDateFormat& simple_date_format,
531 bool is_alt_calendar = false) {
532 // getType() returns legacy calendar type name instead of LDML/BCP47 calendar
533 // key values. intl.js maps them to BCP47 values for key "ca".
534 // TODO(jshin): Consider doing it here, instead.
535 std::string calendar_str = simple_date_format.getCalendar()->getType();
536
537 // Maps ICU calendar names to LDML/BCP47 types for key 'ca'.
538 // See typeMap section in third_party/icu/source/data/misc/keyTypeData.txt
539 // and
540 // http://www.unicode.org/repos/cldr/tags/latest/common/bcp47/calendar.xml
541 if (calendar_str == "gregorian") {
542 if (is_alt_calendar) {
543 calendar_str = "iso8601";
544 } else {
545 calendar_str = "gregory";
546 }
547 } else if (calendar_str == "ethiopic-amete-alem") {
548 calendar_str = "ethioaa";
549 } else if (calendar_str == "islamic") {
550 if (is_alt_calendar) {
551 calendar_str = "islamic-rgsa";
552 }
553 }
554 return isolate->factory()->NewStringFromAsciiChecked(calendar_str.c_str());
555}
556
557Handle<Object> GetTimeZone(Isolate* isolate,
558 const icu::SimpleDateFormat& simple_date_format) {
559 return JSDateTimeFormat::TimeZoneId(
560 isolate, simple_date_format.getCalendar()->getTimeZone());
561}
562} // namespace
563
564Handle<String> JSDateTimeFormat::Calendar(
565 Isolate* isolate, Handle<JSDateTimeFormat> date_time_format) {
566 return GetCalendar(isolate,
567 *(date_time_format->icu_simple_date_format().raw()),
568 date_time_format->alt_calendar());
569}
570
571Handle<Object> JSDateTimeFormat::TimeZone(
572 Isolate* isolate, Handle<JSDateTimeFormat> date_time_format) {
573 return GetTimeZone(isolate,
574 *(date_time_format->icu_simple_date_format().raw()));
575}
576
577// ecma402 #sec-intl.datetimeformat.prototype.resolvedoptions
578MaybeHandle<JSObject> JSDateTimeFormat::ResolvedOptions(
579 Isolate* isolate, Handle<JSDateTimeFormat> date_time_format) {
580 Factory* factory = isolate->factory();
581 // 4. Let options be ! ObjectCreate(%ObjectPrototype%).
582 Handle<JSObject> options = factory->NewJSObject(isolate->object_function());
583
584 Handle<Object> resolved_obj;
585
586 Handle<String> locale = Handle<String>(date_time_format->locale(), isolate);
587 DCHECK(!date_time_format->icu_locale().is_null())((void) 0);
588 DCHECK_NOT_NULL(date_time_format->icu_locale().raw())((void) 0);
589 icu::Locale* icu_locale = date_time_format->icu_locale().raw();
590
591 icu::SimpleDateFormat* icu_simple_date_format =
592 date_time_format->icu_simple_date_format().raw();
593 Handle<Object> timezone =
594 JSDateTimeFormat::TimeZone(isolate, date_time_format);
595
596 // Ugly hack. ICU doesn't expose numbering system in any way, so we have
597 // to assume that for given locale NumberingSystem constructor produces the
598 // same digits as NumberFormat/Calendar would.
599 // Tracked by https://unicode-org.atlassian.net/browse/ICU-13431
600 std::string numbering_system = Intl::GetNumberingSystem(*icu_locale);
601
602 icu::UnicodeString pattern_unicode;
603 icu_simple_date_format->toPattern(pattern_unicode);
604 std::string pattern;
605 pattern_unicode.toUTF8String(pattern);
606
607 // 5. For each row of Table 6, except the header row, in table order, do
608 // Table 6: Resolved Options of DateTimeFormat Instances
609 // Internal Slot Property
610 // [[Locale]] "locale"
611 // [[Calendar]] "calendar"
612 // [[NumberingSystem]] "numberingSystem"
613 // [[TimeZone]] "timeZone"
614 // [[HourCycle]] "hourCycle"
615 // "hour12"
616 // [[Weekday]] "weekday"
617 // [[Era]] "era"
618 // [[Year]] "year"
619 // [[Month]] "month"
620 // [[Day]] "day"
621 // [[Hour]] "hour"
622 // [[Minute]] "minute"
623 // [[Second]] "second"
624 // [[FractionalSecondDigits]] "fractionalSecondDigits"
625 // [[TimeZoneName]] "timeZoneName"
626 Maybe<bool> maybe_create_locale = JSReceiver::CreateDataProperty(
627 isolate, options, factory->locale_string(), locale, Just(kDontThrow));
628 DCHECK(maybe_create_locale.FromJust())((void) 0);
629 USE(maybe_create_locale)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe_create_locale
}; (void)unused_tmp_array_for_use_macro; } while (false)
;
630
631 Handle<String> calendar =
632 JSDateTimeFormat::Calendar(isolate, date_time_format);
633 Maybe<bool> maybe_create_calendar = JSReceiver::CreateDataProperty(
634 isolate, options, factory->calendar_string(), calendar, Just(kDontThrow));
635 DCHECK(maybe_create_calendar.FromJust())((void) 0);
636 USE(maybe_create_calendar)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe_create_calendar
}; (void)unused_tmp_array_for_use_macro; } while (false)
;
637
638 if (!numbering_system.empty()) {
639 Maybe<bool> maybe_create_numbering_system = JSReceiver::CreateDataProperty(
640 isolate, options, factory->numberingSystem_string(),
641 factory->NewStringFromAsciiChecked(numbering_system.c_str()),
642 Just(kDontThrow));
643 DCHECK(maybe_create_numbering_system.FromJust())((void) 0);
644 USE(maybe_create_numbering_system)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe_create_numbering_system
}; (void)unused_tmp_array_for_use_macro; } while (false)
;
645 }
646 Maybe<bool> maybe_create_time_zone = JSReceiver::CreateDataProperty(
647 isolate, options, factory->timeZone_string(), timezone, Just(kDontThrow));
648 DCHECK(maybe_create_time_zone.FromJust())((void) 0);
649 USE(maybe_create_time_zone)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe_create_time_zone
}; (void)unused_tmp_array_for_use_macro; } while (false)
;
650
651 // 5.b.i. Let hc be dtf.[[HourCycle]].
652 HourCycle hc = date_time_format->hour_cycle();
653
654 if (hc != HourCycle::kUndefined) {
655 Maybe<bool> maybe_create_hour_cycle = JSReceiver::CreateDataProperty(
656 isolate, options, factory->hourCycle_string(),
657 date_time_format->HourCycleAsString(), Just(kDontThrow));
658 DCHECK(maybe_create_hour_cycle.FromJust())((void) 0);
659 USE(maybe_create_hour_cycle)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe_create_hour_cycle
}; (void)unused_tmp_array_for_use_macro; } while (false)
;
660 switch (hc) {
661 // ii. If hc is "h11" or "h12", let v be true.
662 case HourCycle::kH11:
663 case HourCycle::kH12: {
664 Maybe<bool> maybe_create_hour12 = JSReceiver::CreateDataProperty(
665 isolate, options, factory->hour12_string(), factory->true_value(),
666 Just(kDontThrow));
667 DCHECK(maybe_create_hour12.FromJust())((void) 0);
668 USE(maybe_create_hour12)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe_create_hour12
}; (void)unused_tmp_array_for_use_macro; } while (false)
;
669 } break;
670 // iii. Else if, hc is "h23" or "h24", let v be false.
671 case HourCycle::kH23:
672 case HourCycle::kH24: {
673 Maybe<bool> maybe_create_hour12 = JSReceiver::CreateDataProperty(
674 isolate, options, factory->hour12_string(), factory->false_value(),
675 Just(kDontThrow));
676 DCHECK(maybe_create_hour12.FromJust())((void) 0);
677 USE(maybe_create_hour12)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe_create_hour12
}; (void)unused_tmp_array_for_use_macro; } while (false)
;
678 } break;
679 // iv. Else, let v be undefined.
680 case HourCycle::kUndefined:
681 break;
682 }
683 }
684
685 // If dateStyle and timeStyle are undefined, then internal slots
686 // listed in "Table 1: Components of date and time formats" will be set
687 // in Step 33.f.iii.1 of InitializeDateTimeFormat
688 if (date_time_format->date_style() == DateTimeStyle::kUndefined &&
689 date_time_format->time_style() == DateTimeStyle::kUndefined) {
690 for (const auto& item : GetPatternItems()) {
691 // fractionalSecondsDigits need to be added before timeZoneName
692 if (item.property == "timeZoneName") {
693 int fsd = FractionalSecondDigitsFromPattern(pattern);
694 if (fsd > 0) {
695 Maybe<bool> maybe_create_fractional_seconds_digits =
696 JSReceiver::CreateDataProperty(
697 isolate, options, factory->fractionalSecondDigits_string(),
698 factory->NewNumberFromInt(fsd), Just(kDontThrow));
699 DCHECK(maybe_create_fractional_seconds_digits.FromJust())((void) 0);
700 USE(maybe_create_fractional_seconds_digits)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe_create_fractional_seconds_digits
}; (void)unused_tmp_array_for_use_macro; } while (false)
;
701 }
702 }
703 for (const auto& pair : item.pairs) {
704 if (pattern.find(pair.pattern) != std::string::npos) {
705 Maybe<bool> maybe_create_property = JSReceiver::CreateDataProperty(
706 isolate, options,
707 factory->NewStringFromAsciiChecked(item.property.c_str()),
708 factory->NewStringFromAsciiChecked(pair.value.c_str()),
709 Just(kDontThrow));
710 DCHECK(maybe_create_property.FromJust())((void) 0);
711 USE(maybe_create_property)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe_create_property
}; (void)unused_tmp_array_for_use_macro; } while (false)
;
712 break;
713 }
714 }
715 }
716 }
717
718 // dateStyle
719 if (date_time_format->date_style() != DateTimeStyle::kUndefined) {
720 Maybe<bool> maybe_create_date_style = JSReceiver::CreateDataProperty(
721 isolate, options, factory->dateStyle_string(),
722 DateTimeStyleAsString(isolate, date_time_format->date_style()),
723 Just(kDontThrow));
724 DCHECK(maybe_create_date_style.FromJust())((void) 0);
725 USE(maybe_create_date_style)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe_create_date_style
}; (void)unused_tmp_array_for_use_macro; } while (false)
;
726 }
727
728 // timeStyle
729 if (date_time_format->time_style() != DateTimeStyle::kUndefined) {
730 Maybe<bool> maybe_create_time_style = JSReceiver::CreateDataProperty(
731 isolate, options, factory->timeStyle_string(),
732 DateTimeStyleAsString(isolate, date_time_format->time_style()),
733 Just(kDontThrow));
734 DCHECK(maybe_create_time_style.FromJust())((void) 0);
735 USE(maybe_create_time_style)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{maybe_create_time_style
}; (void)unused_tmp_array_for_use_macro; } while (false)
;
736 }
737 return options;
738}
739
740namespace {
741
742// ecma402/#sec-formatdatetime
743// FormatDateTime( dateTimeFormat, x )
744MaybeHandle<String> FormatDateTime(Isolate* isolate,
745 const icu::SimpleDateFormat& date_format,
746 double x) {
747 double date_value = DateCache::TimeClip(x);
748 if (std::isnan(date_value)) {
749 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<String>(__isolate__->factory()->NewRangeError
(MessageTemplate::kInvalidTimeValue)); } while (false)
750 String)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<String>(__isolate__->factory()->NewRangeError
(MessageTemplate::kInvalidTimeValue)); } while (false)
;
751 }
752
753 icu::UnicodeString result;
754 date_format.format(date_value, result);
755
756 return Intl::ToString(isolate, result);
757}
758
759} // namespace
760
761// ecma402/#sec-datetime-format-functions
762// DateTime Format Functions
763MaybeHandle<String> JSDateTimeFormat::DateTimeFormat(
764 Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
765 Handle<Object> date) {
766 // 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]]
767 // internal slot.
768
769 // 3. If date is not provided or is undefined, then
770 double x;
771 if (date->IsUndefined()) {
772 // 3.a Let x be Call(%Date_now%, undefined).
773 x = JSDate::CurrentTimeValue(isolate);
774 } else {
775 // 4. Else,
776 // a. Let x be ? ToNumber(date).
777 ASSIGN_RETURN_ON_EXCEPTION(isolate, date, Object::ToNumber(isolate, date),do { if (!(Object::ToNumber(isolate, date)).ToHandle(&date
)) { ((void) 0); return MaybeHandle<String>(); } } while
(false)
778 String)do { if (!(Object::ToNumber(isolate, date)).ToHandle(&date
)) { ((void) 0); return MaybeHandle<String>(); } } while
(false)
;
779 DCHECK(date->IsNumber())((void) 0);
780 x = date->Number();
781 }
782 // 5. Return FormatDateTime(dtf, x).
783 icu::SimpleDateFormat* format =
784 date_time_format->icu_simple_date_format().raw();
785 return FormatDateTime(isolate, *format, x);
786}
787
788namespace {
789Isolate::ICUObjectCacheType ConvertToCacheType(
790 JSDateTimeFormat::DefaultsOption type) {
791 switch (type) {
792 case JSDateTimeFormat::DefaultsOption::kDate:
793 return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormatForDate;
794 case JSDateTimeFormat::DefaultsOption::kTime:
795 return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormatForTime;
796 case JSDateTimeFormat::DefaultsOption::kAll:
797 return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormat;
798 }
799}
800} // namespace
801
802MaybeHandle<String> JSDateTimeFormat::ToLocaleDateTime(
803 Isolate* isolate, Handle<Object> date, Handle<Object> locales,
804 Handle<Object> options, RequiredOption required, DefaultsOption defaults,
805 const char* method_name) {
806 Isolate::ICUObjectCacheType cache_type = ConvertToCacheType(defaults);
807
808 Factory* factory = isolate->factory();
809 // 1. Let x be ? thisTimeValue(this value);
810 if (!date->IsJSDate()) {
811 THROW_NEW_ERROR(isolate,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<String>(__isolate__->factory()->NewTypeError
(MessageTemplate::kMethodInvokedOnWrongType, factory->Date_string
())); } while (false)
812 NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<String>(__isolate__->factory()->NewTypeError
(MessageTemplate::kMethodInvokedOnWrongType, factory->Date_string
())); } while (false)
813 factory->Date_string()),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<String>(__isolate__->factory()->NewTypeError
(MessageTemplate::kMethodInvokedOnWrongType, factory->Date_string
())); } while (false)
814 String)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<String>(__isolate__->factory()->NewTypeError
(MessageTemplate::kMethodInvokedOnWrongType, factory->Date_string
())); } while (false)
;
815 }
816
817 double const x = Handle<JSDate>::cast(date)->value().Number();
818 // 2. If x is NaN, return "Invalid Date"
819 if (std::isnan(x)) {
820 return factory->Invalid_Date_string();
821 }
822
823 // We only cache the instance when locales is a string/undefined and
824 // options is undefined, as that is the only case when the specified
825 // side-effects of examining those arguments are unobservable.
826 bool can_cache = (locales->IsString() || locales->IsUndefined(isolate)) &&
827 options->IsUndefined(isolate);
828 if (can_cache) {
829 // Both locales and options are undefined, check the cache.
830 icu::SimpleDateFormat* cached_icu_simple_date_format =
831 static_cast<icu::SimpleDateFormat*>(
832 isolate->get_cached_icu_object(cache_type, locales));
833 if (cached_icu_simple_date_format != nullptr) {
834 return FormatDateTime(isolate, *cached_icu_simple_date_format, x);
835 }
836 }
837 // 3. Let options be ? ToDateTimeOptions(options, required, defaults).
838 Handle<JSObject> internal_options;
839 ASSIGN_RETURN_ON_EXCEPTION(do { if (!(ToDateTimeOptions(isolate, options, required, defaults
)).ToHandle(&internal_options)) { ((void) 0); return MaybeHandle
<String>(); } } while (false)
840 isolate, internal_options,do { if (!(ToDateTimeOptions(isolate, options, required, defaults
)).ToHandle(&internal_options)) { ((void) 0); return MaybeHandle
<String>(); } } while (false)
841 ToDateTimeOptions(isolate, options, required, defaults), String)do { if (!(ToDateTimeOptions(isolate, options, required, defaults
)).ToHandle(&internal_options)) { ((void) 0); return MaybeHandle
<String>(); } } while (false)
;
842
843 // 4. Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »).
844 Handle<JSFunction> constructor = Handle<JSFunction>(
845 JSFunction::cast(
846 isolate->context().native_context().intl_date_time_format_function()),
847 isolate);
848 Handle<Map> map;
849 ASSIGN_RETURN_ON_EXCEPTION(do { if (!(JSFunction::GetDerivedMap(isolate, constructor, constructor
)).ToHandle(&map)) { ((void) 0); return MaybeHandle<String
>(); } } while (false)
850 isolate, map,do { if (!(JSFunction::GetDerivedMap(isolate, constructor, constructor
)).ToHandle(&map)) { ((void) 0); return MaybeHandle<String
>(); } } while (false)
851 JSFunction::GetDerivedMap(isolate, constructor, constructor), String)do { if (!(JSFunction::GetDerivedMap(isolate, constructor, constructor
)).ToHandle(&map)) { ((void) 0); return MaybeHandle<String
>(); } } while (false)
;
852 Handle<JSDateTimeFormat> date_time_format;
853 ASSIGN_RETURN_ON_EXCEPTION(do { if (!(JSDateTimeFormat::New(isolate, map, locales, internal_options
, method_name)).ToHandle(&date_time_format)) { ((void) 0)
; return MaybeHandle<String>(); } } while (false)
854 isolate, date_time_format,do { if (!(JSDateTimeFormat::New(isolate, map, locales, internal_options
, method_name)).ToHandle(&date_time_format)) { ((void) 0)
; return MaybeHandle<String>(); } } while (false)
855 JSDateTimeFormat::New(isolate, map, locales, internal_options,do { if (!(JSDateTimeFormat::New(isolate, map, locales, internal_options
, method_name)).ToHandle(&date_time_format)) { ((void) 0)
; return MaybeHandle<String>(); } } while (false)
856 method_name),do { if (!(JSDateTimeFormat::New(isolate, map, locales, internal_options
, method_name)).ToHandle(&date_time_format)) { ((void) 0)
; return MaybeHandle<String>(); } } while (false)
857 String)do { if (!(JSDateTimeFormat::New(isolate, map, locales, internal_options
, method_name)).ToHandle(&date_time_format)) { ((void) 0)
; return MaybeHandle<String>(); } } while (false)
;
858
859 if (can_cache) {
860 isolate->set_icu_object_in_cache(
861 cache_type, locales,
862 std::static_pointer_cast<icu::UMemory>(
863 date_time_format->icu_simple_date_format().get()));
864 }
865 // 5. Return FormatDateTime(dateFormat, x).
866 icu::SimpleDateFormat* format =
867 date_time_format->icu_simple_date_format().raw();
868 return FormatDateTime(isolate, *format, x);
869}
870
871namespace {
872
873Maybe<bool> IsPropertyUndefined(Isolate* isolate, Handle<JSObject> options,
874 Handle<String> property) {
875 // i. Let prop be the property name.
876 // ii. Let value be ? Get(options, prop).
877 Handle<Object> value;
878 ASSIGN_RETURN_ON_EXCEPTION_VALUE(do { if (!(Object::GetPropertyOrElement(isolate, options, property
)).ToHandle(&value)) { ((void) 0); return Nothing<bool
>(); } } while (false)
879 isolate, value, Object::GetPropertyOrElement(isolate, options, property),do { if (!(Object::GetPropertyOrElement(isolate, options, property
)).ToHandle(&value)) { ((void) 0); return Nothing<bool
>(); } } while (false)
880 Nothing<bool>())do { if (!(Object::GetPropertyOrElement(isolate, options, property
)).ToHandle(&value)) { ((void) 0); return Nothing<bool
>(); } } while (false)
;
881 return Just(value->IsUndefined(isolate));
882}
883
884Maybe<bool> NeedsDefault(Isolate* isolate, Handle<JSObject> options,
885 const std::vector<Handle<String>>& props) {
886 bool needs_default = true;
887 for (const auto& prop : props) {
888 // i. Let prop be the property name.
889 // ii. Let value be ? Get(options, prop)
890 Maybe<bool> maybe_undefined = IsPropertyUndefined(isolate, options, prop);
891 MAYBE_RETURN(maybe_undefined, Nothing<bool>())do { if ((maybe_undefined).IsNothing()) return Nothing<bool
>(); } while (false)
;
892 // iii. If value is not undefined, let needDefaults be false.
893 if (!maybe_undefined.FromJust()) {
894 needs_default = false;
895 }
896 }
897 return Just(needs_default);
898}
899
900Maybe<bool> CreateDefault(Isolate* isolate, Handle<JSObject> options,
901 const std::vector<std::string>& props) {
902 Factory* factory = isolate->factory();
903 // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric").
904 for (const auto& prop : props) {
905 MAYBE_RETURN(do { if ((JSReceiver::CreateDataProperty( isolate, options, factory
->NewStringFromAsciiChecked(prop.c_str()), factory->numeric_string
(), Just(kThrowOnError))).IsNothing()) return Nothing<bool
>(); } while (false)
906 JSReceiver::CreateDataProperty(do { if ((JSReceiver::CreateDataProperty( isolate, options, factory
->NewStringFromAsciiChecked(prop.c_str()), factory->numeric_string
(), Just(kThrowOnError))).IsNothing()) return Nothing<bool
>(); } while (false)
907 isolate, options, factory->NewStringFromAsciiChecked(prop.c_str()),do { if ((JSReceiver::CreateDataProperty( isolate, options, factory
->NewStringFromAsciiChecked(prop.c_str()), factory->numeric_string
(), Just(kThrowOnError))).IsNothing()) return Nothing<bool
>(); } while (false)
908 factory->numeric_string(), Just(kThrowOnError)),do { if ((JSReceiver::CreateDataProperty( isolate, options, factory
->NewStringFromAsciiChecked(prop.c_str()), factory->numeric_string
(), Just(kThrowOnError))).IsNothing()) return Nothing<bool
>(); } while (false)
909 Nothing<bool>())do { if ((JSReceiver::CreateDataProperty( isolate, options, factory
->NewStringFromAsciiChecked(prop.c_str()), factory->numeric_string
(), Just(kThrowOnError))).IsNothing()) return Nothing<bool
>(); } while (false)
;
910 }
911 return Just(true);
912}
913
914} // namespace
915
916// ecma-402/#sec-todatetimeoptions
917MaybeHandle<JSObject> JSDateTimeFormat::ToDateTimeOptions(
918 Isolate* isolate, Handle<Object> input_options, RequiredOption required,
919 DefaultsOption defaults) {
920 Factory* factory = isolate->factory();
921 // 1. If options is undefined, let options be null; otherwise let options be ?
922 // ToObject(options).
923 Handle<JSObject> options;
924 if (input_options->IsUndefined(isolate)) {
925 options = factory->NewJSObjectWithNullProto();
926 } else {
927 Handle<JSReceiver> options_obj;
928 ASSIGN_RETURN_ON_EXCEPTION(isolate, options_obj,do { if (!(Object::ToObject(isolate, input_options)).ToHandle
(&options_obj)) { ((void) 0); return MaybeHandle<JSObject
>(); } } while (false)
929 Object::ToObject(isolate, input_options),do { if (!(Object::ToObject(isolate, input_options)).ToHandle
(&options_obj)) { ((void) 0); return MaybeHandle<JSObject
>(); } } while (false)
930 JSObject)do { if (!(Object::ToObject(isolate, input_options)).ToHandle
(&options_obj)) { ((void) 0); return MaybeHandle<JSObject
>(); } } while (false)
;
931 // 2. Let options be ObjectCreate(options).
932 ASSIGN_RETURN_ON_EXCEPTION(isolate, options,do { if (!(JSObject::ObjectCreate(isolate, options_obj)).ToHandle
(&options)) { ((void) 0); return MaybeHandle<JSObject>
(); } } while (false)
933 JSObject::ObjectCreate(isolate, options_obj),do { if (!(JSObject::ObjectCreate(isolate, options_obj)).ToHandle
(&options)) { ((void) 0); return MaybeHandle<JSObject>
(); } } while (false)
934 JSObject)do { if (!(JSObject::ObjectCreate(isolate, options_obj)).ToHandle
(&options)) { ((void) 0); return MaybeHandle<JSObject>
(); } } while (false)
;
935 }
936
937 // 3. Let needDefaults be true.
938 bool needs_default = true;
939
940 // 4. If required is "date" or "any", then
941 if (required == RequiredOption::kAny || required == RequiredOption::kDate) {
942 // a. For each of the property names "weekday", "year", "month",
943 // "day", do
944 std::vector<Handle<String>> list(
945 {factory->weekday_string(), factory->year_string()});
946 list.push_back(factory->month_string());
947 list.push_back(factory->day_string());
948 Maybe<bool> maybe_needs_default = NeedsDefault(isolate, options, list);
949 MAYBE_RETURN(maybe_needs_default, Handle<JSObject>())do { if ((maybe_needs_default).IsNothing()) return Handle<
JSObject>(); } while (false)
;
950 needs_default = maybe_needs_default.FromJust();
951 }
952
953 // 5. If required is "time" or "any", then
954 if (required == RequiredOption::kAny || required == RequiredOption::kTime) {
955 // a. For each of the property names "dayPeriod", "hour", "minute",
956 // "second", "fractionalSecondDigits", do
957 std::vector<Handle<String>> list;
958 list.push_back(factory->dayPeriod_string());
959 list.push_back(factory->hour_string());
960 list.push_back(factory->minute_string());
961 list.push_back(factory->second_string());
962 list.push_back(factory->fractionalSecondDigits_string());
963 Maybe<bool> maybe_needs_default = NeedsDefault(isolate, options, list);
964 MAYBE_RETURN(maybe_needs_default, Handle<JSObject>())do { if ((maybe_needs_default).IsNothing()) return Handle<
JSObject>(); } while (false)
;
965 needs_default &= maybe_needs_default.FromJust();
966 }
967
968 // 6. Let dateStyle be ? Get(options, "dateStyle").
969 Maybe<bool> maybe_datestyle_undefined =
970 IsPropertyUndefined(isolate, options, factory->dateStyle_string());
971 MAYBE_RETURN(maybe_datestyle_undefined, Handle<JSObject>())do { if ((maybe_datestyle_undefined).IsNothing()) return Handle
<JSObject>(); } while (false)
;
972 // 7. Let timeStyle be ? Get(options, "timeStyle").
973 Maybe<bool> maybe_timestyle_undefined =
974 IsPropertyUndefined(isolate, options, factory->timeStyle_string());
975 MAYBE_RETURN(maybe_timestyle_undefined, Handle<JSObject>())do { if ((maybe_timestyle_undefined).IsNothing()) return Handle
<JSObject>(); } while (false)
;
976 // 8. If dateStyle is not undefined or timeStyle is not undefined, let
977 // needDefaults be false.
978 if (!maybe_datestyle_undefined.FromJust() ||
979 !maybe_timestyle_undefined.FromJust()) {
980 needs_default = false;
981 }
982 // 9. If required is "date" and timeStyle is not undefined,
983 if (required == RequiredOption::kDate &&
984 !maybe_timestyle_undefined.FromJust()) {
985 // a. Throw a TypeError exception.
986 THROW_NEW_ERROR(do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSObject>(__isolate__->factory()->NewTypeError
(MessageTemplate::kInvalid, factory->NewStringFromStaticChars
("option"), factory->NewStringFromStaticChars("timeStyle")
)); } while (false)
987 isolate,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSObject>(__isolate__->factory()->NewTypeError
(MessageTemplate::kInvalid, factory->NewStringFromStaticChars
("option"), factory->NewStringFromStaticChars("timeStyle")
)); } while (false)
988 NewTypeError(MessageTemplate::kInvalid,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSObject>(__isolate__->factory()->NewTypeError
(MessageTemplate::kInvalid, factory->NewStringFromStaticChars
("option"), factory->NewStringFromStaticChars("timeStyle")
)); } while (false)
989 factory->NewStringFromStaticChars("option"),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSObject>(__isolate__->factory()->NewTypeError
(MessageTemplate::kInvalid, factory->NewStringFromStaticChars
("option"), factory->NewStringFromStaticChars("timeStyle")
)); } while (false)
990 factory->NewStringFromStaticChars("timeStyle")),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSObject>(__isolate__->factory()->NewTypeError
(MessageTemplate::kInvalid, factory->NewStringFromStaticChars
("option"), factory->NewStringFromStaticChars("timeStyle")
)); } while (false)
991 JSObject)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSObject>(__isolate__->factory()->NewTypeError
(MessageTemplate::kInvalid, factory->NewStringFromStaticChars
("option"), factory->NewStringFromStaticChars("timeStyle")
)); } while (false)
;
992 }
993 // 10. If required is "time" and dateStyle is not undefined,
994 if (required == RequiredOption::kTime &&
995 !maybe_datestyle_undefined.FromJust()) {
996 // a. Throw a TypeError exception.
997 THROW_NEW_ERROR(do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSObject>(__isolate__->factory()->NewTypeError
(MessageTemplate::kInvalid, factory->NewStringFromStaticChars
("option"), factory->NewStringFromStaticChars("dateStyle")
)); } while (false)
998 isolate,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSObject>(__isolate__->factory()->NewTypeError
(MessageTemplate::kInvalid, factory->NewStringFromStaticChars
("option"), factory->NewStringFromStaticChars("dateStyle")
)); } while (false)
999 NewTypeError(MessageTemplate::kInvalid,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSObject>(__isolate__->factory()->NewTypeError
(MessageTemplate::kInvalid, factory->NewStringFromStaticChars
("option"), factory->NewStringFromStaticChars("dateStyle")
)); } while (false)
1000 factory->NewStringFromStaticChars("option"),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSObject>(__isolate__->factory()->NewTypeError
(MessageTemplate::kInvalid, factory->NewStringFromStaticChars
("option"), factory->NewStringFromStaticChars("dateStyle")
)); } while (false)
1001 factory->NewStringFromStaticChars("dateStyle")),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSObject>(__isolate__->factory()->NewTypeError
(MessageTemplate::kInvalid, factory->NewStringFromStaticChars
("option"), factory->NewStringFromStaticChars("dateStyle")
)); } while (false)
1002 JSObject)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSObject>(__isolate__->factory()->NewTypeError
(MessageTemplate::kInvalid, factory->NewStringFromStaticChars
("option"), factory->NewStringFromStaticChars("dateStyle")
)); } while (false)
;
1003 }
1004
1005 // 11. If needDefaults is true and defaults is either "date" or "all", then
1006 if (needs_default) {
1007 if (defaults == DefaultsOption::kAll || defaults == DefaultsOption::kDate) {
1008 // a. For each of the property names "year", "month", "day", do)
1009 const std::vector<std::string> list({"year", "month", "day"});
1010 MAYBE_RETURN(CreateDefault(isolate, options, list), Handle<JSObject>())do { if ((CreateDefault(isolate, options, list)).IsNothing())
return Handle<JSObject>(); } while (false)
;
1011 }
1012 // 12. If needDefaults is true and defaults is either "time" or "all", then
1013 if (defaults == DefaultsOption::kAll || defaults == DefaultsOption::kTime) {
1014 // a. For each of the property names "hour", "minute", "second", do
1015 const std::vector<std::string> list({"hour", "minute", "second"});
1016 MAYBE_RETURN(CreateDefault(isolate, options, list), Handle<JSObject>())do { if ((CreateDefault(isolate, options, list)).IsNothing())
return Handle<JSObject>(); } while (false)
;
1017 }
1018 }
1019 // 13. Return options.
1020 return options;
1021}
1022
1023MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::UnwrapDateTimeFormat(
1024 Isolate* isolate, Handle<JSReceiver> format_holder) {
1025 Handle<Context> native_context =
1026 Handle<Context>(isolate->context().native_context(), isolate);
1027 Handle<JSFunction> constructor = Handle<JSFunction>(
1028 JSFunction::cast(native_context->intl_date_time_format_function()),
1029 isolate);
1030 Handle<Object> dtf;
1031 ASSIGN_RETURN_ON_EXCEPTION(do { if (!(Intl::LegacyUnwrapReceiver(isolate, format_holder,
constructor, format_holder->IsJSDateTimeFormat())).ToHandle
(&dtf)) { ((void) 0); return MaybeHandle<JSDateTimeFormat
>(); } } while (false)
1032 isolate, dtf,do { if (!(Intl::LegacyUnwrapReceiver(isolate, format_holder,
constructor, format_holder->IsJSDateTimeFormat())).ToHandle
(&dtf)) { ((void) 0); return MaybeHandle<JSDateTimeFormat
>(); } } while (false)
1033 Intl::LegacyUnwrapReceiver(isolate, format_holder, constructor,do { if (!(Intl::LegacyUnwrapReceiver(isolate, format_holder,
constructor, format_holder->IsJSDateTimeFormat())).ToHandle
(&dtf)) { ((void) 0); return MaybeHandle<JSDateTimeFormat
>(); } } while (false)
1034 format_holder->IsJSDateTimeFormat()),do { if (!(Intl::LegacyUnwrapReceiver(isolate, format_holder,
constructor, format_holder->IsJSDateTimeFormat())).ToHandle
(&dtf)) { ((void) 0); return MaybeHandle<JSDateTimeFormat
>(); } } while (false)
1035 JSDateTimeFormat)do { if (!(Intl::LegacyUnwrapReceiver(isolate, format_holder,
constructor, format_holder->IsJSDateTimeFormat())).ToHandle
(&dtf)) { ((void) 0); return MaybeHandle<JSDateTimeFormat
>(); } } while (false)
;
1036 // 2. If Type(dtf) is not Object or dtf does not have an
1037 // [[InitializedDateTimeFormat]] internal slot, then
1038 if (!dtf->IsJSDateTimeFormat()) {
1039 // a. Throw a TypeError exception.
1040 THROW_NEW_ERROR(isolate,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, isolate
->factory()->NewStringFromAsciiChecked( "UnwrapDateTimeFormat"
), format_holder)); } while (false)
1041 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, isolate
->factory()->NewStringFromAsciiChecked( "UnwrapDateTimeFormat"
), format_holder)); } while (false)
1042 isolate->factory()->NewStringFromAsciiChecked(do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, isolate
->factory()->NewStringFromAsciiChecked( "UnwrapDateTimeFormat"
), format_holder)); } while (false)
1043 "UnwrapDateTimeFormat"),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, isolate
->factory()->NewStringFromAsciiChecked( "UnwrapDateTimeFormat"
), format_holder)); } while (false)
1044 format_holder),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, isolate
->factory()->NewStringFromAsciiChecked( "UnwrapDateTimeFormat"
), format_holder)); } while (false)
1045 JSDateTimeFormat)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, isolate
->factory()->NewStringFromAsciiChecked( "UnwrapDateTimeFormat"
), format_holder)); } while (false)
;
1046 }
1047 // 3. Return dtf.
1048 return Handle<JSDateTimeFormat>::cast(dtf);
1049}
1050
1051std::unique_ptr<icu::TimeZone> JSDateTimeFormat::CreateTimeZone(
1052 const char* timezone) {
1053 // Create time zone as specified by the user. We have to re-create time zone
1054 // since calendar takes ownership.
1055 if (timezone == nullptr) {
1056 // 19.a. Else / Let timeZone be DefaultTimeZone().
1057 return std::unique_ptr<icu::TimeZone>(icu::TimeZone::createDefault());
1058 }
1059 std::string canonicalized = CanonicalizeTimeZoneID(timezone);
1060 if (canonicalized.empty()) return std::unique_ptr<icu::TimeZone>();
1061 std::unique_ptr<icu::TimeZone> tz(
1062 icu::TimeZone::createTimeZone(canonicalized.c_str()));
1063 // 18.b If the result of IsValidTimeZoneName(timeZone) is false, then
1064 // i. Throw a RangeError exception.
1065 if (!Intl::IsValidTimeZoneName(*tz)) return std::unique_ptr<icu::TimeZone>();
1066 return tz;
1067}
1068
1069namespace {
1070
1071class CalendarCache {
1072 public:
1073 icu::Calendar* CreateCalendar(const icu::Locale& locale, icu::TimeZone* tz) {
1074 icu::UnicodeString tz_id;
1075 tz->getID(tz_id);
1076 std::string key;
1077 tz_id.toUTF8String<std::string>(key);
1078 key += ":";
1079 key += locale.getName();
1080
1081 base::MutexGuard guard(&mutex_);
1082 auto it = map_.find(key);
1083 if (it != map_.end()) {
1084 delete tz;
1085 return it->second->clone();
1086 }
1087 // Create a calendar using locale, and apply time zone to it.
1088 UErrorCode status = U_ZERO_ERROR;
1089 std::unique_ptr<icu::Calendar> calendar(
1090 icu::Calendar::createInstance(tz, locale, status));
1091 DCHECK(U_SUCCESS(status))((void) 0);
1092 DCHECK_NOT_NULL(calendar.get())((void) 0);
1093
1094 if (calendar->getDynamicClassID() ==
1095 icu::GregorianCalendar::getStaticClassID()) {
1096 icu::GregorianCalendar* gc =
1097 static_cast<icu::GregorianCalendar*>(calendar.get());
1098 status = U_ZERO_ERROR;
1099 // The beginning of ECMAScript time, namely -(2**53)
1100 const double start_of_time = -9007199254740992;
1101 gc->setGregorianChange(start_of_time, status);
1102 DCHECK(U_SUCCESS(status))((void) 0);
1103 }
1104
1105 if (map_.size() > 8) { // Cache at most 8 calendars.
1106 map_.clear();
1107 }
1108 map_[key].reset(calendar.release());
1109 return map_[key]->clone();
1110 }
1111
1112 private:
1113 std::map<std::string, std::unique_ptr<icu::Calendar>> map_;
1114 base::Mutex mutex_;
1115};
1116
1117icu::Calendar* CreateCalendar(Isolate* isolate, const icu::Locale& icu_locale,
1118 icu::TimeZone* tz) {
1119 static base::LazyInstance<CalendarCache>::type calendar_cache =
1120 LAZY_INSTANCE_INITIALIZER{ { 0 }, { {} } };
1121 return calendar_cache.Pointer()->CreateCalendar(icu_locale, tz);
1122}
1123
1124icu::UnicodeString ReplaceHourCycleInPattern(icu::UnicodeString pattern,
1125 JSDateTimeFormat::HourCycle hc) {
1126 char16_t replacement;
1127 switch (hc) {
1128 case JSDateTimeFormat::HourCycle::kUndefined:
1129 return pattern;
1130 case JSDateTimeFormat::HourCycle::kH11:
1131 replacement = 'K';
1132 break;
1133 case JSDateTimeFormat::HourCycle::kH12:
1134 replacement = 'h';
1135 break;
1136 case JSDateTimeFormat::HourCycle::kH23:
1137 replacement = 'H';
1138 break;
1139 case JSDateTimeFormat::HourCycle::kH24:
1140 replacement = 'k';
1141 break;
1142 }
1143 bool replace = true;
1144 icu::UnicodeString result;
1145 char16_t last = u'\0';
1146 for (int32_t i = 0; i < pattern.length(); i++) {
1147 char16_t ch = pattern.charAt(i);
1148 switch (ch) {
1149 case '\'':
1150 replace = !replace;
1151 result.append(ch);
1152 break;
1153 case 'H':
1154 V8_FALLTHROUGH[[clang::fallthrough]];
1155 case 'h':
1156 V8_FALLTHROUGH[[clang::fallthrough]];
1157 case 'K':
1158 V8_FALLTHROUGH[[clang::fallthrough]];
1159 case 'k':
1160 // If the previous field is a day, add a space before the hour.
1161 if (replace && last == u'd') {
1162 result.append(' ');
1163 }
1164 result.append(replace ? replacement : ch);
1165 break;
1166 default:
1167 result.append(ch);
1168 break;
1169 }
1170 last = ch;
1171 }
1172 return result;
1173}
1174
1175std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormat(
1176 const icu::Locale& icu_locale, const icu::UnicodeString& skeleton,
1177 icu::DateTimePatternGenerator* generator, JSDateTimeFormat::HourCycle hc) {
1178 // See https://github.com/tc39/ecma402/issues/225 . The best pattern
1179 // generation needs to be done in the base locale according to the
1180 // current spec however odd it may be. See also crbug.com/826549 .
1181 // This is a temporary work-around to get v8's external behavior to match
1182 // the current spec, but does not follow the spec provisions mentioned
1183 // in the above Ecma 402 issue.
1184 // TODO(jshin): The spec may need to be revised because using the base
1185 // locale for the pattern match is not quite right. Moreover, what to
1186 // do with 'related year' part when 'chinese/dangi' calendar is specified
1187 // has to be discussed. Revisit once the spec is clarified/revised.
1188 icu::UnicodeString pattern;
1189 UErrorCode status = U_ZERO_ERROR;
1190 pattern = generator->getBestPattern(skeleton, UDATPG_MATCH_HOUR_FIELD_LENGTH,
1191 status);
1192 pattern = ReplaceHourCycleInPattern(pattern, hc);
1193 DCHECK(U_SUCCESS(status))((void) 0);
1194
1195 // Make formatter from skeleton. Calendar and numbering system are added
1196 // to the locale as Unicode extension (if they were specified at all).
1197 status = U_ZERO_ERROR;
1198 std::unique_ptr<icu::SimpleDateFormat> date_format(
1199 new icu::SimpleDateFormat(pattern, icu_locale, status));
1200 if (U_FAILURE(status)) return std::unique_ptr<icu::SimpleDateFormat>();
1201
1202 DCHECK_NOT_NULL(date_format.get())((void) 0);
1203 return date_format;
1204}
1205
1206class DateFormatCache {
1207 public:
1208 icu::SimpleDateFormat* Create(const icu::Locale& icu_locale,
1209 const icu::UnicodeString& skeleton,
1210 icu::DateTimePatternGenerator* generator,
1211 JSDateTimeFormat::HourCycle hc) {
1212 std::string key;
1213 skeleton.toUTF8String<std::string>(key);
1214 key += ":";
1215 key += icu_locale.getName();
1216
1217 base::MutexGuard guard(&mutex_);
1218 auto it = map_.find(key);
1219 if (it != map_.end()) {
1220 return static_cast<icu::SimpleDateFormat*>(it->second->clone());
1221 }
1222
1223 if (map_.size() > 8) { // Cache at most 8 DateFormats.
1224 map_.clear();
1225 }
1226 std::unique_ptr<icu::SimpleDateFormat> instance(
1227 CreateICUDateFormat(icu_locale, skeleton, generator, hc));
1228 if (instance.get() == nullptr) return nullptr;
1229 map_[key] = std::move(instance);
1230 return static_cast<icu::SimpleDateFormat*>(map_[key]->clone());
1231 }
1232
1233 private:
1234 std::map<std::string, std::unique_ptr<icu::SimpleDateFormat>> map_;
1235 base::Mutex mutex_;
1236};
1237
1238std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormatFromCache(
1239 const icu::Locale& icu_locale, const icu::UnicodeString& skeleton,
1240 icu::DateTimePatternGenerator* generator, JSDateTimeFormat::HourCycle hc) {
1241 static base::LazyInstance<DateFormatCache>::type cache =
1242 LAZY_INSTANCE_INITIALIZER{ { 0 }, { {} } };
1243 return std::unique_ptr<icu::SimpleDateFormat>(
1244 cache.Pointer()->Create(icu_locale, skeleton, generator, hc));
1245}
1246
1247icu::UnicodeString SkeletonFromDateFormat(
1248 const icu::SimpleDateFormat& icu_date_format) {
1249 icu::UnicodeString pattern;
1250 pattern = icu_date_format.toPattern(pattern);
1251
1252 UErrorCode status = U_ZERO_ERROR;
1253 icu::UnicodeString skeleton =
1254 icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
1255 DCHECK(U_SUCCESS(status))((void) 0);
1256 return skeleton;
1257}
1258
1259icu::DateIntervalFormat* LazyCreateDateIntervalFormat(
1260 Isolate* isolate, Handle<JSDateTimeFormat> date_time_format) {
1261 Managed<icu::DateIntervalFormat> managed_format =
1262 date_time_format->icu_date_interval_format();
1263 if (managed_format.get()) {
1264 return managed_format.raw();
1265 }
1266 icu::SimpleDateFormat* icu_simple_date_format =
1267 date_time_format->icu_simple_date_format().raw();
1268 UErrorCode status = U_ZERO_ERROR;
1269
1270 icu::Locale loc = *(date_time_format->icu_locale().raw());
1271 // We need to pass in the hc to DateIntervalFormat by using Unicode 'hc'
1272 // extension.
1273 std::string hcString = ToHourCycleString(date_time_format->hour_cycle());
1274 if (!hcString.empty()) {
1275 loc.setUnicodeKeywordValue("hc", hcString, status);
1276 }
1277
1278 std::unique_ptr<icu::DateIntervalFormat> date_interval_format(
1279 icu::DateIntervalFormat::createInstance(
1280 SkeletonFromDateFormat(*icu_simple_date_format), loc, status));
1281 if (U_FAILURE(status)) {
1282 return nullptr;
1283 }
1284 date_interval_format->setTimeZone(icu_simple_date_format->getTimeZone());
1285 Handle<Managed<icu::DateIntervalFormat>> managed_interval_format =
1286 Managed<icu::DateIntervalFormat>::FromUniquePtr(
1287 isolate, 0, std::move(date_interval_format));
1288 date_time_format->set_icu_date_interval_format(*managed_interval_format);
1289 return (*managed_interval_format).raw();
1290}
1291
1292JSDateTimeFormat::HourCycle HourCycleFromPattern(
1293 const icu::UnicodeString pattern) {
1294 bool in_quote = false;
1295 for (int32_t i = 0; i < pattern.length(); i++) {
1296 char16_t ch = pattern[i];
1297 switch (ch) {
1298 case '\'':
1299 in_quote = !in_quote;
1300 break;
1301 case 'K':
1302 if (!in_quote) return JSDateTimeFormat::HourCycle::kH11;
1303 break;
1304 case 'h':
1305 if (!in_quote) return JSDateTimeFormat::HourCycle::kH12;
1306 break;
1307 case 'H':
1308 if (!in_quote) return JSDateTimeFormat::HourCycle::kH23;
1309 break;
1310 case 'k':
1311 if (!in_quote) return JSDateTimeFormat::HourCycle::kH24;
1312 break;
1313 }
1314 }
1315 return JSDateTimeFormat::HourCycle::kUndefined;
1316}
1317
1318icu::DateFormat::EStyle DateTimeStyleToEStyle(
1319 JSDateTimeFormat::DateTimeStyle style) {
1320 switch (style) {
1321 case JSDateTimeFormat::DateTimeStyle::kFull:
1322 return icu::DateFormat::EStyle::kFull;
1323 case JSDateTimeFormat::DateTimeStyle::kLong:
1324 return icu::DateFormat::EStyle::kLong;
1325 case JSDateTimeFormat::DateTimeStyle::kMedium:
1326 return icu::DateFormat::EStyle::kMedium;
1327 case JSDateTimeFormat::DateTimeStyle::kShort:
1328 return icu::DateFormat::EStyle::kShort;
1329 case JSDateTimeFormat::DateTimeStyle::kUndefined:
1330 UNREACHABLE()V8_Fatal("unreachable code");
1331 }
1332}
1333
1334icu::UnicodeString ReplaceSkeleton(const icu::UnicodeString input,
1335 JSDateTimeFormat::HourCycle hc) {
1336 icu::UnicodeString result;
1337 char16_t to;
1338 switch (hc) {
1339 case JSDateTimeFormat::HourCycle::kH11:
1340 to = 'K';
1341 break;
1342 case JSDateTimeFormat::HourCycle::kH12:
1343 to = 'h';
1344 break;
1345 case JSDateTimeFormat::HourCycle::kH23:
1346 to = 'H';
1347 break;
1348 case JSDateTimeFormat::HourCycle::kH24:
1349 to = 'k';
1350 break;
1351 case JSDateTimeFormat::HourCycle::kUndefined:
1352 UNREACHABLE()V8_Fatal("unreachable code");
1353 }
1354 for (int32_t i = 0; i < input.length(); i++) {
1355 switch (input[i]) {
1356 // We need to skip 'a', 'b', 'B' here due to
1357 // https://unicode-org.atlassian.net/browse/ICU-20437
1358 case 'a':
1359 V8_FALLTHROUGH[[clang::fallthrough]];
1360 case 'b':
1361 V8_FALLTHROUGH[[clang::fallthrough]];
1362 case 'B':
1363 // ignore
1364 break;
1365 case 'h':
1366 V8_FALLTHROUGH[[clang::fallthrough]];
1367 case 'H':
1368 V8_FALLTHROUGH[[clang::fallthrough]];
1369 case 'K':
1370 V8_FALLTHROUGH[[clang::fallthrough]];
1371 case 'k':
1372 result += to;
1373 break;
1374 default:
1375 result += input[i];
1376 break;
1377 }
1378 }
1379 return result;
1380}
1381
1382std::unique_ptr<icu::SimpleDateFormat> DateTimeStylePattern(
1383 JSDateTimeFormat::DateTimeStyle date_style,
1384 JSDateTimeFormat::DateTimeStyle time_style, icu::Locale& icu_locale,
1385 JSDateTimeFormat::HourCycle hc, icu::DateTimePatternGenerator* generator) {
1386 std::unique_ptr<icu::SimpleDateFormat> result;
1387 if (date_style != JSDateTimeFormat::DateTimeStyle::kUndefined) {
1388 if (time_style != JSDateTimeFormat::DateTimeStyle::kUndefined) {
1389 result.reset(reinterpret_cast<icu::SimpleDateFormat*>(
1390 icu::DateFormat::createDateTimeInstance(
1391 DateTimeStyleToEStyle(date_style),
1392 DateTimeStyleToEStyle(time_style), icu_locale)));
1393 } else {
1394 result.reset(reinterpret_cast<icu::SimpleDateFormat*>(
1395 icu::DateFormat::createDateInstance(DateTimeStyleToEStyle(date_style),
1396 icu_locale)));
1397 // For instance without time, we do not need to worry about the hour cycle
1398 // impact so we can return directly.
1399 if (result.get() != nullptr) {
1400 return result;
1401 }
1402 }
1403 } else {
1404 if (time_style != JSDateTimeFormat::DateTimeStyle::kUndefined) {
1405 result.reset(reinterpret_cast<icu::SimpleDateFormat*>(
1406 icu::DateFormat::createTimeInstance(DateTimeStyleToEStyle(time_style),
1407 icu_locale)));
1408 } else {
1409 UNREACHABLE()V8_Fatal("unreachable code");
1410 }
1411 }
1412
1413 UErrorCode status = U_ZERO_ERROR;
1414 // Somehow we fail to create the instance.
1415 if (result.get() == nullptr) {
1416 // Fallback to the locale without "nu".
1417 if (!icu_locale.getUnicodeKeywordValue<std::string>("nu", status).empty()) {
1418 status = U_ZERO_ERROR;
1419 icu_locale.setUnicodeKeywordValue("nu", nullptr, status);
1420 return DateTimeStylePattern(date_style, time_style, icu_locale, hc,
1421 generator);
1422 }
1423 status = U_ZERO_ERROR;
1424 // Fallback to the locale without "hc".
1425 if (!icu_locale.getUnicodeKeywordValue<std::string>("hc", status).empty()) {
1426 status = U_ZERO_ERROR;
1427 icu_locale.setUnicodeKeywordValue("hc", nullptr, status);
1428 return DateTimeStylePattern(date_style, time_style, icu_locale, hc,
1429 generator);
1430 }
1431 status = U_ZERO_ERROR;
1432 // Fallback to the locale without "ca".
1433 if (!icu_locale.getUnicodeKeywordValue<std::string>("ca", status).empty()) {
1434 status = U_ZERO_ERROR;
1435 icu_locale.setUnicodeKeywordValue("ca", nullptr, status);
1436 return DateTimeStylePattern(date_style, time_style, icu_locale, hc,
1437 generator);
1438 }
1439 return nullptr;
1440 }
1441 icu::UnicodeString pattern;
1442 pattern = result->toPattern(pattern);
1443
1444 status = U_ZERO_ERROR;
1445 icu::UnicodeString skeleton =
1446 icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
1447 DCHECK(U_SUCCESS(status))((void) 0);
1448
1449 // If the skeleton match the HourCycle, we just return it.
1450 if (hc == HourCycleFromPattern(pattern)) {
1451 return result;
1452 }
1453
1454 return CreateICUDateFormatFromCache(icu_locale, ReplaceSkeleton(skeleton, hc),
1455 generator, hc);
1456}
1457
1458class DateTimePatternGeneratorCache {
1459 public:
1460 // Return a clone copy that the caller have to free.
1461 icu::DateTimePatternGenerator* CreateGenerator(Isolate* isolate,
1462 const icu::Locale& locale) {
1463 std::string key(locale.getName());
1464 base::MutexGuard guard(&mutex_);
1465 auto it = map_.find(key);
1466 icu::DateTimePatternGenerator* orig;
1467 if (it != map_.end()) {
1468 DCHECK(it->second != nullptr)((void) 0);
1469 orig = it->second.get();
1470 } else {
1471 UErrorCode status = U_ZERO_ERROR;
1472 orig = icu::DateTimePatternGenerator::createInstance(locale, status);
1473 // It may not be an U_MEMORY_ALLOCATION_ERROR.
1474 // Fallback to use "root".
1475 if (U_FAILURE(status)) {
1476 status = U_ZERO_ERROR;
1477 orig = icu::DateTimePatternGenerator::createInstance("root", status);
1478 }
1479 if (U_SUCCESS(status) && orig != nullptr) {
1480 map_[key].reset(orig);
1481 } else {
1482 DCHECK(status == U_MEMORY_ALLOCATION_ERROR)((void) 0);
1483 V8::FatalProcessOutOfMemory(
1484 isolate, "DateTimePatternGeneratorCache::CreateGenerator");
1485 }
1486 }
1487 icu::DateTimePatternGenerator* clone = orig ? orig->clone() : nullptr;
1488 if (clone == nullptr) {
1489 V8::FatalProcessOutOfMemory(
1490 isolate, "DateTimePatternGeneratorCache::CreateGenerator");
1491 }
1492 return clone;
1493 }
1494
1495 private:
1496 std::map<std::string, std::unique_ptr<icu::DateTimePatternGenerator>> map_;
1497 base::Mutex mutex_;
1498};
1499
1500} // namespace
1501
1502enum FormatMatcherOption { kBestFit, kBasic };
1503
1504// ecma402/#sec-initializedatetimeformat
1505MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::New(
1506 Isolate* isolate, Handle<Map> map, Handle<Object> locales,
1507 Handle<Object> input_options, const char* service) {
1508 Factory* factory = isolate->factory();
1509 // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
1510 Maybe<std::vector<std::string>> maybe_requested_locales =
1511 Intl::CanonicalizeLocaleList(isolate, locales);
1512 MAYBE_RETURN(maybe_requested_locales, Handle<JSDateTimeFormat>())do { if ((maybe_requested_locales).IsNothing()) return Handle
<JSDateTimeFormat>(); } while (false)
;
1513 std::vector<std::string> requested_locales =
1514 maybe_requested_locales.FromJust();
1515 // 2. Let options be ? ToDateTimeOptions(options, "any", "date").
1516 Handle<JSObject> options;
1517 ASSIGN_RETURN_ON_EXCEPTION(do { if (!(JSDateTimeFormat::ToDateTimeOptions( isolate, input_options
, RequiredOption::kAny, DefaultsOption::kDate)).ToHandle(&
options)) { ((void) 0); return MaybeHandle<JSDateTimeFormat
>(); } } while (false)
1518 isolate, options,do { if (!(JSDateTimeFormat::ToDateTimeOptions( isolate, input_options
, RequiredOption::kAny, DefaultsOption::kDate)).ToHandle(&
options)) { ((void) 0); return MaybeHandle<JSDateTimeFormat
>(); } } while (false)
1519 JSDateTimeFormat::ToDateTimeOptions(do { if (!(JSDateTimeFormat::ToDateTimeOptions( isolate, input_options
, RequiredOption::kAny, DefaultsOption::kDate)).ToHandle(&
options)) { ((void) 0); return MaybeHandle<JSDateTimeFormat
>(); } } while (false)
1520 isolate, input_options, RequiredOption::kAny, DefaultsOption::kDate),do { if (!(JSDateTimeFormat::ToDateTimeOptions( isolate, input_options
, RequiredOption::kAny, DefaultsOption::kDate)).ToHandle(&
options)) { ((void) 0); return MaybeHandle<JSDateTimeFormat
>(); } } while (false)
1521 JSDateTimeFormat)do { if (!(JSDateTimeFormat::ToDateTimeOptions( isolate, input_options
, RequiredOption::kAny, DefaultsOption::kDate)).ToHandle(&
options)) { ((void) 0); return MaybeHandle<JSDateTimeFormat
>(); } } while (false)
;
1522
1523 // 4. Let matcher be ? GetOption(options, "localeMatcher", "string",
1524 // « "lookup", "best fit" », "best fit").
1525 // 5. Set opt.[[localeMatcher]] to matcher.
1526 Maybe<Intl::MatcherOption> maybe_locale_matcher =
1527 Intl::GetLocaleMatcher(isolate, options, service);
1528 MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSDateTimeFormat>())do { if ((maybe_locale_matcher).IsNothing()) return MaybeHandle
<JSDateTimeFormat>(); } while (false)
;
1529 Intl::MatcherOption locale_matcher = maybe_locale_matcher.FromJust();
1530
1531 std::unique_ptr<char[]> calendar_str = nullptr;
1532 std::unique_ptr<char[]> numbering_system_str = nullptr;
1533 const std::vector<const char*> empty_values = {};
1534 // 6. Let calendar be ? GetOption(options, "calendar",
1535 // "string", undefined, undefined).
1536 Maybe<bool> maybe_calendar = GetStringOption(
1537 isolate, options, "calendar", empty_values, service, &calendar_str);
1538 MAYBE_RETURN(maybe_calendar, MaybeHandle<JSDateTimeFormat>())do { if ((maybe_calendar).IsNothing()) return MaybeHandle<
JSDateTimeFormat>(); } while (false)
;
1539 if (maybe_calendar.FromJust() && calendar_str != nullptr) {
1540 icu::Locale default_locale;
1541 if (!Intl::IsWellFormedCalendar(calendar_str.get())) {
1542 THROW_NEW_ERROR(do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalid, factory->calendar_string
(), factory->NewStringFromAsciiChecked(calendar_str.get())
)); } while (false)
1543 isolate,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalid, factory->calendar_string
(), factory->NewStringFromAsciiChecked(calendar_str.get())
)); } while (false)
1544 NewRangeError(MessageTemplate::kInvalid, factory->calendar_string(),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalid, factory->calendar_string
(), factory->NewStringFromAsciiChecked(calendar_str.get())
)); } while (false)
1545 factory->NewStringFromAsciiChecked(calendar_str.get())),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalid, factory->calendar_string
(), factory->NewStringFromAsciiChecked(calendar_str.get())
)); } while (false)
1546 JSDateTimeFormat)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalid, factory->calendar_string
(), factory->NewStringFromAsciiChecked(calendar_str.get())
)); } while (false)
;
1547 }
1548 }
1549
1550 // 8. Let numberingSystem be ? GetOption(options, "numberingSystem",
1551 // "string", undefined, undefined).
1552 Maybe<bool> maybe_numberingSystem = Intl::GetNumberingSystem(
1553 isolate, options, service, &numbering_system_str);
1554 MAYBE_RETURN(maybe_numberingSystem, MaybeHandle<JSDateTimeFormat>())do { if ((maybe_numberingSystem).IsNothing()) return MaybeHandle
<JSDateTimeFormat>(); } while (false)
;
1555
1556 // 6. Let hour12 be ? GetOption(options, "hour12", "boolean", undefined,
1557 // undefined).
1558 bool hour12;
1559 Maybe<bool> maybe_get_hour12 =
1560 GetBoolOption(isolate, options, "hour12", service, &hour12);
1561 MAYBE_RETURN(maybe_get_hour12, Handle<JSDateTimeFormat>())do { if ((maybe_get_hour12).IsNothing()) return Handle<JSDateTimeFormat
>(); } while (false)
;
1562
1563 // 7. Let hourCycle be ? GetOption(options, "hourCycle", "string", « "h11",
1564 // "h12", "h23", "h24" », undefined).
1565 Maybe<HourCycle> maybe_hour_cycle = GetHourCycle(isolate, options, service);
1566 MAYBE_RETURN(maybe_hour_cycle, MaybeHandle<JSDateTimeFormat>())do { if ((maybe_hour_cycle).IsNothing()) return MaybeHandle<
JSDateTimeFormat>(); } while (false)
;
1567 HourCycle hour_cycle = maybe_hour_cycle.FromJust();
1568
1569 // 8. If hour12 is not undefined, then
1570 if (maybe_get_hour12.FromJust()) {
1571 // a. Let hourCycle be null.
1572 hour_cycle = HourCycle::kUndefined;
1573 }
1574 // 9. Set opt.[[hc]] to hourCycle.
1575
1576 // ecma402/#sec-intl.datetimeformat-internal-slots
1577 // The value of the [[RelevantExtensionKeys]] internal slot is
1578 // « "ca", "nu", "hc" ».
1579 std::set<std::string> relevant_extension_keys = {"nu", "ca", "hc"};
1580
1581 // 10. Let localeData be %DateTimeFormat%.[[LocaleData]].
1582 // 11. Let r be ResolveLocale( %DateTimeFormat%.[[AvailableLocales]],
1583 // requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]],
1584 // localeData).
1585 //
1586 Maybe<Intl::ResolvedLocale> maybe_resolve_locale = Intl::ResolveLocale(
1587 isolate, JSDateTimeFormat::GetAvailableLocales(), requested_locales,
1588 locale_matcher, relevant_extension_keys);
1589 if (maybe_resolve_locale.IsNothing()) {
1590 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kIcuError)); } while (false)
1591 JSDateTimeFormat)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kIcuError)); } while (false)
;
1592 }
1593 Intl::ResolvedLocale r = maybe_resolve_locale.FromJust();
1594
1595 icu::Locale icu_locale = r.icu_locale;
1596 DCHECK(!icu_locale.isBogus())((void) 0);
1597
1598 UErrorCode status = U_ZERO_ERROR;
1599 if (calendar_str != nullptr) {
1600 auto ca_extension_it = r.extensions.find("ca");
1601 if (ca_extension_it != r.extensions.end() &&
1602 ca_extension_it->second != calendar_str.get()) {
1603 icu_locale.setUnicodeKeywordValue("ca", nullptr, status);
1604 DCHECK(U_SUCCESS(status))((void) 0);
1605 }
1606 }
1607 if (numbering_system_str != nullptr) {
1608 auto nu_extension_it = r.extensions.find("nu");
1609 if (nu_extension_it != r.extensions.end() &&
1610 nu_extension_it->second != numbering_system_str.get()) {
1611 icu_locale.setUnicodeKeywordValue("nu", nullptr, status);
1612 DCHECK(U_SUCCESS(status))((void) 0);
1613 }
1614 }
1615
1616 // Need to keep a copy of icu_locale which not changing "ca", "nu", "hc"
1617 // by option.
1618 icu::Locale resolved_locale(icu_locale);
1619
1620 if (calendar_str != nullptr &&
1621 Intl::IsValidCalendar(icu_locale, calendar_str.get())) {
1622 icu_locale.setUnicodeKeywordValue("ca", calendar_str.get(), status);
1623 DCHECK(U_SUCCESS(status))((void) 0);
1624 }
1625 bool alt_calendar =
1626 strstr(icu_locale.getName(), "calendar=iso8601") != nullptr ||
1627 strstr(icu_locale.getName(), "calendar=islamic-rgsa") != nullptr;
1628
1629 if (numbering_system_str != nullptr &&
1630 Intl::IsValidNumberingSystem(numbering_system_str.get())) {
1631 icu_locale.setUnicodeKeywordValue("nu", numbering_system_str.get(), status);
1632 DCHECK(U_SUCCESS(status))((void) 0);
1633 }
1634
1635 static base::LazyInstance<DateTimePatternGeneratorCache>::type
1636 generator_cache = LAZY_INSTANCE_INITIALIZER{ { 0 }, { {} } };
1637
1638 std::unique_ptr<icu::DateTimePatternGenerator> generator(
1639 generator_cache.Pointer()->CreateGenerator(isolate, icu_locale));
1640
1641 // 15.Let hcDefault be dataLocaleData.[[hourCycle]].
1642 HourCycle hc_default = ToHourCycle(generator->getDefaultHourCycle(status));
1643 DCHECK(U_SUCCESS(status))((void) 0);
1644
1645 // 16.Let hc be r.[[hc]].
1646 HourCycle hc = HourCycle::kUndefined;
1647 if (hour_cycle == HourCycle::kUndefined) {
1648 auto hc_extension_it = r.extensions.find("hc");
1649 if (hc_extension_it != r.extensions.end()) {
1650 hc = ToHourCycle(hc_extension_it->second.c_str());
1651 }
1652 } else {
1653 hc = hour_cycle;
1654 }
1655 // 17. If hc is null, then
1656 if (hc == HourCycle::kUndefined) {
1657 // a. Set hc to hcDefault.
1658 hc = hc_default;
1659 }
1660
1661 // 18. If hour12 is not undefined, then
1662 if (maybe_get_hour12.FromJust()) {
1663 // a. If hour12 is true, then
1664 if (hour12) {
1665 // i. If hcDefault is "h11" or "h23", then
1666 if (hc_default == HourCycle::kH11 || hc_default == HourCycle::kH23) {
1667 // 1. Set hc to "h11".
1668 hc = HourCycle::kH11;
1669 // ii. Else,
1670 } else {
1671 // 1. Set hc to "h12".
1672 hc = HourCycle::kH12;
1673 }
1674 // b. Else,
1675 } else {
1676 // ii. If hcDefault is "h11" or "h23", then
1677 if (hc_default == HourCycle::kH11 || hc_default == HourCycle::kH23) {
1678 // 1. Set hc to "h23".
1679 hc = HourCycle::kH23;
1680 // iii. Else,
1681 } else {
1682 // 1. Set hc to "h24".
1683 hc = HourCycle::kH24;
1684 }
1685 }
1686 }
1687
1688 // 17. Let timeZone be ? Get(options, "timeZone").
1689 std::unique_ptr<char[]> timezone = nullptr;
1690 Maybe<bool> maybe_timezone = GetStringOption(
1691 isolate, options, "timeZone", empty_values, service, &timezone);
1692 MAYBE_RETURN(maybe_timezone, Handle<JSDateTimeFormat>())do { if ((maybe_timezone).IsNothing()) return Handle<JSDateTimeFormat
>(); } while (false)
;
1693
1694 std::unique_ptr<icu::TimeZone> tz =
1695 JSDateTimeFormat::CreateTimeZone(timezone.get());
1696 if (tz.get() == nullptr) {
1697 THROW_NEW_ERROR(do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalidTimeZone, factory->
NewStringFromAsciiChecked(timezone.get()))); } while (false)
1698 isolate,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalidTimeZone, factory->
NewStringFromAsciiChecked(timezone.get()))); } while (false)
1699 NewRangeError(MessageTemplate::kInvalidTimeZone,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalidTimeZone, factory->
NewStringFromAsciiChecked(timezone.get()))); } while (false)
1700 factory->NewStringFromAsciiChecked(timezone.get())),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalidTimeZone, factory->
NewStringFromAsciiChecked(timezone.get()))); } while (false)
1701 JSDateTimeFormat)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalidTimeZone, factory->
NewStringFromAsciiChecked(timezone.get()))); } while (false)
;
1702 }
1703
1704 std::unique_ptr<icu::Calendar> calendar(
1705 CreateCalendar(isolate, icu_locale, tz.release()));
1706
1707 // 18.b If the result of IsValidTimeZoneName(timeZone) is false, then
1708 // i. Throw a RangeError exception.
1709 if (calendar.get() == nullptr) {
1710 THROW_NEW_ERROR(do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalidTimeZone, factory->
NewStringFromAsciiChecked(timezone.get()))); } while (false)
1711 isolate,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalidTimeZone, factory->
NewStringFromAsciiChecked(timezone.get()))); } while (false)
1712 NewRangeError(MessageTemplate::kInvalidTimeZone,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalidTimeZone, factory->
NewStringFromAsciiChecked(timezone.get()))); } while (false)
1713 factory->NewStringFromAsciiChecked(timezone.get())),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalidTimeZone, factory->
NewStringFromAsciiChecked(timezone.get()))); } while (false)
1714 JSDateTimeFormat)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kInvalidTimeZone, factory->
NewStringFromAsciiChecked(timezone.get()))); } while (false)
;
1715 }
1716
1717 DateTimeStyle date_style = DateTimeStyle::kUndefined;
1718 DateTimeStyle time_style = DateTimeStyle::kUndefined;
1719 std::unique_ptr<icu::SimpleDateFormat> icu_date_format;
1720
1721 // 28. For each row of Table 1, except the header row, do
1722 bool has_hour_option = false;
1723 std::string skeleton;
1724 for (const PatternData& item : GetPatternData(hc)) {
1725 // Need to read fractionalSecondDigits before reading the timeZoneName
1726 if (item.property == "timeZoneName") {
1727 // Let _value_ be ? GetNumberOption(options, "fractionalSecondDigits", 1,
1728 // 3, *undefined*). The *undefined* is represented by value 0 here.
1729 Maybe<int> maybe_fsd = GetNumberOption(
1730 isolate, options, factory->fractionalSecondDigits_string(), 1, 3, 0);
1731 MAYBE_RETURN(maybe_fsd, MaybeHandle<JSDateTimeFormat>())do { if ((maybe_fsd).IsNothing()) return MaybeHandle<JSDateTimeFormat
>(); } while (false)
;
1732 // Convert fractionalSecondDigits to skeleton.
1733 int fsd = maybe_fsd.FromJust();
1734 for (int i = 0; i < fsd; i++) {
1735 skeleton += "S";
1736 }
1737 }
1738 std::unique_ptr<char[]> input;
1739 // i. Let prop be the name given in the Property column of the row.
1740 // ii. Let value be ? GetOption(options, prop, "string", « the strings
1741 // given in the Values column of the row », undefined).
1742 Maybe<bool> maybe_get_option =
1743 GetStringOption(isolate, options, item.property.c_str(),
1744 item.allowed_values, service, &input);
1745 MAYBE_RETURN(maybe_get_option, Handle<JSDateTimeFormat>())do { if ((maybe_get_option).IsNothing()) return Handle<JSDateTimeFormat
>(); } while (false)
;
1746 if (maybe_get_option.FromJust()) {
1747 if (item.property == "hour") {
1748 has_hour_option = true;
1749 }
1750 DCHECK_NOT_NULL(input.get())((void) 0);
1751 // iii. Set opt.[[<prop>]] to value.
1752 skeleton += item.map.find(input.get())->second;
1753 }
1754 }
1755
1756 // 29. Let matcher be ? GetOption(options, "formatMatcher", "string", «
1757 // "basic", "best fit" », "best fit").
1758 // We implement only best fit algorithm, but still need to check
1759 // if the formatMatcher values are in range.
1760 // c. Let matcher be ? GetOption(options, "formatMatcher", "string",
1761 // « "basic", "best fit" », "best fit").
1762 Maybe<FormatMatcherOption> maybe_format_matcher =
1763 GetStringOption<FormatMatcherOption>(
1764 isolate, options, "formatMatcher", service, {"best fit", "basic"},
1765 {FormatMatcherOption::kBestFit, FormatMatcherOption::kBasic},
1766 FormatMatcherOption::kBestFit);
1767 MAYBE_RETURN(maybe_format_matcher, MaybeHandle<JSDateTimeFormat>())do { if ((maybe_format_matcher).IsNothing()) return MaybeHandle
<JSDateTimeFormat>(); } while (false)
;
1768 // TODO(ftang): uncomment the following line and handle format_matcher.
1769 // FormatMatcherOption format_matcher = maybe_format_matcher.FromJust();
1770
1771 // 32. Let dateStyle be ? GetOption(options, "dateStyle", "string", «
1772 // "full", "long", "medium", "short" », undefined).
1773 Maybe<DateTimeStyle> maybe_date_style = GetStringOption<DateTimeStyle>(
1774 isolate, options, "dateStyle", service,
1775 {"full", "long", "medium", "short"},
1776 {DateTimeStyle::kFull, DateTimeStyle::kLong, DateTimeStyle::kMedium,
1777 DateTimeStyle::kShort},
1778 DateTimeStyle::kUndefined);
1779 MAYBE_RETURN(maybe_date_style, MaybeHandle<JSDateTimeFormat>())do { if ((maybe_date_style).IsNothing()) return MaybeHandle<
JSDateTimeFormat>(); } while (false)
;
1780 // 33. Set dateTimeFormat.[[DateStyle]] to dateStyle.
1781 date_style = maybe_date_style.FromJust();
1782
1783 // 34. Let timeStyle be ? GetOption(options, "timeStyle", "string", «
1784 // "full", "long", "medium", "short" »).
1785 Maybe<DateTimeStyle> maybe_time_style = GetStringOption<DateTimeStyle>(
1786 isolate, options, "timeStyle", service,
1787 {"full", "long", "medium", "short"},
1788 {DateTimeStyle::kFull, DateTimeStyle::kLong, DateTimeStyle::kMedium,
1789 DateTimeStyle::kShort},
1790 DateTimeStyle::kUndefined);
1791 MAYBE_RETURN(maybe_time_style, MaybeHandle<JSDateTimeFormat>())do { if ((maybe_time_style).IsNothing()) return MaybeHandle<
JSDateTimeFormat>(); } while (false)
;
1792
1793 // 35. Set dateTimeFormat.[[TimeStyle]] to timeStyle.
1794 time_style = maybe_time_style.FromJust();
1795
1796 // 36. If timeStyle is not undefined, then
1797 HourCycle dateTimeFormatHourCycle = HourCycle::kUndefined;
1798 if (time_style != DateTimeStyle::kUndefined) {
1799 // a. Set dateTimeFormat.[[HourCycle]] to hc.
1800 dateTimeFormatHourCycle = hc;
1801 }
1802
1803 // 37. If dateStyle or timeStyle are not undefined, then
1804 if (date_style != DateTimeStyle::kUndefined ||
1805 time_style != DateTimeStyle::kUndefined) {
1806 // a. For each row in Table 1, except the header row, do
1807 // i. Let prop be the name given in the Property column of the row.
1808 // ii. Let p be opt.[[<prop>]].
1809 // iii. If p is not undefined, then
1810 // 1. Throw a TypeError exception.
1811 if (skeleton.length() > 0) {
1812 std::string prop;
1813 for (const auto& item : GetPatternItems()) {
1814 for (const auto& pair : item.pairs) {
1815 if (skeleton.find(pair.pattern) != std::string::npos) {
1816 prop.assign(item.property);
1817 break;
1818 }
1819 }
1820 if (!prop.empty()) {
1821 break;
1822 }
1823 }
1824 if (prop.empty() && skeleton.find("S") != std::string::npos) {
1825 prop.assign("fractionalSecondDigits");
1826 }
1827 if (!prop.empty()) {
1828 THROW_NEW_ERROR(do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kCantSetOptionXWhenYIsUsed, factory
->NewStringFromAsciiChecked(prop.c_str()), date_style != DateTimeStyle
::kUndefined ? factory->dateStyle_string() : factory->timeStyle_string
())); } while (false)
1829 isolate,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kCantSetOptionXWhenYIsUsed, factory
->NewStringFromAsciiChecked(prop.c_str()), date_style != DateTimeStyle
::kUndefined ? factory->dateStyle_string() : factory->timeStyle_string
())); } while (false)
1830 NewTypeError(MessageTemplate::kCantSetOptionXWhenYIsUsed,do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kCantSetOptionXWhenYIsUsed, factory
->NewStringFromAsciiChecked(prop.c_str()), date_style != DateTimeStyle
::kUndefined ? factory->dateStyle_string() : factory->timeStyle_string
())); } while (false)
1831 factory->NewStringFromAsciiChecked(prop.c_str()),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kCantSetOptionXWhenYIsUsed, factory
->NewStringFromAsciiChecked(prop.c_str()), date_style != DateTimeStyle
::kUndefined ? factory->dateStyle_string() : factory->timeStyle_string
())); } while (false)
1832 date_style != DateTimeStyle::kUndefineddo { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kCantSetOptionXWhenYIsUsed, factory
->NewStringFromAsciiChecked(prop.c_str()), date_style != DateTimeStyle
::kUndefined ? factory->dateStyle_string() : factory->timeStyle_string
())); } while (false)
1833 ? factory->dateStyle_string()do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kCantSetOptionXWhenYIsUsed, factory
->NewStringFromAsciiChecked(prop.c_str()), date_style != DateTimeStyle
::kUndefined ? factory->dateStyle_string() : factory->timeStyle_string
())); } while (false)
1834 : factory->timeStyle_string()),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kCantSetOptionXWhenYIsUsed, factory
->NewStringFromAsciiChecked(prop.c_str()), date_style != DateTimeStyle
::kUndefined ? factory->dateStyle_string() : factory->timeStyle_string
())); } while (false)
1835 JSDateTimeFormat)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewTypeError(MessageTemplate::kCantSetOptionXWhenYIsUsed, factory
->NewStringFromAsciiChecked(prop.c_str()), date_style != DateTimeStyle
::kUndefined ? factory->dateStyle_string() : factory->timeStyle_string
())); } while (false)
;
1836 }
1837 UNREACHABLE()V8_Fatal("unreachable code");
1838 }
1839 // b. Let pattern be DateTimeStylePattern(dateStyle, timeStyle,
1840 // dataLocaleData, hc).
1841 isolate->CountUsage(
1842 v8::Isolate::UseCounterFeature::kDateTimeFormatDateTimeStyle);
1843
1844 icu_date_format =
1845 DateTimeStylePattern(date_style, time_style, icu_locale,
1846 dateTimeFormatHourCycle, generator.get());
1847 if (icu_date_format.get() == nullptr) {
1848 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kIcuError)); } while (false)
1849 JSDateTimeFormat)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kIcuError)); } while (false)
;
1850 }
1851 } else {
1852 // e. If dateTimeFormat.[[Hour]] is not undefined, then
1853 if (has_hour_option) {
1854 // v. Set dateTimeFormat.[[HourCycle]] to hc.
1855 dateTimeFormatHourCycle = hc;
1856 } else {
1857 // f. Else,
1858 // Set dateTimeFormat.[[HourCycle]] to undefined.
1859 dateTimeFormatHourCycle = HourCycle::kUndefined;
1860 }
1861 icu::UnicodeString skeleton_ustr(skeleton.c_str());
1862 icu_date_format = CreateICUDateFormatFromCache(
1863 icu_locale, skeleton_ustr, generator.get(), dateTimeFormatHourCycle);
1864 if (icu_date_format.get() == nullptr) {
1865 // Remove extensions and try again.
1866 icu_locale = icu::Locale(icu_locale.getBaseName());
1867 icu_date_format = CreateICUDateFormatFromCache(
1868 icu_locale, skeleton_ustr, generator.get(), dateTimeFormatHourCycle);
1869 if (icu_date_format.get() == nullptr) {
1870 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kIcuError)); } while (false)
1871 JSDateTimeFormat)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSDateTimeFormat>(__isolate__->factory()->
NewRangeError(MessageTemplate::kIcuError)); } while (false)
;
1872 }
1873 }
1874 }
1875
1876 // The creation of Calendar depends on timeZone so we have to put 13 after 17.
1877 // Also icu_date_format is not created until here.
1878 // 13. Set dateTimeFormat.[[Calendar]] to r.[[ca]].
1879 icu_date_format->adoptCalendar(calendar.release());
1880
1881 // 12.1.1 InitializeDateTimeFormat ( dateTimeFormat, locales, options )
1882 //
1883 // Steps 8-9 set opt.[[hc]] to value *other than undefined*
1884 // if "hour12" is set or "hourCycle" is set in the option.
1885 //
1886 // 9.2.6 ResolveLocale (... )
1887 // Step 8.h / 8.i and 8.k
1888 //
1889 // An hour12 option always overrides an hourCycle option.
1890 // Additionally hour12 and hourCycle both clear out any existing Unicode
1891 // extension key in the input locale.
1892 //
1893 // See details in https://github.com/tc39/test262/pull/2035
1894 if (maybe_get_hour12.FromJust() ||
1895 maybe_hour_cycle.FromJust() != HourCycle::kUndefined) {
1896 auto hc_extension_it = r.extensions.find("hc");
1897 if (hc_extension_it != r.extensions.end()) {
1898 if (dateTimeFormatHourCycle !=
1899 ToHourCycle(hc_extension_it->second.c_str())) {
1900 // Remove -hc- if it does not agree with what we used.
1901 status = U_ZERO_ERROR;
1902 resolved_locale.setUnicodeKeywordValue("hc", nullptr, status);
1903 DCHECK(U_SUCCESS(status))((void) 0);
1904 }
1905 }
1906 }
1907
1908 Maybe<std::string> maybe_locale_str = Intl::ToLanguageTag(resolved_locale);
1909 MAYBE_RETURN(maybe_locale_str, MaybeHandle<JSDateTimeFormat>())do { if ((maybe_locale_str).IsNothing()) return MaybeHandle<
JSDateTimeFormat>(); } while (false)
;
1910 Handle<String> locale_str = isolate->factory()->NewStringFromAsciiChecked(
1911 maybe_locale_str.FromJust().c_str());
1912
1913 Handle<Managed<icu::Locale>> managed_locale =
1914 Managed<icu::Locale>::FromRawPtr(isolate, 0, icu_locale.clone());
1915
1916 Handle<Managed<icu::SimpleDateFormat>> managed_format =
1917 Managed<icu::SimpleDateFormat>::FromUniquePtr(isolate, 0,
1918 std::move(icu_date_format));
1919
1920 Handle<Managed<icu::DateIntervalFormat>> managed_interval_format =
1921 Managed<icu::DateIntervalFormat>::FromRawPtr(isolate, 0, nullptr);
1922
1923 // Now all properties are ready, so we can allocate the result object.
1924 Handle<JSDateTimeFormat> date_time_format = Handle<JSDateTimeFormat>::cast(
1925 isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
1926 DisallowGarbageCollection no_gc;
1927 date_time_format->set_flags(0);
1928 if (date_style != DateTimeStyle::kUndefined) {
1929 date_time_format->set_date_style(date_style);
1930 }
1931 if (time_style != DateTimeStyle::kUndefined) {
1932 date_time_format->set_time_style(time_style);
1933 }
1934 date_time_format->set_hour_cycle(dateTimeFormatHourCycle);
1935 date_time_format->set_alt_calendar(alt_calendar);
1936 date_time_format->set_locale(*locale_str);
1937 date_time_format->set_icu_locale(*managed_locale);
1938 date_time_format->set_icu_simple_date_format(*managed_format);
1939 date_time_format->set_icu_date_interval_format(*managed_interval_format);
1940 return date_time_format;
1941}
1942
1943namespace {
1944
1945// The list comes from third_party/icu/source/i18n/unicode/udat.h.
1946// They're mapped to DateTimeFormat components listed at
1947// https://tc39.github.io/ecma402/#sec-datetimeformat-abstracts .
1948Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
1949 switch (field_id) {
1950 case -1:
1951 return isolate->factory()->literal_string();
1952 case UDAT_YEAR_FIELD:
1953 case UDAT_EXTENDED_YEAR_FIELD:
1954 return isolate->factory()->year_string();
1955 case UDAT_YEAR_NAME_FIELD:
1956 return isolate->factory()->yearName_string();
1957 case UDAT_MONTH_FIELD:
1958 case UDAT_STANDALONE_MONTH_FIELD:
1959 return isolate->factory()->month_string();
1960 case UDAT_DATE_FIELD:
1961 return isolate->factory()->day_string();
1962 case UDAT_HOUR_OF_DAY1_FIELD:
1963 case UDAT_HOUR_OF_DAY0_FIELD:
1964 case UDAT_HOUR1_FIELD:
1965 case UDAT_HOUR0_FIELD:
1966 return isolate->factory()->hour_string();
1967 case UDAT_MINUTE_FIELD:
1968 return isolate->factory()->minute_string();
1969 case UDAT_SECOND_FIELD:
1970 return isolate->factory()->second_string();
1971 case UDAT_DAY_OF_WEEK_FIELD:
1972 case UDAT_DOW_LOCAL_FIELD:
1973 case UDAT_STANDALONE_DAY_FIELD:
1974 return isolate->factory()->weekday_string();
1975 case UDAT_AM_PM_FIELD:
1976 case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
1977 case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
1978 return isolate->factory()->dayPeriod_string();
1979 case UDAT_TIMEZONE_FIELD:
1980 case UDAT_TIMEZONE_RFC_FIELD:
1981 case UDAT_TIMEZONE_GENERIC_FIELD:
1982 case UDAT_TIMEZONE_SPECIAL_FIELD:
1983 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
1984 case UDAT_TIMEZONE_ISO_FIELD:
1985 case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
1986 return isolate->factory()->timeZoneName_string();
1987 case UDAT_ERA_FIELD:
1988 return isolate->factory()->era_string();
1989 case UDAT_FRACTIONAL_SECOND_FIELD:
1990 return isolate->factory()->fractionalSecond_string();
1991 case UDAT_RELATED_YEAR_FIELD:
1992 return isolate->factory()->relatedYear_string();
1993
1994 case UDAT_QUARTER_FIELD:
1995 case UDAT_STANDALONE_QUARTER_FIELD:
1996 default:
1997 // Other UDAT_*_FIELD's cannot show up because there is no way to specify
1998 // them via options of Intl.DateTimeFormat.
1999 UNREACHABLE()V8_Fatal("unreachable code");
2000 }
2001}
2002
2003} // namespace
2004
2005MaybeHandle<JSArray> JSDateTimeFormat::FormatToParts(
2006 Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
2007 double date_value, bool output_source) {
2008 Factory* factory = isolate->factory();
2009 icu::SimpleDateFormat* format =
2010 date_time_format->icu_simple_date_format().raw();
2011 DCHECK_NOT_NULL(format)((void) 0);
2012
2013 icu::UnicodeString formatted;
2014 icu::FieldPositionIterator fp_iter;
2015 icu::FieldPosition fp;
2016 UErrorCode status = U_ZERO_ERROR;
2017 format->format(date_value, formatted, &fp_iter, status);
2018 if (U_FAILURE(status)) {
2019 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), JSArray)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSArray>(__isolate__->factory()->NewTypeError
(MessageTemplate::kIcuError)); } while (false)
;
2020 }
2021
2022 Handle<JSArray> result = factory->NewJSArray(0);
2023 int32_t length = formatted.length();
2024 if (length == 0) return result;
2025
2026 int index = 0;
2027 int32_t previous_end_pos = 0;
2028 Handle<String> substring;
2029 while (fp_iter.next(fp)) {
2030 int32_t begin_pos = fp.getBeginIndex();
2031 int32_t end_pos = fp.getEndIndex();
2032
2033 if (previous_end_pos < begin_pos) {
2034 ASSIGN_RETURN_ON_EXCEPTION(do { if (!(Intl::ToString(isolate, formatted, previous_end_pos
, begin_pos)).ToHandle(&substring)) { ((void) 0); return MaybeHandle
<JSArray>(); } } while (false)
2035 isolate, substring,do { if (!(Intl::ToString(isolate, formatted, previous_end_pos
, begin_pos)).ToHandle(&substring)) { ((void) 0); return MaybeHandle
<JSArray>(); } } while (false)
2036 Intl::ToString(isolate, formatted, previous_end_pos, begin_pos),do { if (!(Intl::ToString(isolate, formatted, previous_end_pos
, begin_pos)).ToHandle(&substring)) { ((void) 0); return MaybeHandle
<JSArray>(); } } while (false)
2037 JSArray)do { if (!(Intl::ToString(isolate, formatted, previous_end_pos
, begin_pos)).ToHandle(&substring)) { ((void) 0); return MaybeHandle
<JSArray>(); } } while (false)
;
2038 if (output_source) {
2039 Intl::AddElement(isolate, result, index,
2040 IcuDateFieldIdToDateType(-1, isolate), substring,
2041 isolate->factory()->source_string(),
2042 isolate->factory()->shared_string());
2043 } else {
2044 Intl::AddElement(isolate, result, index,
2045 IcuDateFieldIdToDateType(-1, isolate), substring);
2046 }
2047 ++index;
2048 }
2049 ASSIGN_RETURN_ON_EXCEPTION(do { if (!(Intl::ToString(isolate, formatted, begin_pos, end_pos
)).ToHandle(&substring)) { ((void) 0); return MaybeHandle
<JSArray>(); } } while (false)
2050 isolate, substring,do { if (!(Intl::ToString(isolate, formatted, begin_pos, end_pos
)).ToHandle(&substring)) { ((void) 0); return MaybeHandle
<JSArray>(); } } while (false)
2051 Intl::ToString(isolate, formatted, begin_pos, end_pos), JSArray)do { if (!(Intl::ToString(isolate, formatted, begin_pos, end_pos
)).ToHandle(&substring)) { ((void) 0); return MaybeHandle
<JSArray>(); } } while (false)
;
2052 if (output_source) {
2053 Intl::AddElement(isolate, result, index,
2054 IcuDateFieldIdToDateType(fp.getField(), isolate),
2055 substring, isolate->factory()->source_string(),
2056 isolate->factory()->shared_string());
2057 } else {
2058 Intl::AddElement(isolate, result, index,
2059 IcuDateFieldIdToDateType(fp.getField(), isolate),
2060 substring);
2061 }
2062 previous_end_pos = end_pos;
2063 ++index;
2064 }
2065 if (previous_end_pos < length) {
2066 ASSIGN_RETURN_ON_EXCEPTION(do { if (!(Intl::ToString(isolate, formatted, previous_end_pos
, length)).ToHandle(&substring)) { ((void) 0); return MaybeHandle
<JSArray>(); } } while (false)
2067 isolate, substring,do { if (!(Intl::ToString(isolate, formatted, previous_end_pos
, length)).ToHandle(&substring)) { ((void) 0); return MaybeHandle
<JSArray>(); } } while (false)
2068 Intl::ToString(isolate, formatted, previous_end_pos, length), JSArray)do { if (!(Intl::ToString(isolate, formatted, previous_end_pos
, length)).ToHandle(&substring)) { ((void) 0); return MaybeHandle
<JSArray>(); } } while (false)
;
2069 if (output_source) {
2070 Intl::AddElement(isolate, result, index,
2071 IcuDateFieldIdToDateType(-1, isolate), substring,
2072 isolate->factory()->source_string(),
2073 isolate->factory()->shared_string());
2074 } else {
2075 Intl::AddElement(isolate, result, index,
2076 IcuDateFieldIdToDateType(-1, isolate), substring);
2077 }
2078 }
2079 JSObject::ValidateElements(*result);
2080 return result;
2081}
2082
2083const std::set<std::string>& JSDateTimeFormat::GetAvailableLocales() {
2084 return Intl::GetAvailableLocalesForDateFormat();
2085}
2086
2087Handle<String> JSDateTimeFormat::HourCycleAsString() const {
2088 switch (hour_cycle()) {
2089 case HourCycle::kUndefined:
2090 return GetReadOnlyRoots().undefined_string_handle();
2091 case HourCycle::kH11:
2092 return GetReadOnlyRoots().h11_string_handle();
2093 case HourCycle::kH12:
2094 return GetReadOnlyRoots().h12_string_handle();
2095 case HourCycle::kH23:
2096 return GetReadOnlyRoots().h23_string_handle();
2097 case HourCycle::kH24:
2098 return GetReadOnlyRoots().h24_string_handle();
2099 default:
2100 UNREACHABLE()V8_Fatal("unreachable code");
2101 }
2102}
2103
2104namespace {
2105
2106Maybe<bool> AddPartForFormatRange(
2107 Isolate* isolate, Handle<JSArray> array, const icu::UnicodeString& string,
2108 int32_t index, int32_t field, int32_t start, int32_t end,
2109 const Intl::FormatRangeSourceTracker& tracker) {
2110 Handle<String> substring;
2111 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, substring,do { if (!(Intl::ToString(isolate, string, start, end)).ToHandle
(&substring)) { ((void) 0); return Nothing<bool>();
} } while (false)
2112 Intl::ToString(isolate, string, start, end),do { if (!(Intl::ToString(isolate, string, start, end)).ToHandle
(&substring)) { ((void) 0); return Nothing<bool>();
} } while (false)
2113 Nothing<bool>())do { if (!(Intl::ToString(isolate, string, start, end)).ToHandle
(&substring)) { ((void) 0); return Nothing<bool>();
} } while (false)
;
2114 Intl::AddElement(isolate, array, index,
2115 IcuDateFieldIdToDateType(field, isolate), substring,
2116 isolate->factory()->source_string(),
2117 Intl::SourceString(isolate, tracker.GetSource(start, end)));
2118 return Just(true);
2119}
2120
2121MaybeHandle<String> FormattedToString(Isolate* isolate,
2122 const icu::FormattedValue& formatted,
2123 bool* outputRange) {
2124 UErrorCode status = U_ZERO_ERROR;
2125 icu::UnicodeString result = formatted.toString(status);
2126 if (U_FAILURE(status)) {
2127 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), String)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<String>(__isolate__->factory()->NewTypeError
(MessageTemplate::kIcuError)); } while (false)
;
2128 }
2129 *outputRange = false;
2130 icu::ConstrainedFieldPosition cfpos;
2131 while (formatted.nextPosition(cfpos, status)) {
2132 if (cfpos.getCategory() == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
2133 *outputRange = true;
2134 break;
2135 }
2136 }
2137 return Intl::ToString(isolate, result);
2138}
2139
2140// A helper function to convert the FormattedDateInterval to a
2141// MaybeHandle<JSArray> for the implementation of formatRangeToParts.
2142MaybeHandle<JSArray> FormattedDateIntervalToJSArray(
2143 Isolate* isolate, const icu::FormattedValue& formatted, bool* outputRange) {
2144 UErrorCode status = U_ZERO_ERROR;
2145 icu::UnicodeString result = formatted.toString(status);
2146
2147 Factory* factory = isolate->factory();
2148 Handle<JSArray> array = factory->NewJSArray(0);
2149 icu::ConstrainedFieldPosition cfpos;
2150 int index = 0;
2151 int32_t previous_end_pos = 0;
2152 Intl::FormatRangeSourceTracker tracker;
2153 *outputRange = false;
2154 while (formatted.nextPosition(cfpos, status)) {
2155 int32_t category = cfpos.getCategory();
2156 int32_t field = cfpos.getField();
2157 int32_t start = cfpos.getStart();
2158 int32_t limit = cfpos.getLimit();
2159
2160 if (category == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
2161 DCHECK_LE(field, 2)((void) 0);
2162 *outputRange = true;
2163 tracker.Add(field, start, limit);
2164 } else {
2165 DCHECK(category == UFIELD_CATEGORY_DATE)((void) 0);
2166 if (start > previous_end_pos) {
2167 // Add "literal" from the previous end position to the start if
2168 // necessary.
2169 Maybe<bool> maybe_added =
2170 AddPartForFormatRange(isolate, array, result, index, -1,
2171 previous_end_pos, start, tracker);
2172 MAYBE_RETURN(maybe_added, Handle<JSArray>())do { if ((maybe_added).IsNothing()) return Handle<JSArray>
(); } while (false)
;
2173 previous_end_pos = start;
Value stored to 'previous_end_pos' is never read
2174 index++;
2175 }
2176 Maybe<bool> maybe_added = AddPartForFormatRange(
2177 isolate, array, result, index, field, start, limit, tracker);
2178 MAYBE_RETURN(maybe_added, Handle<JSArray>())do { if ((maybe_added).IsNothing()) return Handle<JSArray>
(); } while (false)
;
2179 previous_end_pos = limit;
2180 ++index;
2181 }
2182 }
2183 int32_t end = result.length();
2184 // Add "literal" in the end if necessary.
2185 if (end > previous_end_pos) {
2186 Maybe<bool> maybe_added = AddPartForFormatRange(
2187 isolate, array, result, index, -1, previous_end_pos, end, tracker);
2188 MAYBE_RETURN(maybe_added, Handle<JSArray>())do { if ((maybe_added).IsNothing()) return Handle<JSArray>
(); } while (false)
;
2189 }
2190
2191 if (U_FAILURE(status)) {
2192 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), JSArray)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<JSArray>(__isolate__->factory()->NewTypeError
(MessageTemplate::kIcuError)); } while (false)
;
2193 }
2194
2195 JSObject::ValidateElements(*array);
2196 return array;
2197}
2198
2199// The shared code between formatRange and formatRangeToParts
2200template <typename T,
2201 MaybeHandle<T> (*F)(Isolate*, const icu::FormattedValue&, bool*)>
2202MaybeHandle<T> FormatRangeCommon(Isolate* isolate,
2203 Handle<JSDateTimeFormat> date_time_format,
2204 double x, double y, bool* outputRange) {
2205 // Track newer feature formateRange and formatRangeToParts
2206 isolate->CountUsage(v8::Isolate::UseCounterFeature::kDateTimeFormatRange);
2207
2208 // #sec-partitiondatetimerangepattern
2209 // 1. Let x be TimeClip(x).
2210 x = DateCache::TimeClip(x);
2211 // 2. If x is NaN, throw a RangeError exception.
2212 if (std::isnan(x)) {
2213 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<T>(__isolate__->factory()->NewRangeError(MessageTemplate
::kInvalidTimeValue)); } while (false)
2214 T)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<T>(__isolate__->factory()->NewRangeError(MessageTemplate
::kInvalidTimeValue)); } while (false)
;
2215 }
2216 // 3. Let y be TimeClip(y).
2217 y = DateCache::TimeClip(y);
2218 // 4. If y is NaN, throw a RangeError exception.
2219 if (std::isnan(y)) {
2220 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue),do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<T>(__isolate__->factory()->NewRangeError(MessageTemplate
::kInvalidTimeValue)); } while (false)
2221 T)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<T>(__isolate__->factory()->NewRangeError(MessageTemplate
::kInvalidTimeValue)); } while (false)
;
2222 }
2223
2224 icu::DateIntervalFormat* format =
2225 LazyCreateDateIntervalFormat(isolate, date_time_format);
2226 if (format == nullptr) {
2227 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), T)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<T>(__isolate__->factory()->NewTypeError(MessageTemplate
::kIcuError)); } while (false)
;
2228 }
2229
2230 UErrorCode status = U_ZERO_ERROR;
2231
2232 icu::SimpleDateFormat* date_format =
2233 date_time_format->icu_simple_date_format().raw();
2234 const icu::Calendar* calendar = date_format->getCalendar();
2235 std::unique_ptr<icu::Calendar> c1(calendar->clone());
2236 std::unique_ptr<icu::Calendar> c2(calendar->clone());
2237 c1->setTime(x, status);
2238 c2->setTime(y, status);
2239 // We need to format by Calendar because we need the Gregorian change
2240 // adjustment already in the SimpleDateFormat to set the correct value of date
2241 // older than Oct 15, 1582.
2242 icu::FormattedDateInterval formatted =
2243 format->formatToValue(*c1, *c2, status);
2244 if (U_FAILURE(status)) {
2245 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), T)do { auto* __isolate__ = (isolate); return __isolate__->template
Throw<T>(__isolate__->factory()->NewTypeError(MessageTemplate
::kIcuError)); } while (false)
;
2246 }
2247 return F(isolate, formatted, outputRange);
2248}
2249
2250} // namespace
2251
2252MaybeHandle<String> JSDateTimeFormat::FormatRange(
2253 Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, double x,
2254 double y) {
2255 bool outputRange = true;
2256 MaybeHandle<String> ret = FormatRangeCommon<String, FormattedToString>(
2257 isolate, date_time_format, x, y, &outputRange);
2258 if (outputRange) {
2259 return ret;
2260 }
2261 return FormatDateTime(isolate,
2262 *(date_time_format->icu_simple_date_format().raw()), x);
2263}
2264
2265MaybeHandle<JSArray> JSDateTimeFormat::FormatRangeToParts(
2266 Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, double x,
2267 double y) {
2268 bool outputRange = true;
2269 MaybeHandle<JSArray> ret =
2270 FormatRangeCommon<JSArray, FormattedDateIntervalToJSArray>(
2271 isolate, date_time_format, x, y, &outputRange);
2272 if (outputRange) {
2273 return ret;
2274 }
2275 return JSDateTimeFormat::FormatToParts(isolate, date_time_format, x, true);
2276}
2277
2278} // namespace internal
2279} // namespace v8