v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
js-date-time-format.cc
Go to the documentation of this file.
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
6
7#include <algorithm>
8#include <map>
9#include <memory>
10#include <optional>
11#include <string>
12#include <utility>
13#include <vector>
14
15#include "src/base/bit-field.h"
16#include "src/date/date.h"
18#include "src/heap/factory.h"
24#include "unicode/calendar.h"
25#include "unicode/dtitvfmt.h"
26#include "unicode/dtptngen.h"
27#include "unicode/fieldpos.h"
28#include "unicode/gregocal.h"
29#include "unicode/smpdtfmt.h"
30#include "unicode/unistr.h"
31
32#ifndef V8_INTL_SUPPORT
33#error Internationalization is expected to be enabled.
34#endif // V8_INTL_SUPPORT
35
36namespace v8::internal {
37
38namespace {
39
40std::string ToHourCycleString(JSDateTimeFormat::HourCycle hc) {
41 switch (hc) {
43 return "h11";
45 return "h12";
47 return "h23";
49 return "h24";
51 return "";
52 default:
54 }
55}
56
57JSDateTimeFormat::HourCycle ToHourCycle(const std::string& hc) {
58 if (hc == "h11") return JSDateTimeFormat::HourCycle::kH11;
59 if (hc == "h12") return JSDateTimeFormat::HourCycle::kH12;
60 if (hc == "h23") return JSDateTimeFormat::HourCycle::kH23;
61 if (hc == "h24") return JSDateTimeFormat::HourCycle::kH24;
63}
64
65JSDateTimeFormat::HourCycle ToHourCycle(UDateFormatHourCycle hc) {
66 switch (hc) {
67 case UDAT_HOUR_CYCLE_11:
69 case UDAT_HOUR_CYCLE_12:
71 case UDAT_HOUR_CYCLE_23:
73 case UDAT_HOUR_CYCLE_24:
75 default:
77 }
78}
79
80// The following two functions are hack until we add necessary API to ICU
81// to get default hour cycle for 12 hours system (h11 or h12) or 24 hours system
82// (h23 or h24).
83// From timeData in third_party/icu/source/data/misc/supplementalData.txt
84// we know the preferred values are either h or H.
85// And all allowed values are also h or H except in JP K (h11) is listed before
86// h (h12).
87JSDateTimeFormat::HourCycle DefaultHourCycle12(
88 const icu::Locale& locale, JSDateTimeFormat::HourCycle defaultHourCycle) {
89 if (defaultHourCycle == JSDateTimeFormat::HourCycle::kH11 ||
90 defaultHourCycle == JSDateTimeFormat::HourCycle::kH12) {
91 return defaultHourCycle;
92 }
93 if (std::strcmp(locale.getCountry(), "JP") == 0) {
95 }
97}
98
99JSDateTimeFormat::HourCycle DefaultHourCycle24(
100 const icu::Locale& locale, JSDateTimeFormat::HourCycle defaultHourCycle) {
101 if (defaultHourCycle == JSDateTimeFormat::HourCycle::kH23 ||
102 defaultHourCycle == JSDateTimeFormat::HourCycle::kH24) {
103 return defaultHourCycle;
104 }
106}
107
108Maybe<JSDateTimeFormat::HourCycle> GetHourCycle(
109 Isolate* isolate, DirectHandle<JSReceiver> options,
110 const char* method_name) {
112 isolate, options, "hourCycle", method_name, {"h11", "h12", "h23", "h24"},
116}
117
118class PatternMap {
119 public:
120 PatternMap(std::string pattern, std::string value)
121 : pattern(std::move(pattern)), value(std::move(value)) {}
122 virtual ~PatternMap() = default;
123 std::string pattern;
124 std::string value;
125};
126
127#define BIT_FIELDS(V, _) \
128 V(Era, bool, 1, _) \
129 V(Year, bool, 1, _) \
130 V(Month, bool, 1, _) \
131 V(Weekday, bool, 1, _) \
132 V(Day, bool, 1, _) \
133 V(DayPeriod, bool, 1, _) \
134 V(Hour, bool, 1, _) \
135 V(Minute, bool, 1, _) \
136 V(Second, bool, 1, _) \
137 V(TimeZoneName, bool, 1, _) \
138 V(FractionalSecondDigits, bool, 1, _)
140#undef BIT_FIELDS
141
142class PatternItem {
143 public:
144 PatternItem(int32_t shift, const std::string property,
145 std::vector<PatternMap> pairs,
146 std::vector<const char*> allowed_values)
147 : bitShift(shift),
148 property(std::move(property)),
149 pairs(std::move(pairs)),
151 virtual ~PatternItem() = default;
152
153 int32_t bitShift;
154 const std::string property;
155 // It is important for the pattern in the pairs from longer one to shorter one
156 // if the longer one contains substring of an shorter one.
157 std::vector<PatternMap> pairs;
158 std::vector<const char*> allowed_values;
159};
160
161static std::vector<PatternItem> BuildPatternItems() {
162 const std::vector<const char*> kLongShort = {"long", "short"};
163 const std::vector<const char*> kNarrowLongShort = {"narrow", "long", "short"};
164 const std::vector<const char*> k2DigitNumeric = {"2-digit", "numeric"};
165 const std::vector<const char*> kNarrowLongShort2DigitNumeric = {
166 "narrow", "long", "short", "2-digit", "numeric"};
167 std::vector<PatternItem> items = {
168 PatternItem(Weekday::kShift, "weekday",
169 {{"EEEEE", "narrow"},
170 {"EEEE", "long"},
171 {"EEE", "short"},
172 {"ccccc", "narrow"},
173 {"cccc", "long"},
174 {"ccc", "short"}},
175 kNarrowLongShort),
176 PatternItem(Era::kShift, "era",
177 {{"GGGGG", "narrow"}, {"GGGG", "long"}, {"GGG", "short"}},
178 kNarrowLongShort),
179 PatternItem(Year::kShift, "year", {{"yy", "2-digit"}, {"y", "numeric"}},
180 k2DigitNumeric)};
181 // Sometimes we get L instead of M for month - standalone name.
182 items.push_back(PatternItem(Month::kShift, "month",
183 {{"MMMMM", "narrow"},
184 {"MMMM", "long"},
185 {"MMM", "short"},
186 {"MM", "2-digit"},
187 {"M", "numeric"},
188 {"LLLLL", "narrow"},
189 {"LLLL", "long"},
190 {"LLL", "short"},
191 {"LL", "2-digit"},
192 {"L", "numeric"}},
193 kNarrowLongShort2DigitNumeric));
194 items.push_back(PatternItem(Day::kShift, "day",
195 {{"dd", "2-digit"}, {"d", "numeric"}},
196 k2DigitNumeric));
197 items.push_back(PatternItem(DayPeriod::kShift, "dayPeriod",
198 {{"BBBBB", "narrow"},
199 {"bbbbb", "narrow"},
200 {"BBBB", "long"},
201 {"bbbb", "long"},
202 {"B", "short"},
203 {"b", "short"}},
204 kNarrowLongShort));
205 items.push_back(PatternItem(Hour::kShift, "hour",
206 {{"HH", "2-digit"},
207 {"H", "numeric"},
208 {"hh", "2-digit"},
209 {"h", "numeric"},
210 {"kk", "2-digit"},
211 {"k", "numeric"},
212 {"KK", "2-digit"},
213 {"K", "numeric"}},
214 k2DigitNumeric));
215 items.push_back(PatternItem(Minute::kShift, "minute",
216 {{"mm", "2-digit"}, {"m", "numeric"}},
217 k2DigitNumeric));
218 items.push_back(PatternItem(Second::kShift, "second",
219 {{"ss", "2-digit"}, {"s", "numeric"}},
220 k2DigitNumeric));
221
222 const std::vector<const char*> kTimezone = {"long", "short",
223 "longOffset", "shortOffset",
224 "longGeneric", "shortGeneric"};
225 items.push_back(PatternItem(TimeZoneName::kShift, "timeZoneName",
226 {{"zzzz", "long"},
227 {"z", "short"},
228 {"OOOO", "longOffset"},
229 {"O", "shortOffset"},
230 {"vvvv", "longGeneric"},
231 {"v", "shortGeneric"}},
232 kTimezone));
233 return items;
234}
235
236class PatternItems {
237 public:
238 PatternItems() : data(BuildPatternItems()) {}
239 virtual ~PatternItems() = default;
240 const std::vector<PatternItem>& Get() const { return data; }
241
242 private:
243 const std::vector<PatternItem> data;
244};
245
246static const std::vector<PatternItem>& GetPatternItems() {
249 return items.Pointer()->Get();
250}
251
252class PatternData {
253 public:
254 PatternData(int32_t shift, const std::string property,
255 std::vector<PatternMap> pairs,
256 std::vector<const char*> allowed_values)
257 : bitShift(shift),
258 property(std::move(property)),
260 for (const auto& pair : pairs) {
261 map.insert(std::make_pair(pair.value, pair.pattern));
262 }
263 }
264 virtual ~PatternData() = default;
265
267 const std::string property;
268 std::map<const std::string, const std::string> map;
269 std::vector<const char*> allowed_values;
270};
271
272const std::vector<PatternData> CreateCommonData(const PatternData& hour_data) {
273 std::vector<PatternData> build;
274 for (const PatternItem& item : GetPatternItems()) {
275 if (item.property == "hour") {
276 build.push_back(hour_data);
277 } else {
278 build.push_back(PatternData(item.bitShift, item.property, item.pairs,
279 item.allowed_values));
280 }
281 }
282 return build;
283}
284
285const std::vector<PatternData> CreateData(const char* digit2,
286 const char* numeric) {
287 return CreateCommonData(PatternData(
288 Hour::kShift, "hour", {{digit2, "2-digit"}, {numeric, "numeric"}},
289 {"2-digit", "numeric"}));
290}
291
292// According to "Date Field Symbol Table" in
293// http://userguide.icu-project.org/formatparse/datetime
294// Symbol | Meaning | Example(s)
295// h hour in am/pm (1~12) h 7
296// hh 07
297// H hour in day (0~23) H 0
298// HH 00
299// k hour in day (1~24) k 24
300// kk 24
301// K hour in am/pm (0~11) K 0
302// KK 00
303
304class Pattern {
305 public:
306 Pattern(const char* d1, const char* d2) : data(CreateData(d1, d2)) {}
307 virtual ~Pattern() = default;
308 virtual const std::vector<PatternData>& Get() const { return data; }
309
310 private:
311 std::vector<PatternData> data;
312};
313
314#define DEFFINE_TRAIT(name, d1, d2) \
315 struct name { \
316 static void Construct(void* allocated_ptr) { \
317 new (allocated_ptr) Pattern(d1, d2); \
318 } \
319 };
320DEFFINE_TRAIT(H11Trait, "KK", "K")
321DEFFINE_TRAIT(H12Trait, "hh", "h")
322DEFFINE_TRAIT(H23Trait, "HH", "H")
323DEFFINE_TRAIT(H24Trait, "kk", "k")
324DEFFINE_TRAIT(HDefaultTrait, "jj", "j")
325#undef DEFFINE_TRAIT
326
327const std::vector<PatternData>& GetPatternData(
328 JSDateTimeFormat::HourCycle hour_cycle) {
329 switch (hour_cycle) {
333 return h11.Pointer()->Get();
334 }
338 return h12.Pointer()->Get();
339 }
343 return h23.Pointer()->Get();
344 }
348 return h24.Pointer()->Get();
349 }
353 return hDefault.Pointer()->Get();
354 }
355 default:
356 UNREACHABLE();
357 }
358}
359
360std::string GetGMTTzID(const std::string& input) {
361 std::string ret = "Etc/GMT";
362 switch (input.length()) {
363 case 8:
364 if (input[7] == '0') return ret + '0';
365 break;
366 case 9:
367 if ((input[7] == '+' || input[7] == '-') &&
368 base::IsInRange(input[8], '0', '9')) {
369 return ret + input[7] + input[8];
370 }
371 break;
372 case 10:
373 if ((input[7] == '+' || input[7] == '-') && (input[8] == '1') &&
374 base::IsInRange(input[9], '0', '4')) {
375 return ret + input[7] + input[8] + input[9];
376 }
377 break;
378 }
379 return "";
380}
381
382// Locale independenty version of isalpha for ascii range. This will return
383// false if the ch is alpha but not in ascii range.
384bool IsAsciiAlpha(char ch) {
385 return base::IsInRange(ch, 'A', 'Z') || base::IsInRange(ch, 'a', 'z');
386}
387
388// Locale independent toupper for ascii range. This will not return İ (dotted I)
389// for i under Turkish locale while std::toupper may.
390char LocaleIndependentAsciiToUpper(char ch) {
391 return (base::IsInRange(ch, 'a', 'z')) ? (ch - 'a' + 'A') : ch;
392}
393
394// Locale independent tolower for ascii range.
395char LocaleIndependentAsciiToLower(char ch) {
396 return (base::IsInRange(ch, 'A', 'Z')) ? (ch - 'A' + 'a') : ch;
397}
398
399// Returns titlecased location, bueNos_airES -> Buenos_Aires
400// or ho_cHi_minH -> Ho_Chi_Minh. It is locale-agnostic and only
401// deals with ASCII only characters.
402// 'of', 'au' and 'es' are special-cased and lowercased.
403// ICU's timezone parsing is case sensitive, but ECMAScript is case insensitive
404std::string ToTitleCaseTimezoneLocation(const std::string& input) {
405 std::string title_cased;
406 int word_length = 0;
407 for (char ch : input) {
408 // Convert first char to upper case, the rest to lower case
409 if (IsAsciiAlpha(ch)) {
410 title_cased += word_length == 0 ? LocaleIndependentAsciiToUpper(ch)
411 : LocaleIndependentAsciiToLower(ch);
412 word_length++;
413 } else if (ch == '_' || ch == '-' || ch == '/') {
414 // Special case Au/Es/Of to be lower case.
415 if (word_length == 2) {
416 size_t pos = title_cased.length() - 2;
417 std::string substr = title_cased.substr(pos, 2);
418 if (substr == "Of" || substr == "Es" || substr == "Au") {
419 title_cased[pos] = LocaleIndependentAsciiToLower(title_cased[pos]);
420 }
421 }
422 title_cased += ch;
423 word_length = 0;
424 } else {
425 // Invalid input
426 return std::string();
427 }
428 }
429
430 return title_cased;
431}
432
433class SpecialTimeZoneMap {
434 public:
435 SpecialTimeZoneMap() {
436 Add("America/Argentina/ComodRivadavia");
437 Add("America/Knox_IN");
438 Add("Antarctica/DumontDUrville");
439 Add("Antarctica/McMurdo");
440 Add("Australia/ACT");
441 Add("Australia/LHI");
442 Add("Australia/NSW");
443 Add("Brazil/DeNoronha");
444 Add("Chile/EasterIsland");
445 Add("GB");
446 Add("GB-Eire");
447 Add("Mexico/BajaNorte");
448 Add("Mexico/BajaSur");
449 Add("NZ");
450 Add("NZ-CHAT");
451 Add("W-SU");
452 }
453
454 std::string Find(const std::string& id) {
455 auto it = map_.find(id);
456 if (it != map_.end()) {
457 return it->second;
458 }
459 return "";
460 }
461
462 private:
463 void Add(const char* id) {
464 std::string upper(id);
465 transform(upper.begin(), upper.end(), upper.begin(),
466 LocaleIndependentAsciiToUpper);
467 map_.insert({upper, id});
468 }
469 std::map<std::string, std::string> map_;
470};
471
472} // namespace
473
474// Return the time zone id which match ICU's expectation of title casing
475// return empty string when error.
476std::string JSDateTimeFormat::CanonicalizeTimeZoneID(const std::string& input) {
477 std::string upper = input;
478 transform(upper.begin(), upper.end(), upper.begin(),
479 LocaleIndependentAsciiToUpper);
480 if (upper.length() == 3) {
481 if (upper == "GMT") return "UTC";
482 // For id such as "CET", return upper case.
483 return upper;
484 } else if (upper.length() == 7 && '0' <= upper[3] && upper[3] <= '9') {
485 // For id such as "CST6CDT", return upper case.
486 return upper;
487 } else if (upper.length() > 3) {
488 if (memcmp(upper.c_str(), "ETC", 3) == 0) {
489 if (upper == "ETC/UTC" || upper == "ETC/GMT" || upper == "ETC/UCT") {
490 return "UTC";
491 }
492 if (strncmp(upper.c_str(), "ETC/GMT", 7) == 0) {
493 return GetGMTTzID(input);
494 }
495 } else if (memcmp(upper.c_str(), "GMT", 3) == 0) {
496 if (upper == "GMT0" || upper == "GMT+0" || upper == "GMT-0") {
497 return "UTC";
498 }
499 } else if (memcmp(upper.c_str(), "US/", 3) == 0) {
500 std::string title = ToTitleCaseTimezoneLocation(input);
501 if (title.length() >= 2) {
502 // Change "Us/" to "US/"
503 title[1] = 'S';
504 }
505 return title;
506 } else if (strncmp(upper.c_str(), "SYSTEMV/", 8) == 0) {
507 upper.replace(0, 8, "SystemV/");
508 return upper;
509 }
510 }
511 // We expect only _, '-' and / beside ASCII letters.
512
513 static base::LazyInstance<SpecialTimeZoneMap>::type special_time_zone_map =
515
516 std::string special_case = special_time_zone_map.Pointer()->Find(upper);
517 if (!special_case.empty()) {
518 return special_case;
519 }
520 return ToTitleCaseTimezoneLocation(input);
521}
522
523namespace {
524DirectHandle<String> DateTimeStyleAsString(
526 switch (style) {
528 return isolate->factory()->full_string();
530 return isolate->factory()->long_string();
532 return isolate->factory()->medium_string();
534 return isolate->factory()->short_string();
536 UNREACHABLE();
537 }
538 UNREACHABLE();
539}
540
541int FractionalSecondDigitsFromPattern(const std::string& pattern) {
542 int result = 0;
543 for (size_t i = 0; i < pattern.length() && result < 3; i++) {
544 if (pattern[i] == 'S') {
545 result++;
546 }
547 }
548 return result;
549}
550} // namespace
551
553 Isolate* isolate, const icu::UnicodeString& id) {
554 // In CLDR (http://unicode.org/cldr/trac/ticket/9943), Etc/UTC is made
555 // a separate timezone ID from Etc/GMT even though they're still the same
556 // timezone. We have Etc/UTC because 'UTC', 'Etc/Universal',
557 // 'Etc/Zulu' and others are turned to 'Etc/UTC' by ICU. Etc/GMT comes
558 // from Etc/GMT0, Etc/GMT+0, Etc/GMT-0, Etc/Greenwich.
559 // ecma402#sec-canonicalizetimezonename step 3
560 if (id == UNICODE_STRING_SIMPLE("Etc/UTC") ||
561 id == UNICODE_STRING_SIMPLE("Etc/GMT")) {
562 return isolate->factory()->UTC_string();
563 }
564 // If the id is in the format of GMT[+-]hh:mm, change it to
565 // [+-]hh:mm.
566 if (id.startsWith(u"GMT", 3)) {
567 return Intl::ToString(isolate, id.tempSubString(3));
568 }
569 return Intl::ToString(isolate, id);
570}
571
573 const icu::TimeZone& tz) {
574 Factory* factory = isolate->factory();
575 icu::UnicodeString time_zone;
576 tz.getID(time_zone);
577 icu::UnicodeString canonical_time_zone;
578 if (time_zone == u"GMT") {
579 canonical_time_zone = u"+00:00";
580 } else {
581 UErrorCode status = U_ZERO_ERROR;
582 icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
583 if (U_FAILURE(status)) {
584 // When the time_zone is neither a known system time zone ID nor a
585 // valid custom time zone ID, the status is a failure.
586 return factory->undefined_value();
587 }
588 }
589 DirectHandle<String> timezone_value;
591 isolate, timezone_value, TimeZoneIdToString(isolate, canonical_time_zone),
592 {});
593 return timezone_value;
594}
595
596namespace {
597DirectHandle<String> GetCalendar(
598 Isolate* isolate, const icu::SimpleDateFormat& simple_date_format) {
599 // getType() returns legacy calendar type name instead of LDML/BCP47 calendar
600 // key values. intl.js maps them to BCP47 values for key "ca".
601 // TODO(jshin): Consider doing it here, instead.
602 std::string calendar_str = simple_date_format.getCalendar()->getType();
603
604 // Maps ICU calendar names to LDML/BCP47 types for key 'ca'.
605 // See typeMap section in third_party/icu/source/data/misc/keyTypeData.txt
606 // and
607 // http://www.unicode.org/repos/cldr/tags/latest/common/bcp47/calendar.xml
608 if (calendar_str == "gregorian") {
609 calendar_str = "gregory";
610 } else if (calendar_str == "ethiopic-amete-alem") {
611 calendar_str = "ethioaa";
612 }
613 return isolate->factory()->NewStringFromAsciiChecked(calendar_str.c_str());
614}
615
616DirectHandle<Object> GetTimeZone(
617 Isolate* isolate, const icu::SimpleDateFormat& simple_date_format) {
619 isolate, simple_date_format.getCalendar()->getTimeZone());
620}
621} // namespace
622
624 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format) {
625 return GetCalendar(isolate,
626 *(date_time_format->icu_simple_date_format()->raw()));
627}
628
630 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format) {
631 return GetTimeZone(isolate,
632 *(date_time_format->icu_simple_date_format()->raw()));
633}
634
635// ecma402 #sec-intl.datetimeformat.prototype.resolvedoptions
637 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format) {
638 Factory* factory = isolate->factory();
639 // 4. Let options be ! ObjectCreate(%ObjectPrototype%).
640 DirectHandle<JSObject> options =
641 factory->NewJSObject(isolate->object_function());
642
643 DirectHandle<Object> resolved_obj;
644
645 DirectHandle<String> locale(date_time_format->locale(), isolate);
646 DCHECK(!date_time_format->icu_locale().is_null());
647 DCHECK_NOT_NULL(date_time_format->icu_locale()->raw());
648 icu::Locale* icu_locale = date_time_format->icu_locale()->raw();
649
650 icu::SimpleDateFormat* icu_simple_date_format =
651 date_time_format->icu_simple_date_format()->raw();
652 DirectHandle<Object> timezone =
653 JSDateTimeFormat::TimeZone(isolate, date_time_format);
654
655 // Ugly hack. ICU doesn't expose numbering system in any way, so we have
656 // to assume that for given locale NumberingSystem constructor produces the
657 // same digits as NumberFormat/Calendar would.
658 // Tracked by https://unicode-org.atlassian.net/browse/ICU-13431
659 std::string numbering_system = Intl::GetNumberingSystem(*icu_locale);
660
661 icu::UnicodeString pattern_unicode;
662 icu_simple_date_format->toPattern(pattern_unicode);
663 std::string pattern;
664 pattern_unicode.toUTF8String(pattern);
665
666 // 5. For each row of Table 6, except the header row, in table order, do
667 // Table 6: Resolved Options of DateTimeFormat Instances
668 // Internal Slot Property
669 // [[Locale]] "locale"
670 // [[Calendar]] "calendar"
671 // [[NumberingSystem]] "numberingSystem"
672 // [[TimeZone]] "timeZone"
673 // [[HourCycle]] "hourCycle"
674 // "hour12"
675 // [[Weekday]] "weekday"
676 // [[Era]] "era"
677 // [[Year]] "year"
678 // [[Month]] "month"
679 // [[Day]] "day"
680 // [[Hour]] "hour"
681 // [[Minute]] "minute"
682 // [[Second]] "second"
683 // [[FractionalSecondDigits]] "fractionalSecondDigits"
684 // [[TimeZoneName]] "timeZoneName"
685 Maybe<bool> maybe_create_locale = JSReceiver::CreateDataProperty(
686 isolate, options, factory->locale_string(), locale, Just(kDontThrow));
687 DCHECK(maybe_create_locale.FromJust());
688 USE(maybe_create_locale);
689
691 JSDateTimeFormat::Calendar(isolate, date_time_format);
692 Maybe<bool> maybe_create_calendar = JSReceiver::CreateDataProperty(
693 isolate, options, factory->calendar_string(), calendar, Just(kDontThrow));
694 DCHECK(maybe_create_calendar.FromJust());
695 USE(maybe_create_calendar);
696
697 if (!numbering_system.empty()) {
698 Maybe<bool> maybe_create_numbering_system = JSReceiver::CreateDataProperty(
699 isolate, options, factory->numberingSystem_string(),
700 factory->NewStringFromAsciiChecked(numbering_system.c_str()),
702 DCHECK(maybe_create_numbering_system.FromJust());
703 USE(maybe_create_numbering_system);
704 }
705 Maybe<bool> maybe_create_time_zone = JSReceiver::CreateDataProperty(
706 isolate, options, factory->timeZone_string(), timezone, Just(kDontThrow));
707 DCHECK(maybe_create_time_zone.FromJust());
708 USE(maybe_create_time_zone);
709
710 // 5.b.i. Let hc be dtf.[[HourCycle]].
711 HourCycle hc = date_time_format->hour_cycle();
712
713 if (hc != HourCycle::kUndefined) {
714 Maybe<bool> maybe_create_hour_cycle = JSReceiver::CreateDataProperty(
715 isolate, options, factory->hourCycle_string(),
716 date_time_format->HourCycleAsString(isolate), Just(kDontThrow));
717 DCHECK(maybe_create_hour_cycle.FromJust());
718 USE(maybe_create_hour_cycle);
719 switch (hc) {
720 // ii. If hc is "h11" or "h12", let v be true.
721 case HourCycle::kH11:
722 case HourCycle::kH12: {
723 Maybe<bool> maybe_create_hour12 = JSReceiver::CreateDataProperty(
724 isolate, options, factory->hour12_string(), factory->true_value(),
726 DCHECK(maybe_create_hour12.FromJust());
727 USE(maybe_create_hour12);
728 } break;
729 // iii. Else if, hc is "h23" or "h24", let v be false.
730 case HourCycle::kH23:
731 case HourCycle::kH24: {
732 Maybe<bool> maybe_create_hour12 = JSReceiver::CreateDataProperty(
733 isolate, options, factory->hour12_string(), factory->false_value(),
735 DCHECK(maybe_create_hour12.FromJust());
736 USE(maybe_create_hour12);
737 } break;
738 // iv. Else, let v be undefined.
740 break;
741 }
742 }
743
744 // If dateStyle and timeStyle are undefined, then internal slots
745 // listed in "Table 1: Components of date and time formats" will be set
746 // in Step 33.f.iii.1 of InitializeDateTimeFormat
747 if (date_time_format->date_style() == DateTimeStyle::kUndefined &&
748 date_time_format->time_style() == DateTimeStyle::kUndefined) {
749 for (const auto& item : GetPatternItems()) {
750 // fractionalSecondsDigits need to be added before timeZoneName
751 if (item.property == "timeZoneName") {
752 int fsd = FractionalSecondDigitsFromPattern(pattern);
753 if (fsd > 0) {
754 Maybe<bool> maybe_create_fractional_seconds_digits =
756 isolate, options, factory->fractionalSecondDigits_string(),
757 factory->NewNumberFromInt(fsd), Just(kDontThrow));
758 DCHECK(maybe_create_fractional_seconds_digits.FromJust());
759 USE(maybe_create_fractional_seconds_digits);
760 }
761 }
762 for (const auto& pair : item.pairs) {
763 if (pattern.find(pair.pattern) != std::string::npos) {
764 Maybe<bool> maybe_create_property = JSReceiver::CreateDataProperty(
765 isolate, options,
766 factory->NewStringFromAsciiChecked(item.property.c_str()),
767 factory->NewStringFromAsciiChecked(pair.value.c_str()),
769 DCHECK(maybe_create_property.FromJust());
770 USE(maybe_create_property);
771 break;
772 }
773 }
774 }
775 }
776
777 // dateStyle
778 if (date_time_format->date_style() != DateTimeStyle::kUndefined) {
779 Maybe<bool> maybe_create_date_style = JSReceiver::CreateDataProperty(
780 isolate, options, factory->dateStyle_string(),
781 DateTimeStyleAsString(isolate, date_time_format->date_style()),
783 DCHECK(maybe_create_date_style.FromJust());
784 USE(maybe_create_date_style);
785 }
786
787 // timeStyle
788 if (date_time_format->time_style() != DateTimeStyle::kUndefined) {
789 Maybe<bool> maybe_create_time_style = JSReceiver::CreateDataProperty(
790 isolate, options, factory->timeStyle_string(),
791 DateTimeStyleAsString(isolate, date_time_format->time_style()),
793 DCHECK(maybe_create_time_style.FromJust());
794 USE(maybe_create_time_style);
795 }
796 return options;
797}
798
799namespace {
800
801// #sec-temporal-istemporalobject
802bool IsTemporalObject(DirectHandle<Object> value) {
803 // 1. If Type(value) is not Object, then
804 if (!IsJSReceiver(*value)) {
805 // a. Return false.
806 return false;
807 }
808 // 2. If value does not have an [[InitializedTemporalDate]],
809 // [[InitializedTemporalTime]], [[InitializedTemporalDateTime]],
810 // [[InitializedTemporalZonedDateTime]], [[InitializedTemporalYearMonth]],
811 // [[InitializedTemporalMonthDay]], or [[InitializedTemporalInstant]] internal
812 // slot, then
813 if (!IsJSTemporalPlainDate(*value) && !IsJSTemporalPlainTime(*value) &&
814 !IsJSTemporalPlainDateTime(*value) &&
815 !IsJSTemporalZonedDateTime(*value) &&
816 !IsJSTemporalPlainYearMonth(*value) &&
817 !IsJSTemporalPlainMonthDay(*value) && !IsJSTemporalInstant(*value)) {
818 // a. Return false.
819 return false;
820 }
821 // 3. Return true.
822 return true;
823}
824
825// #sec-temporal-sametemporaltype
826bool SameTemporalType(DirectHandle<Object> x, DirectHandle<Object> y) {
827 // 1. If either of ! IsTemporalObject(x) or ! IsTemporalObject(y) is false,
828 // return false.
829 if (!IsTemporalObject(x)) return false;
830 if (!IsTemporalObject(y)) return false;
831 // 2. If x has an [[InitializedTemporalDate]] internal slot and y does not,
832 // return false.
833 if (IsJSTemporalPlainDate(*x) && !IsJSTemporalPlainDate(*y)) return false;
834 // 3. If x has an [[InitializedTemporalTime]] internal slot and y does not,
835 // return false.
836 if (IsJSTemporalPlainTime(*x) && !IsJSTemporalPlainTime(*y)) return false;
837 // 4. If x has an [[InitializedTemporalDateTime]] internal slot and y does
838 // not, return false.
839 if (IsJSTemporalPlainDateTime(*x) && !IsJSTemporalPlainDateTime(*y)) {
840 return false;
841 }
842 // 5. If x has an [[InitializedTemporalZonedDateTime]] internal slot and y
843 // does not, return false.
844 if (IsJSTemporalZonedDateTime(*x) && !IsJSTemporalZonedDateTime(*y)) {
845 return false;
846 }
847 // 6. If x has an [[InitializedTemporalYearMonth]] internal slot and y does
848 // not, return false.
849 if (IsJSTemporalPlainYearMonth(*x) && !IsJSTemporalPlainYearMonth(*y)) {
850 return false;
851 }
852 // 7. If x has an [[InitializedTemporalMonthDay]] internal slot and y does
853 // not, return false.
854 if (IsJSTemporalPlainMonthDay(*x) && !IsJSTemporalPlainMonthDay(*y)) {
855 return false;
856 }
857 // 8. If x has an [[InitializedTemporalInstant]] internal slot and y does not,
858 // return false.
859 if (IsJSTemporalInstant(*x) && !IsJSTemporalInstant(*y)) return false;
860 // 9. Return true.
861 return true;
862}
863
864enum class PatternKind {
865 kDate,
866 kPlainDate,
867 kPlainDateTime,
868 kPlainTime,
869 kPlainYearMonth,
870 kPlainMonthDay,
871 kZonedDateTime,
872 kInstant,
873};
874struct DateTimeValueRecord {
876 PatternKind kind;
877};
878
879DateTimeValueRecord TemporalInstantToRecord(
880 Isolate* isolate, DirectHandle<JSTemporalInstant> instant,
881 PatternKind kind) {
882 double milliseconds =
883 BigInt::Divide(isolate, direct_handle(instant->nanoseconds(), isolate),
884 BigInt::FromInt64(isolate, 1000000))
885 .ToHandleChecked()
886 ->AsInt64();
887 return {milliseconds, kind};
888}
889
890Maybe<DateTimeValueRecord> TemporalPlainDateTimeToRecord(
891 Isolate* isolate, const icu::SimpleDateFormat& date_time_format,
892 PatternKind kind, DirectHandle<JSTemporalPlainDateTime> plain_date_time,
893 const char* method_name) {
894 // 8. Let timeZone be ! CreateTemporalTimeZone(dateTimeFormat.[[TimeZone]]).
895 DirectHandle<Object> time_zone_obj = GetTimeZone(isolate, date_time_format);
896 // TODO(ftang): we should change the return type of GetTimeZone() to
897 // Handle<String> by ensure it will not return undefined.
898 CHECK(IsString(*time_zone_obj));
899 DirectHandle<JSTemporalTimeZone> time_zone =
900 temporal::CreateTemporalTimeZone(isolate, Cast<String>(time_zone_obj))
901 .ToHandleChecked();
902 // 9. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, plainDateTime,
903 // "compatible").
904 DirectHandle<JSTemporalInstant> instant;
906 isolate, instant,
908 isolate, time_zone, plain_date_time, method_name),
910 // 10. If pattern is null, throw a TypeError exception.
911
912 // 11. Return the Record { [[pattern]]: pattern.[[pattern]],
913 // [[rangePatterns]]: pattern.[[rangePatterns]], [[epochNanoseconds]]:
914 // instant.[[Nanoseconds]] }.
915 return Just(TemporalInstantToRecord(isolate, instant, kind));
916}
917
918template <typename T>
919Maybe<DateTimeValueRecord> TemporalToRecord(
920 Isolate* isolate, const icu::SimpleDateFormat& date_time_format,
921 PatternKind kind, DirectHandle<T> temporal,
922 DirectHandle<JSReceiver> calendar, const char* method_name) {
923 // 7. Let plainDateTime be ? CreateTemporalDateTime(temporalDate.[[ISOYear]],
924 // temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], 12, 0, 0, 0, 0, 0,
925 // calendarOverride).
926 DirectHandle<JSTemporalPlainDateTime> plain_date_time;
928 isolate, plain_date_time,
930 isolate,
931 {{temporal->iso_year(), temporal->iso_month(), temporal->iso_day()},
932 {12, 0, 0, 0, 0, 0}},
933 calendar),
935 return TemporalPlainDateTimeToRecord(isolate, date_time_format, kind,
936 plain_date_time, method_name);
937}
938
939// #sec-temporal-handledatetimevaluetemporaldate
940Maybe<DateTimeValueRecord> HandleDateTimeTemporalDate(
941 Isolate* isolate, const icu::SimpleDateFormat& date_time_format,
942 DirectHandle<String> date_time_format_calendar,
943 DirectHandle<JSTemporalPlainDate> temporal_date, const char* method_name) {
944 // 1. Assert: temporalDate has an [[InitializedTemporalDate]] internal slot.
945
946 // 2. Let pattern be dateTimeFormat.[[TemporalPlainDatePattern]].
947
948 // 3. Let calendar be ? ToString(temporalDate.[[Calendar]]).
949 DirectHandle<String> calendar;
951 isolate, calendar,
952 Object::ToString(isolate,
953 direct_handle(temporal_date->calendar(), isolate)),
955
956 // 4. If calendar is dateTimeFormat.[[Calendar]], then
957 DirectHandle<JSReceiver> calendar_override;
958 if (String::Equals(isolate, calendar, date_time_format_calendar)) {
959 // a. Let calendarOverride be temporalDate.[[Calendar]].
960 calendar_override = direct_handle(temporal_date->calendar(), isolate);
961 // 5. Else if calendar is "iso8601", then
962 } else if (String::Equals(isolate, calendar,
963 isolate->factory()->iso8601_string())) {
964 // a. Let calendarOverride be ?
965 // GetBuiltinCalendar(dateTimeFormat.[[Calendar]]).
967 isolate, calendar_override,
968 temporal::GetBuiltinCalendar(isolate, date_time_format_calendar),
970 // 6. Else,
971 } else {
972 // a. Throw a RangeError exception.
974 isolate,
975 NewRangeError(MessageTemplate::kInvalid,
976 isolate->factory()->calendar_string(), calendar),
978 }
979 return TemporalToRecord<JSTemporalPlainDate>(
980 isolate, date_time_format, PatternKind::kPlainDate, temporal_date,
981 calendar_override, method_name);
982}
983
984// #sec-temporal-handledatetimevaluetemporaldatetime
985Maybe<DateTimeValueRecord> HandleDateTimeTemporalDateTime(
986 Isolate* isolate, const icu::SimpleDateFormat& date_time_format,
987 DirectHandle<String> date_time_format_calendar,
988 DirectHandle<JSTemporalPlainDateTime> date_time, const char* method_name) {
989 // 1. Assert: dateTime has an [[InitializedTemporalDateTime]] internal slot.
990 // 2. Let pattern be dateTimeFormat.[[TemporalPlainDateTimePattern]].
991 // 3. Let calendar be ? ToString(dateTime.[[Calendar]]).
992 DirectHandle<String> calendar;
994 isolate, calendar,
995 Object::ToString(isolate, direct_handle(date_time->calendar(), isolate)),
997 // 4. If calendar is not "iso8601" and not equal to
998 // dateTimeFormat.[[Calendar]], then
999 DirectHandle<JSReceiver> calendar_override;
1000 if (!String::Equals(isolate, calendar,
1001 isolate->factory()->iso8601_string()) &&
1002 !String::Equals(isolate, calendar, date_time_format_calendar)) {
1003 // a. Throw a RangeError exception.
1005 isolate,
1006 NewRangeError(MessageTemplate::kInvalid,
1007 isolate->factory()->calendar_string(), calendar),
1009 }
1010
1011 // 5. Let timeZone be ! CreateTemporalTimeZone(dateTimeFormat.[[TimeZone]]).
1012 // 6. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, dateTime,
1013 // "compatible").
1014 // 7. If pattern is null, throw a TypeError exception.
1015
1016 // 8. Return the Record { [[pattern]]: pattern.[[pattern]], [[rangePatterns]]:
1017 // pattern.[[rangePatterns]], [[epochNanoseconds]]: instant.[[Nanoseconds]] }.
1018
1019 return TemporalPlainDateTimeToRecord(isolate, date_time_format,
1020 PatternKind::kPlainDateTime, date_time,
1021 method_name);
1022}
1023
1024// #sec-temporal-handledatetimevaluetemporalzoneddatetime
1025Maybe<DateTimeValueRecord> HandleDateTimeTemporalZonedDateTime(
1026 Isolate* isolate, const icu::SimpleDateFormat& date_time_format,
1027 DirectHandle<String> date_time_format_calendar,
1028 DirectHandle<JSTemporalZonedDateTime> zoned_date_time,
1029 const char* method_name) {
1030 // 1. Assert: zonedDateTime has an [[InitializedTemporalZonedDateTime]]
1031 // internal slot.
1032 // 2. Let pattern be dateTimeFormat.[[TemporalZonedDateTimePattern]].
1033
1034 // 3. Let calendar be ? ToString(zonedDateTime.[[Calendar]]).
1035 DirectHandle<String> calendar;
1037 isolate, calendar,
1038 Object::ToString(isolate,
1039 direct_handle(zoned_date_time->calendar(), isolate)),
1041 // 4. If calendar is not "iso8601" and not equal to
1042 // dateTimeFormat.[[Calendar]], then
1043 DirectHandle<JSReceiver> calendar_override;
1044 if (!String::Equals(isolate, calendar,
1045 isolate->factory()->iso8601_string()) &&
1046 !String::Equals(isolate, calendar, date_time_format_calendar)) {
1047 // a. Throw a RangeError exception.
1049 isolate,
1050 NewRangeError(MessageTemplate::kInvalid,
1051 isolate->factory()->calendar_string(), calendar),
1053 }
1054 // 5. Let timeZone be ? ToString(zonedDateTime.[[TimeZone]]).
1055 DirectHandle<String> time_zone;
1057 isolate, time_zone,
1058 Object::ToString(isolate,
1059 direct_handle(zoned_date_time->time_zone(), isolate)),
1061 // 6. If dateTimeFormat.[[TimeZone]] is not equal to DefaultTimeZone(), and
1062 // timeZone is not equal to dateTimeFormat.[[TimeZone]], then
1063 DirectHandle<Object> date_time_format_time_zone =
1064 GetTimeZone(isolate, date_time_format);
1065 DCHECK(IsString(*date_time_format_time_zone));
1066 DirectHandle<String> date_time_format_time_zone_string =
1067 Cast<String>(date_time_format_time_zone);
1068 if (!String::Equals(isolate, date_time_format_time_zone_string,
1069 Intl::DefaultTimeZone(isolate)) &&
1070 !String::Equals(isolate, time_zone, date_time_format_time_zone_string)) {
1071 // a. Throw a RangeError exception.
1073 isolate,
1074 NewRangeError(MessageTemplate::kInvalid,
1075 isolate->factory()->timeZone_string(), time_zone),
1077 }
1078 // 7. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]).
1079 DirectHandle<JSTemporalInstant> instant =
1081 isolate, direct_handle(zoned_date_time->nanoseconds(), isolate))
1082 .ToHandleChecked();
1083 // 8. If pattern is null, throw a TypeError exception.
1084
1085 // 9. Return the Record { [[pattern]]: pattern.[[pattern]], [[rangePatterns]]:
1086 // pattern.[[rangePatterns]], [[epochNanoseconds]]: instant.[[Nanoseconds]] }.
1087 return Just(
1088 TemporalInstantToRecord(isolate, instant, PatternKind::kZonedDateTime));
1089}
1090
1091// #sec-temporal-handledatetimevaluetemporalinstant
1092Maybe<DateTimeValueRecord> HandleDateTimeTemporalInstant(
1093 Isolate* isolate, const icu::SimpleDateFormat& date_time_format,
1094 DirectHandle<JSTemporalInstant> instant, const char* method_name) {
1095 // 1. Assert: instant has an [[InitializedTemporalInstant]] internal slot.
1096 // 2. Let pattern be dateTimeFormat.[[TemporalInstantPattern]].
1097 // 3. If pattern is null, throw a TypeError exception.
1098
1099 // 4. Return the Record { [[pattern]]: pattern.[[pattern]], [[rangePatterns]]:
1100 // pattern.[[rangePatterns]], [[epochNanoseconds]]: instant.[[Nanoseconds]] }.
1101 return Just(TemporalInstantToRecord(isolate, instant, PatternKind::kInstant));
1102}
1103
1104// #sec-temporal-handledatetimevaluetemporaltime
1105Maybe<DateTimeValueRecord> HandleDateTimeTemporalTime(
1106 Isolate* isolate, const icu::SimpleDateFormat& date_time_format,
1107 DirectHandle<JSTemporalPlainTime> temporal_time, const char* method_name) {
1108 // 1. Assert: temporalTime has an [[InitializedTemporalTime]] internal slot.
1109 // 2. Let pattern be dateTimeFormat.[[TemporalPlainTimePattern]].
1110
1111 // 3. Let isoCalendar be ! GetISO8601Calendar().
1112
1113 DirectHandle<JSReceiver> iso_calendar = temporal::GetISO8601Calendar(isolate);
1114 // 4. Let plainDateTime be ? CreateTemporalDateTime(1970, 1, 1,
1115 // temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]],
1116 // temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]],
1117 // temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]],
1118 // isoCalendar).
1119 DirectHandle<JSTemporalPlainDateTime> plain_date_time;
1121 isolate, plain_date_time,
1123 isolate,
1124 {{1970, 1, 1},
1125 {temporal_time->iso_hour(), temporal_time->iso_minute(),
1126 temporal_time->iso_second(), temporal_time->iso_millisecond(),
1127 temporal_time->iso_microsecond(), temporal_time->iso_nanosecond()}},
1128 iso_calendar),
1130 return TemporalPlainDateTimeToRecord(isolate, date_time_format,
1131 PatternKind::kPlainTime, plain_date_time,
1132 method_name);
1133}
1134
1135template <typename T>
1136Maybe<DateTimeValueRecord> HandleDateTimeTemporalYearMonthOrMonthDay(
1137 Isolate* isolate, const icu::SimpleDateFormat& date_time_format,
1138 DirectHandle<String> date_time_format_calendar, PatternKind kind,
1139 DirectHandle<T> temporal, const char* method_name) {
1140 // 3. Let calendar be ? ToString(temporalYearMonth.[[Calendar]]).
1141 DirectHandle<String> calendar;
1143 isolate, calendar,
1144 Object::ToString(isolate, direct_handle(temporal->calendar(), isolate)),
1146 // 4. If calendar is not equal to dateTimeFormat.[[Calendar]], then
1147 // https://github.com/tc39/proposal-temporal/issues/2364
1148 if (!String::Equals(isolate, calendar, date_time_format_calendar)) {
1149 // a. Throw a RangeError exception.
1151 isolate,
1152 NewRangeError(MessageTemplate::kInvalid,
1153 isolate->factory()->calendar_string(), calendar),
1155 }
1156
1157 return TemporalToRecord<T>(isolate, date_time_format, kind, temporal,
1158 direct_handle(temporal->calendar(), isolate),
1159 method_name);
1160}
1161
1162// #sec-temporal-handledatetimevaluetemporalyearmonth
1163Maybe<DateTimeValueRecord> HandleDateTimeTemporalYearMonth(
1164 Isolate* isolate, const icu::SimpleDateFormat& date_time_format,
1165 DirectHandle<String> date_time_format_calendar,
1166 DirectHandle<JSTemporalPlainYearMonth> temporal_year_month,
1167 const char* method_name) {
1168 return HandleDateTimeTemporalYearMonthOrMonthDay<JSTemporalPlainYearMonth>(
1169 isolate, date_time_format, date_time_format_calendar,
1170 PatternKind::kPlainYearMonth, temporal_year_month, method_name);
1171}
1172
1173// #sec-temporal-handledatetimevaluetemporalmonthday
1174Maybe<DateTimeValueRecord> HandleDateTimeTemporalMonthDay(
1175 Isolate* isolate, const icu::SimpleDateFormat& date_time_format,
1176 DirectHandle<String> date_time_format_calendar,
1177 DirectHandle<JSTemporalPlainMonthDay> temporal_month_day,
1178 const char* method_name) {
1179 return HandleDateTimeTemporalYearMonthOrMonthDay<JSTemporalPlainMonthDay>(
1180 isolate, date_time_format, date_time_format_calendar,
1181 PatternKind::kPlainMonthDay, temporal_month_day, method_name);
1182}
1183
1184// #sec-temporal-handledatetimeothers
1185Maybe<DateTimeValueRecord> HandleDateTimeOthers(
1186 Isolate* isolate, const icu::SimpleDateFormat& date_time_format,
1187 DirectHandle<Object> x_obj, const char* method_name) {
1188 // 1. Assert: ! IsTemporalObject(x) is false.
1189 DCHECK(!IsTemporalObject(x_obj));
1190 // 2. Let pattern be dateTimeFormat.[[Pattern]].
1191
1192 // 3. Let rangePatterns be dateTimeFormat.[[RangePatterns]].
1193
1194 // 4. If x is undefined, then
1195 double x;
1196 if (IsUndefined(*x_obj)) {
1197 // a. Set x to ! Call(%Date.now%, undefined).
1198 x = static_cast<double>(JSDate::CurrentTimeValue(isolate));
1199 // 5. Else,
1200 } else {
1201 // a. Set x to ? ToNumber(x).
1202 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, x_obj,
1203 Object::ToNumber(isolate, x_obj),
1205 x = Object::NumberValue(*x_obj);
1206 }
1207 // 6. Set x to TimeClip(x).
1208 // 7. If x is NaN, throw a RangeError exception.
1209 if (!DateCache::TryTimeClip(&x)) {
1211 isolate, NewRangeError(MessageTemplate::kInvalidTimeValue),
1213 }
1214
1215 // 8. Let epochNanoseconds be ℤ(x) × 10^6ℤ.
1216
1217 // 9. Return the Record { [[pattern]]: pattern, [[rangePatterns]]:
1218 // rangePatterns, [[epochNanoseconds]]: epochNanoseconds }.
1219 return Just(DateTimeValueRecord({x, PatternKind::kDate}));
1220}
1221
1222// #sec-temporal-handledatetimevalue
1223Maybe<DateTimeValueRecord> HandleDateTimeValue(
1224 Isolate* isolate, const icu::SimpleDateFormat& date_time_format,
1225 DirectHandle<String> date_time_format_calendar, DirectHandle<Object> x,
1226 const char* method_name) {
1227 if (IsTemporalObject(x)) {
1228 // a. If x has an [[InitializedTemporalDate]] internal slot, then
1229 if (IsJSTemporalPlainDate(*x)) {
1230 // i. Return ? HandleDateTimeTemporalDate(dateTimeFormat, x).
1231 return HandleDateTimeTemporalDate(
1232 isolate, date_time_format, date_time_format_calendar,
1233 Cast<JSTemporalPlainDate>(x), method_name);
1234 }
1235 // b. If x has an [[InitializedTemporalYearMonth]] internal slot, then
1236 if (IsJSTemporalPlainYearMonth(*x)) {
1237 // i. Return ? HandleDateTimeTemporalYearMonth(dateTimeFormat, x).
1238 return HandleDateTimeTemporalYearMonth(
1239 isolate, date_time_format, date_time_format_calendar,
1240 Cast<JSTemporalPlainYearMonth>(x), method_name);
1241 }
1242 // c. If x has an [[InitializedTemporalMonthDay]] internal slot, then
1243 if (IsJSTemporalPlainMonthDay(*x)) {
1244 // i. Return ? HandleDateTimeTemporalMonthDay(dateTimeFormat, x).
1245 return HandleDateTimeTemporalMonthDay(
1246 isolate, date_time_format, date_time_format_calendar,
1247 Cast<JSTemporalPlainMonthDay>(x), method_name);
1248 }
1249 // d. If x has an [[InitializedTemporalTime]] internal slot, then
1250 if (IsJSTemporalPlainTime(*x)) {
1251 // i. Return ? HandleDateTimeTemporalTime(dateTimeFormat, x).
1252 return HandleDateTimeTemporalTime(
1253 isolate, date_time_format, Cast<JSTemporalPlainTime>(x), method_name);
1254 }
1255 // e. If x has an [[InitializedTemporalDateTime]] internal slot, then
1256 if (IsJSTemporalPlainDateTime(*x)) {
1257 // i. Return ? HandleDateTimeTemporalDateTime(dateTimeFormat, x).
1258 return HandleDateTimeTemporalDateTime(
1259 isolate, date_time_format, date_time_format_calendar,
1260 Cast<JSTemporalPlainDateTime>(x), method_name);
1261 }
1262 // f. If x has an [[InitializedTemporalInstant]] internal slot, then
1263 if (IsJSTemporalInstant(*x)) {
1264 // i. Return ? HandleDateTimeTemporalInstant(dateTimeFormat, x).
1265 return HandleDateTimeTemporalInstant(
1266 isolate, date_time_format, Cast<JSTemporalInstant>(x), method_name);
1267 }
1268 // g. Assert: x has an [[InitializedTemporalZonedDateTime]] internal slot.
1269 DCHECK(IsJSTemporalZonedDateTime(*x));
1270 // h. Return ? HandleDateTimeTemporalZonedDateTime(dateTimeFormat, x).
1271 return HandleDateTimeTemporalZonedDateTime(
1272 isolate, date_time_format, date_time_format_calendar,
1273 Cast<JSTemporalZonedDateTime>(x), method_name);
1274 }
1275
1276 // 2. Return ? HandleDateTimeOthers(dateTimeFormat, x).
1277 return HandleDateTimeOthers(isolate, date_time_format, x, method_name);
1278}
1279
1280// This helper function handles Supported fields and Default fields in Table 16
1281// ( #table-temporal-patterns ). It remove all the fields not stated in keep
1282// from input, and add the fields in add_default if a skeleton in the same
1283// category is in the input, with considering the equivalent.
1284// For example, if input is "yyyyMMhm", keep is {y,M,d} and add_default is
1285// {y,M,d}, the output will be "yyyyMMd". For example, if input is
1286// "yyyyMMhmOOOO", keep is {h,m,s,z,O,v} and add_default is {h,m,s}, then the
1287// output will be "hmOOOOs". The meaning of the skeleton letters is stated in
1288// UTS35
1289// https://www.unicode.org/reports/tr35/tr35-dates.html#table-date-field-symbol-table
1290icu::UnicodeString KeepSupportedAddDefault(
1291 const icu::UnicodeString& input, const std::set<char16_t>& keep,
1292 const std::set<char16_t>& add_default) {
1293 const std::map<char16_t, char16_t> equivalent({{'L', 'M'},
1294 {'h', 'j'},
1295 {'H', 'j'},
1296 {'k', 'j'},
1297 {'K', 'j'},
1298 {'O', 'z'},
1299 {'v', 'z'}});
1300 std::set<char16_t> to_be_added(add_default);
1301 icu::UnicodeString result;
1302 for (int32_t i = 0; i < input.length(); i++) {
1303 char16_t ch = input.charAt(i);
1304 if (keep.find(ch) != keep.end()) {
1305 to_be_added.erase(ch);
1306 auto also = equivalent.find(ch);
1307 if (also != equivalent.end()) {
1308 to_be_added.erase(also->second);
1309 }
1310 result.append(ch);
1311 }
1312 }
1313 for (auto it = to_be_added.begin(); it != to_be_added.end(); ++it) {
1314 result.append(*it);
1315 }
1316 return result;
1317}
1318
1319icu::UnicodeString GetSkeletonForPatternKind(const icu::UnicodeString& input,
1320 PatternKind kind) {
1321 // [[weekday]] skeleton could be one or more 'E' or 'c'.
1322 // [[era]] skeleton could be one or more 'G'.
1323 // [[year]] skeleton could be one or more 'y'.
1324 // [[month]] skeleton could be one or more 'M' or 'L'.
1325 // [[day]] skeleton could be one or more 'd'.
1326 // [[hour]] skeleton could be one or more 'h', 'H', 'k', 'K', or 'j'.
1327 // [[minute]] skeleton could be one or more 'm'.
1328 // [[second]] skeleton could be one or more 's'.
1329 // [[dayPeriod]] skeleton could be one or more 'b', 'B' or 'a'.
1330 // [[fractionalSecondDigits]] skeleton could be one or more 'S'.
1331 // [[timeZoneName]] skeleton could be one or more 'z', 'O', or 'v'.
1332
1333 switch (kind) {
1334 case PatternKind::kDate:
1335 return input;
1336 case PatternKind::kPlainDate:
1337 return KeepSupportedAddDefault(
1338 // Supported fields: [[weekday]], [[era]], [[year]], [[month]],
1339 // [[day]]
1340 input, {'E', 'c', 'G', 'y', 'M', 'L', 'd'},
1341 // Default fields: [[year]], [[month]], [[day]]
1342 {'y', 'M', 'd'});
1343 case PatternKind::kPlainYearMonth:
1344 return KeepSupportedAddDefault(
1345 // Supported fields: [[era]], [[year]], [[month]]
1346 input, {'G', 'y', 'M', 'L'},
1347 // Default fields: [[year]], [[month]]
1348 {'y', 'M'});
1349 case PatternKind::kPlainMonthDay:
1350 return KeepSupportedAddDefault(
1351 // Supported fields: [[month]] [[day]]
1352 input, {'M', 'L', 'd'},
1353 // Default fields: [[month]] [[day]]
1354 {'M', 'd'});
1355
1356 case PatternKind::kPlainTime:
1357 return KeepSupportedAddDefault(
1358 input,
1359 // Supported fields: [[hour]], [[minute]], [[second]], [[dayPeriod]],
1360 // [[fractionalSecondDigits]]
1361 {'h', 'H', 'k', 'K', 'j', 'm', 's', 'B', 'b', 'a', 'S'},
1362 // Default fields: [[hour]], [[minute]],
1363 // [[second]]
1364 {'j', 'm', 's'});
1365
1366 case PatternKind::kPlainDateTime:
1367 // Row TemporalInstantPattern is the same as TemporalPlainDateTimePattern
1368 // in Table 16: Supported fields for Temporal patterns
1369 // #table-temporal-patterns
1370 [[fallthrough]];
1371 case PatternKind::kInstant:
1372 return KeepSupportedAddDefault(
1373 input,
1374 // Supported fields: [[weekday]], [[era]], [[year]], [[month]],
1375 // [[day]], [[hour]], [[minute]], [[second]], [[dayPeriod]],
1376 // [[fractionalSecondDigits]]
1377 {'E', 'c', 'G', 'y', 'M', 'L', 'd', 'h', 'H', 'k', 'K', 'j', 'm', 's',
1378 'B', 'b', 'a', 'S'},
1379 // Default fields: [[year]], [[month]], [[day]], [[hour]], [[minute]],
1380 // [[second]]
1381 {'y', 'M', 'd', 'j', 'm', 's'});
1382
1383 case PatternKind::kZonedDateTime:
1384 return KeepSupportedAddDefault(
1385 // Supported fields: [[weekday]], [[era]], [[year]], [[month]],
1386 // [[day]], [[hour]], [[minute]], [[second]], [[dayPeriod]],
1387 // [[fractionalSecondDigits]], [[timeZoneName]]
1388 input, {'E', 'c', 'G', 'y', 'M', 'L', 'd', 'h', 'H', 'k', 'K',
1389 'j', 'm', 's', 'B', 'b', 'a', 'S', 'z', 'O', 'v'},
1390 // Default fields: [[year]], [[month]], [[day]], [[hour]], [[minute]],
1391 // [[second]], [[timeZoneName]]
1392 {'y', 'M', 'd', 'j', 'm', 's', 'z'});
1393 }
1394}
1395
1396icu::UnicodeString SkeletonFromDateFormat(
1397 const icu::SimpleDateFormat& icu_date_format) {
1398 icu::UnicodeString pattern;
1399 pattern = icu_date_format.toPattern(pattern);
1400
1401 UErrorCode status = U_ZERO_ERROR;
1402 icu::UnicodeString skeleton =
1403 icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
1404 DCHECK(U_SUCCESS(status));
1405 return skeleton;
1406}
1407
1408std::unique_ptr<icu::SimpleDateFormat> GetSimpleDateTimeForTemporal(
1409 const icu::SimpleDateFormat& date_format, PatternKind kind) {
1410 DCHECK_NE(kind, PatternKind::kDate);
1411 icu::UnicodeString skeleton =
1412 GetSkeletonForPatternKind(SkeletonFromDateFormat(date_format), kind);
1413 UErrorCode status = U_ZERO_ERROR;
1414 std::unique_ptr<icu::SimpleDateFormat> result(
1415 static_cast<icu::SimpleDateFormat*>(
1416 icu::DateFormat::createInstanceForSkeleton(
1417 skeleton, date_format.getSmpFmtLocale(), status)));
1418 DCHECK(result);
1419 DCHECK(U_SUCCESS(status));
1420 result->setTimeZone(date_format.getTimeZone());
1421 return result;
1422}
1423
1424icu::UnicodeString CallICUFormat(const icu::SimpleDateFormat& date_format,
1425 PatternKind kind, double time_in_milliseconds,
1426 icu::FieldPositionIterator* fp_iter,
1427 UErrorCode& status) {
1428 icu::UnicodeString result;
1429 // Use the date_format directly for Date value.
1430 if (kind == PatternKind::kDate) {
1431 date_format.format(time_in_milliseconds, result, fp_iter, status);
1432 return result;
1433 }
1434 // For other Temporal objects, lazy generate a SimpleDateFormat for the kind.
1435 std::unique_ptr<icu::SimpleDateFormat> pattern(
1436 GetSimpleDateTimeForTemporal(date_format, kind));
1437 pattern->format(time_in_milliseconds, result, fp_iter, status);
1438 return result;
1439}
1440
1441// ecma402/#sec-formatdatetime
1442// FormatDateTime( dateTimeFormat, x )
1443MaybeDirectHandle<String> FormatDateTime(
1444 Isolate* isolate, const icu::SimpleDateFormat& date_format, double x) {
1445 if (!DateCache::TryTimeClip(&x)) {
1446 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
1447 }
1448
1449 icu::UnicodeString result;
1450 date_format.format(x, result);
1451
1452 // Revert ICU 72 change that introduced U+202F instead of U+0020
1453 // to separate time from AM/PM. See https://crbug.com/1414292.
1454 result = result.findAndReplace(icu::UnicodeString(0x202f),
1455 icu::UnicodeString(0x20));
1456
1457 return Intl::ToString(isolate, result);
1458}
1459
1460MaybeDirectHandle<String> FormatMillisecondsByKindToString(
1461 Isolate* isolate, const icu::SimpleDateFormat& date_format,
1462 PatternKind kind, double x) {
1463 UErrorCode status = U_ZERO_ERROR;
1464 icu::UnicodeString result =
1465 CallICUFormat(date_format, kind, x, nullptr, status);
1466 DCHECK(U_SUCCESS(status));
1467
1468 return Intl::ToString(isolate, result);
1469}
1470
1471MaybeDirectHandle<String> FormatDateTimeWithTemporalSupport(
1472 Isolate* isolate, const icu::SimpleDateFormat& date_format,
1473 DirectHandle<String> date_time_format_calendar, DirectHandle<Object> x,
1474 const char* method_name) {
1475 DateTimeValueRecord record;
1477 isolate, record,
1478 HandleDateTimeValue(isolate, date_format, date_time_format_calendar, x,
1479 method_name),
1480 {});
1481 return FormatMillisecondsByKindToString(isolate, date_format, record.kind,
1482 record.epoch_milliseconds);
1483}
1484
1485MaybeDirectHandle<String> FormatDateTimeWithTemporalSupport(
1486 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format,
1487 DirectHandle<Object> x, const char* method_name) {
1488 return FormatDateTimeWithTemporalSupport(
1489 isolate, *(date_time_format->icu_simple_date_format()->raw()),
1490 JSDateTimeFormat::Calendar(isolate, date_time_format), x, method_name);
1491}
1492
1493} // namespace
1494
1495// ecma402/#sec-datetime-format-functions
1496// DateTime Format Functions
1498 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format,
1499 DirectHandle<Object> date, const char* method_name) {
1500 // 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]]
1501 // internal slot.
1502 if (v8_flags.harmony_temporal) {
1503 return FormatDateTimeWithTemporalSupport(isolate, date_time_format, date,
1504 method_name);
1505 }
1506
1507 // 3. If date is not provided or is undefined, then
1508 double x;
1509 if (IsUndefined(*date)) {
1510 // 3.a Let x be Call(%Date_now%, undefined).
1511 x = static_cast<double>(JSDate::CurrentTimeValue(isolate));
1512 } else {
1513 // 4. Else,
1514 // a. Let x be ? ToNumber(date).
1516 DCHECK(IsNumber(*date));
1518 }
1519 // 5. Return FormatDateTime(dtf, x).
1520 icu::SimpleDateFormat* format =
1521 date_time_format->icu_simple_date_format()->raw();
1522 return FormatDateTime(isolate, *format, x);
1523}
1524
1525namespace {
1526Isolate::ICUObjectCacheType ConvertToCacheType(
1528 switch (type) {
1530 return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormatForDate;
1532 return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormatForTime;
1534 return Isolate::ICUObjectCacheType::kDefaultSimpleDateFormat;
1535 }
1536}
1537
1538} // namespace
1539
1542 DirectHandle<Object> options, RequiredOption required,
1543 DefaultsOption defaults, const char* method_name) {
1544 Isolate::ICUObjectCacheType cache_type = ConvertToCacheType(defaults);
1545
1546 Factory* factory = isolate->factory();
1547 // 1. Let x be ? thisTimeValue(this value);
1548 if (!IsJSDate(*date)) {
1549 THROW_NEW_ERROR(isolate,
1550 NewTypeError(MessageTemplate::kMethodInvokedOnWrongType,
1551 factory->Date_string()));
1552 }
1553 double const x = Cast<JSDate>(date)->value();
1554 // 2. If x is NaN, return "Invalid Date"
1555 if (std::isnan(x)) {
1556 return factory->Invalid_Date_string();
1557 }
1558
1559 // We only cache the instance when locales is a string/undefined and
1560 // options is undefined, as that is the only case when the specified
1561 // side-effects of examining those arguments are unobservable.
1562 bool can_cache = (IsString(*locales) || IsUndefined(*locales, isolate)) &&
1563 IsUndefined(*options, isolate);
1564 if (can_cache) {
1565 // Both locales and options are undefined, check the cache.
1566 icu::SimpleDateFormat* cached_icu_simple_date_format =
1567 static_cast<icu::SimpleDateFormat*>(
1568 isolate->get_cached_icu_object(cache_type, locales));
1569 if (cached_icu_simple_date_format != nullptr) {
1570 return FormatDateTime(isolate, *cached_icu_simple_date_format, x);
1571 }
1572 }
1573 // 4. Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »).
1574 DirectHandle<JSFunction> constructor(
1575 Cast<JSFunction>(isolate->context()
1576 ->native_context()
1577 ->intl_date_time_format_function()),
1578 isolate);
1581 isolate, map,
1582 JSFunction::GetDerivedMap(isolate, constructor, constructor));
1583 DirectHandle<JSDateTimeFormat> date_time_format;
1585 isolate, date_time_format,
1586 JSDateTimeFormat::CreateDateTimeFormat(isolate, map, locales, options,
1587 required, defaults, method_name));
1588
1589 if (can_cache) {
1590 isolate->set_icu_object_in_cache(
1591 cache_type, locales,
1592 std::static_pointer_cast<icu::UMemory>(
1593 date_time_format->icu_simple_date_format()->get()));
1594 }
1595 // 5. Return FormatDateTime(dateFormat, x).
1596 icu::SimpleDateFormat* format =
1597 date_time_format->icu_simple_date_format()->raw();
1598 return FormatDateTime(isolate, *format, x);
1599}
1600
1603 DirectHandle<Object> options, const char* method_name) {
1604 // 4. Let dateFormat be ? Construct(%DateTimeFormat%, « locales, options »).
1605 DirectHandle<JSFunction> constructor(
1606 isolate->context()->native_context()->intl_date_time_format_function(),
1607 isolate);
1608 DirectHandle<Map> map =
1609 JSFunction::GetDerivedMap(isolate, constructor, constructor)
1610 .ToHandleChecked();
1611 DirectHandle<JSDateTimeFormat> date_time_format;
1613 isolate, date_time_format,
1614 JSDateTimeFormat::New(isolate, map, locales, options, method_name));
1615
1616 // 5. Return FormatDateTime(dateFormat, x).
1617 return FormatDateTimeWithTemporalSupport(isolate, date_time_format, x,
1618 method_name);
1619}
1620
1622 Isolate* isolate, Handle<JSReceiver> format_holder) {
1623 DirectHandle<Context> native_context(isolate->context()->native_context(),
1624 isolate);
1625 DirectHandle<JSFunction> constructor(
1626 Cast<JSFunction>(native_context->intl_date_time_format_function()),
1627 isolate);
1630 isolate, dtf,
1631 Intl::LegacyUnwrapReceiver(isolate, format_holder, constructor,
1632 IsJSDateTimeFormat(*format_holder)));
1633 // 2. If Type(dtf) is not Object or dtf does not have an
1634 // [[InitializedDateTimeFormat]] internal slot, then
1635 if (!IsJSDateTimeFormat(*dtf)) {
1636 // a. Throw a TypeError exception.
1637 THROW_NEW_ERROR(isolate,
1638 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
1639 isolate->factory()->NewStringFromAsciiChecked(
1640 "UnwrapDateTimeFormat"),
1641 format_holder));
1642 }
1643 // 3. Return dtf.
1644 return Cast<JSDateTimeFormat>(dtf);
1645}
1646
1647// Convert the input in the form of
1648// [+-\u2212]hh:?mm to the ID acceptable for SimpleTimeZone
1649// GMT[+-]hh or GMT[+-]hh:mm or empty
1650std::optional<std::string> GetOffsetTimeZone(Isolate* isolate,
1654 const String::FlatContent& flat = time_zone->GetFlatContent(no_gc);
1655 int32_t len = flat.length();
1656 if (len < 3) {
1657 // Error
1658 return std::nullopt;
1659 }
1660 std::string tz("GMT");
1661 switch (flat.Get(0)) {
1662 case 0x2212:
1663 case '-':
1664 tz += '-';
1665 break;
1666 case '+':
1667 tz += '+';
1668 break;
1669 default:
1670 // Error
1671 return std::nullopt;
1672 }
1673 // 00 - 23
1674 uint16_t h0 = flat.Get(1);
1675 uint16_t h1 = flat.Get(2);
1676
1677 if ((h0 >= '0' && h0 <= '1' && h1 >= '0' && h1 <= '9') ||
1678 (h0 == '2' && h1 >= '0' && h1 <= '3')) {
1679 tz += h0;
1680 tz += h1;
1681 } else {
1682 // Error
1683 return std::nullopt;
1684 }
1685 if (len == 3) {
1686 return tz;
1687 }
1688 int32_t p = 3;
1689 uint16_t m0 = flat.Get(p);
1690 if (m0 == ':') {
1691 // Ignore ':'
1692 p++;
1693 if (len == p) {
1694 // Error
1695 return std::nullopt;
1696 }
1697 m0 = flat.Get(p);
1698 }
1699 if (len - p != 2) {
1700 // Error
1701 return std::nullopt;
1702 }
1703 uint16_t m1 = flat.Get(p + 1);
1704 if (m0 >= '0' && m0 <= '5' && m1 >= '0' && m1 <= '9') {
1705 tz += m0;
1706 tz += m1;
1707 return tz;
1708 }
1709 // Error
1710 return std::nullopt;
1711}
1712std::unique_ptr<icu::TimeZone> JSDateTimeFormat::CreateTimeZone(
1713 Isolate* isolate, DirectHandle<String> time_zone_string) {
1714 // Create time zone as specified by the user. We have to re-create time zone
1715 // since calendar takes ownership.
1716 std::optional<std::string> offsetTimeZone =
1717 GetOffsetTimeZone(isolate, time_zone_string);
1718 if (offsetTimeZone.has_value()) {
1719 std::unique_ptr<icu::TimeZone> tz(
1720 icu::TimeZone::createTimeZone(offsetTimeZone->c_str()));
1721 return tz;
1722 }
1723 std::unique_ptr<char[]> time_zone = time_zone_string->ToCString();
1724 std::string canonicalized = CanonicalizeTimeZoneID(time_zone.get());
1725 if (canonicalized.empty()) return std::unique_ptr<icu::TimeZone>();
1726 std::unique_ptr<icu::TimeZone> tz(
1727 icu::TimeZone::createTimeZone(canonicalized.c_str()));
1728 // 18.b If the result of IsValidTimeZoneName(timeZone) is false, then
1729 // i. Throw a RangeError exception.
1730 if (!Intl::IsValidTimeZoneName(*tz)) return std::unique_ptr<icu::TimeZone>();
1731 return tz;
1732}
1733
1734namespace {
1735
1736class CalendarCache {
1737 public:
1738 icu::Calendar* CreateCalendar(const icu::Locale& locale, icu::TimeZone* tz) {
1739 icu::UnicodeString tz_id;
1740 tz->getID(tz_id);
1741 std::string key;
1742 tz_id.toUTF8String<std::string>(key);
1743 key += ":";
1744 key += locale.getName();
1745
1746 base::MutexGuard guard(&mutex_);
1747 auto it = map_.find(key);
1748 if (it != map_.end()) {
1749 delete tz;
1750 return it->second->clone();
1751 }
1752 // Create a calendar using locale, and apply time zone to it.
1753 UErrorCode status = U_ZERO_ERROR;
1754 std::unique_ptr<icu::Calendar> calendar(
1755 icu::Calendar::createInstance(tz, locale, status));
1756 DCHECK(U_SUCCESS(status));
1758
1759 if (calendar->getDynamicClassID() ==
1760 icu::GregorianCalendar::getStaticClassID() ||
1761 strcmp(calendar->getType(), "iso8601") == 0) {
1762 icu::GregorianCalendar* gc =
1763 static_cast<icu::GregorianCalendar*>(calendar.get());
1764 status = U_ZERO_ERROR;
1765 // The beginning of ECMAScript time, namely -(2**53)
1766 const double start_of_time = -9007199254740992;
1767 gc->setGregorianChange(start_of_time, status);
1768 DCHECK(U_SUCCESS(status));
1769 }
1770
1771 if (map_.size() > 8) { // Cache at most 8 calendars.
1772 map_.clear();
1773 }
1774 map_[key] = std::move(calendar);
1775 return map_[key]->clone();
1776 }
1777
1778 private:
1779 std::map<std::string, std::unique_ptr<icu::Calendar>> map_;
1780 base::Mutex mutex_;
1781};
1782
1783icu::Calendar* CreateCalendar(Isolate* isolate, const icu::Locale& icu_locale,
1784 icu::TimeZone* tz) {
1785 static base::LazyInstance<CalendarCache>::type calendar_cache =
1787 return calendar_cache.Pointer()->CreateCalendar(icu_locale, tz);
1788}
1789
1790icu::UnicodeString ReplaceHourCycleInPattern(icu::UnicodeString pattern,
1792 char16_t replacement;
1793 switch (hc) {
1795 return pattern;
1797 replacement = 'K';
1798 break;
1800 replacement = 'h';
1801 break;
1803 replacement = 'H';
1804 break;
1806 replacement = 'k';
1807 break;
1808 }
1809 bool replace = true;
1810 icu::UnicodeString result;
1811 char16_t last = u'\0';
1812 for (int32_t i = 0; i < pattern.length(); i++) {
1813 char16_t ch = pattern.charAt(i);
1814 switch (ch) {
1815 case '\'':
1816 replace = !replace;
1817 result.append(ch);
1818 break;
1819 case 'H':
1820 [[fallthrough]];
1821 case 'h':
1822 [[fallthrough]];
1823 case 'K':
1824 [[fallthrough]];
1825 case 'k':
1826 // If the previous field is a day, add a space before the hour.
1827 if (replace && last == u'd') {
1828 result.append(' ');
1829 }
1830 result.append(replace ? replacement : ch);
1831 break;
1832 default:
1833 result.append(ch);
1834 break;
1835 }
1836 last = ch;
1837 }
1838 return result;
1839}
1840
1841std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormat(
1842 const icu::Locale& icu_locale, const icu::UnicodeString& skeleton,
1843 icu::DateTimePatternGenerator* generator, JSDateTimeFormat::HourCycle hc) {
1844 // See https://github.com/tc39/ecma402/issues/225 . The best pattern
1845 // generation needs to be done in the base locale according to the
1846 // current spec however odd it may be. See also crbug.com/826549 .
1847 // This is a temporary work-around to get v8's external behavior to match
1848 // the current spec, but does not follow the spec provisions mentioned
1849 // in the above Ecma 402 issue.
1850 // TODO(jshin): The spec may need to be revised because using the base
1851 // locale for the pattern match is not quite right. Moreover, what to
1852 // do with 'related year' part when 'chinese/dangi' calendar is specified
1853 // has to be discussed. Revisit once the spec is clarified/revised.
1854 icu::UnicodeString pattern;
1855 UErrorCode status = U_ZERO_ERROR;
1856 pattern = generator->getBestPattern(skeleton, UDATPG_MATCH_HOUR_FIELD_LENGTH,
1857 status);
1858 pattern = ReplaceHourCycleInPattern(pattern, hc);
1859 DCHECK(U_SUCCESS(status));
1860
1861 // Make formatter from skeleton. Calendar and numbering system are added
1862 // to the locale as Unicode extension (if they were specified at all).
1863 status = U_ZERO_ERROR;
1864 std::unique_ptr<icu::SimpleDateFormat> date_format(
1865 new icu::SimpleDateFormat(pattern, icu_locale, status));
1866 if (U_FAILURE(status)) return std::unique_ptr<icu::SimpleDateFormat>();
1867
1868 DCHECK_NOT_NULL(date_format.get());
1869 return date_format;
1870}
1871
1872class DateFormatCache {
1873 public:
1874 icu::SimpleDateFormat* Create(const icu::Locale& icu_locale,
1875 const icu::UnicodeString& skeleton,
1876 icu::DateTimePatternGenerator* generator,
1878 std::string key;
1879 skeleton.toUTF8String<std::string>(key);
1880 key += ":";
1881 key += icu_locale.getName();
1882
1883 base::MutexGuard guard(&mutex_);
1884 auto it = map_.find(key);
1885 if (it != map_.end()) {
1886 return static_cast<icu::SimpleDateFormat*>(it->second->clone());
1887 }
1888
1889 if (map_.size() > 8) { // Cache at most 8 DateFormats.
1890 map_.clear();
1891 }
1892 std::unique_ptr<icu::SimpleDateFormat> instance(
1893 CreateICUDateFormat(icu_locale, skeleton, generator, hc));
1894 if (instance == nullptr) return nullptr;
1895 map_[key] = std::move(instance);
1896 return static_cast<icu::SimpleDateFormat*>(map_[key]->clone());
1897 }
1898
1899 private:
1900 std::map<std::string, std::unique_ptr<icu::SimpleDateFormat>> map_;
1901 base::Mutex mutex_;
1902};
1903
1904std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormatFromCache(
1905 const icu::Locale& icu_locale, const icu::UnicodeString& skeleton,
1906 icu::DateTimePatternGenerator* generator, JSDateTimeFormat::HourCycle hc) {
1909 return std::unique_ptr<icu::SimpleDateFormat>(
1910 cache.Pointer()->Create(icu_locale, skeleton, generator, hc));
1911}
1912
1913// We treat PatternKind::kDate different than other because most of the
1914// pre-existing usage are using the formatter with Date() and Temporal is
1915// new and not yet adopted by the web yet. We try to optimize the performance
1916// and memory usage for the pre-existing code so we cache for it.
1917// We may later consider caching Temporal one also if the usage increase.
1918// Right now we want to avoid making the constructor more expensive and
1919// increasing overhead in the object.
1920std::unique_ptr<icu::DateIntervalFormat> LazyCreateDateIntervalFormat(
1921 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format,
1922 PatternKind kind) {
1924 date_time_format->icu_date_interval_format();
1925 if (kind == PatternKind::kDate && managed_format->get()) {
1926 return std::unique_ptr<icu::DateIntervalFormat>(
1927 managed_format->raw()->clone());
1928 }
1929 UErrorCode status = U_ZERO_ERROR;
1930
1931 icu::Locale loc = *(date_time_format->icu_locale()->raw());
1932 // We need to pass in the hc to DateIntervalFormat by using Unicode 'hc'
1933 // extension.
1934 std::string hcString = ToHourCycleString(date_time_format->hour_cycle());
1935 if (!hcString.empty()) {
1936 loc.setUnicodeKeywordValue("hc", hcString, status);
1937 }
1938
1939 icu::SimpleDateFormat* icu_simple_date_format =
1940 date_time_format->icu_simple_date_format()->raw();
1941
1942 icu::UnicodeString skeleton = GetSkeletonForPatternKind(
1943 SkeletonFromDateFormat(*icu_simple_date_format), kind);
1944
1945 std::unique_ptr<icu::DateIntervalFormat> date_interval_format(
1946 icu::DateIntervalFormat::createInstance(skeleton, loc, status));
1947 DCHECK(U_SUCCESS(status));
1948 date_interval_format->setTimeZone(icu_simple_date_format->getTimeZone());
1949 if (kind != PatternKind::kDate) {
1950 return date_interval_format;
1951 }
1952 DirectHandle<Managed<icu::DateIntervalFormat>> managed_interval_format =
1954 std::move(date_interval_format));
1955 date_time_format->set_icu_date_interval_format(*managed_interval_format);
1956 return std::unique_ptr<icu::DateIntervalFormat>(
1957 managed_interval_format->raw()->clone());
1958}
1959
1960JSDateTimeFormat::HourCycle HourCycleFromPattern(
1961 const icu::UnicodeString pattern) {
1962 bool in_quote = false;
1963 for (int32_t i = 0; i < pattern.length(); i++) {
1964 char16_t ch = pattern[i];
1965 switch (ch) {
1966 case '\'':
1967 in_quote = !in_quote;
1968 break;
1969 case 'K':
1970 if (!in_quote) return JSDateTimeFormat::HourCycle::kH11;
1971 break;
1972 case 'h':
1973 if (!in_quote) return JSDateTimeFormat::HourCycle::kH12;
1974 break;
1975 case 'H':
1976 if (!in_quote) return JSDateTimeFormat::HourCycle::kH23;
1977 break;
1978 case 'k':
1979 if (!in_quote) return JSDateTimeFormat::HourCycle::kH24;
1980 break;
1981 }
1982 }
1984}
1985
1986icu::DateFormat::EStyle DateTimeStyleToEStyle(
1988 switch (style) {
1990 return icu::DateFormat::EStyle::kFull;
1992 return icu::DateFormat::EStyle::kLong;
1994 return icu::DateFormat::EStyle::kMedium;
1996 return icu::DateFormat::EStyle::kShort;
1998 UNREACHABLE();
1999 }
2000}
2001
2002icu::UnicodeString ReplaceSkeleton(const icu::UnicodeString input,
2004 icu::UnicodeString result;
2005 char16_t to;
2006 switch (hc) {
2008 to = 'K';
2009 break;
2011 to = 'h';
2012 break;
2014 to = 'H';
2015 break;
2017 to = 'k';
2018 break;
2020 UNREACHABLE();
2021 }
2022 for (int32_t i = 0; i < input.length(); i++) {
2023 switch (input[i]) {
2024 // We need to skip 'a', 'b', 'B' here due to
2025 // https://unicode-org.atlassian.net/browse/ICU-20437
2026 case 'a':
2027 [[fallthrough]];
2028 case 'b':
2029 [[fallthrough]];
2030 case 'B':
2031 // ignore
2032 break;
2033 case 'h':
2034 [[fallthrough]];
2035 case 'H':
2036 [[fallthrough]];
2037 case 'K':
2038 [[fallthrough]];
2039 case 'k':
2040 result += to;
2041 break;
2042 default:
2043 result += input[i];
2044 break;
2045 }
2046 }
2047 return result;
2048}
2049
2050std::unique_ptr<icu::SimpleDateFormat> DateTimeStylePattern(
2052 JSDateTimeFormat::DateTimeStyle time_style, icu::Locale& icu_locale,
2053 JSDateTimeFormat::HourCycle hc, icu::DateTimePatternGenerator* generator) {
2054 std::unique_ptr<icu::SimpleDateFormat> result;
2057 result.reset(reinterpret_cast<icu::SimpleDateFormat*>(
2058 icu::DateFormat::createDateTimeInstance(
2059 DateTimeStyleToEStyle(date_style),
2060 DateTimeStyleToEStyle(time_style), icu_locale)));
2061 } else {
2062 result.reset(reinterpret_cast<icu::SimpleDateFormat*>(
2063 icu::DateFormat::createDateInstance(DateTimeStyleToEStyle(date_style),
2064 icu_locale)));
2065 // For instance without time, we do not need to worry about the hour cycle
2066 // impact so we can return directly.
2067 if (result != nullptr) {
2068 return result;
2069 }
2070 }
2071 } else {
2073 result.reset(reinterpret_cast<icu::SimpleDateFormat*>(
2074 icu::DateFormat::createTimeInstance(DateTimeStyleToEStyle(time_style),
2075 icu_locale)));
2076 } else {
2077 UNREACHABLE();
2078 }
2079 }
2080
2081 UErrorCode status = U_ZERO_ERROR;
2082 // Somehow we fail to create the instance.
2083 if (result.get() == nullptr) {
2084 // Fallback to the locale without "nu".
2085 if (!icu_locale.getUnicodeKeywordValue<std::string>("nu", status).empty()) {
2086 status = U_ZERO_ERROR;
2087 icu_locale.setUnicodeKeywordValue("nu", nullptr, status);
2088 return DateTimeStylePattern(date_style, time_style, icu_locale, hc,
2089 generator);
2090 }
2091 status = U_ZERO_ERROR;
2092 // Fallback to the locale without "hc".
2093 if (!icu_locale.getUnicodeKeywordValue<std::string>("hc", status).empty()) {
2094 status = U_ZERO_ERROR;
2095 icu_locale.setUnicodeKeywordValue("hc", nullptr, status);
2096 return DateTimeStylePattern(date_style, time_style, icu_locale, hc,
2097 generator);
2098 }
2099 status = U_ZERO_ERROR;
2100 // Fallback to the locale without "ca".
2101 if (!icu_locale.getUnicodeKeywordValue<std::string>("ca", status).empty()) {
2102 status = U_ZERO_ERROR;
2103 icu_locale.setUnicodeKeywordValue("ca", nullptr, status);
2104 return DateTimeStylePattern(date_style, time_style, icu_locale, hc,
2105 generator);
2106 }
2107 return nullptr;
2108 }
2109 icu::UnicodeString pattern;
2110 pattern = result->toPattern(pattern);
2111
2112 status = U_ZERO_ERROR;
2113 icu::UnicodeString skeleton =
2114 icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
2115 DCHECK(U_SUCCESS(status));
2116
2117 // If the skeleton match the HourCycle, we just return it.
2118 if (hc == HourCycleFromPattern(pattern)) {
2119 return result;
2120 }
2121
2122 return CreateICUDateFormatFromCache(icu_locale, ReplaceSkeleton(skeleton, hc),
2123 generator, hc);
2124}
2125
2126class DateTimePatternGeneratorCache {
2127 public:
2128 // Return a clone copy that the caller have to free.
2129 icu::DateTimePatternGenerator* CreateGenerator(Isolate* isolate,
2130 const icu::Locale& locale) {
2131 std::string key(locale.getName());
2132 base::MutexGuard guard(&mutex_);
2133 auto it = map_.find(key);
2134 icu::DateTimePatternGenerator* orig;
2135 if (it != map_.end()) {
2136 DCHECK(it->second != nullptr);
2137 orig = it->second.get();
2138 } else {
2139 UErrorCode status = U_ZERO_ERROR;
2140 orig = icu::DateTimePatternGenerator::createInstance(locale, status);
2141 // It may not be an U_MEMORY_ALLOCATION_ERROR.
2142 // Fallback to use "root".
2143 if (U_FAILURE(status)) {
2144 status = U_ZERO_ERROR;
2145 orig = icu::DateTimePatternGenerator::createInstance("root", status);
2146 }
2147 if (U_SUCCESS(status) && orig != nullptr) {
2148 map_[key].reset(orig);
2149 } else {
2150 DCHECK(status == U_MEMORY_ALLOCATION_ERROR);
2152 isolate, "DateTimePatternGeneratorCache::CreateGenerator");
2153 }
2154 }
2155 icu::DateTimePatternGenerator* clone = orig ? orig->clone() : nullptr;
2156 if (clone == nullptr) {
2158 isolate, "DateTimePatternGeneratorCache::CreateGenerator");
2159 }
2160 return clone;
2161 }
2162
2163 private:
2164 std::map<std::string, std::unique_ptr<icu::DateTimePatternGenerator>> map_;
2165 base::Mutex mutex_;
2166};
2167
2168} // namespace
2169
2171
2172// ecma402/#sec-initializedatetimeformat
2174 Isolate* isolate, DirectHandle<Map> map, DirectHandle<Object> locales,
2175 DirectHandle<Object> input_options, const char* service) {
2177 isolate, map, locales, input_options, RequiredOption::kAny,
2178 DefaultsOption::kDate, service);
2179}
2180
2182 Isolate* isolate, DirectHandle<Map> map, DirectHandle<Object> locales,
2183 DirectHandle<Object> input_options, RequiredOption required,
2184 DefaultsOption defaults, const char* service) {
2185 Factory* factory = isolate->factory();
2186 // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
2187 Maybe<std::vector<std::string>> maybe_requested_locales =
2188 Intl::CanonicalizeLocaleList(isolate, locales);
2189 MAYBE_RETURN(maybe_requested_locales, {});
2190 std::vector<std::string> requested_locales =
2191 maybe_requested_locales.FromJust();
2192 // 2. Let options be ? CoerceOptionsToObject(_options_).
2195 isolate, options, CoerceOptionsToObject(isolate, input_options, service));
2196
2197 // 4. Let matcher be ? GetOption(options, "localeMatcher", "string",
2198 // « "lookup", "best fit" », "best fit").
2199 // 5. Set opt.[[localeMatcher]] to matcher.
2200 Maybe<Intl::MatcherOption> maybe_locale_matcher =
2201 Intl::GetLocaleMatcher(isolate, options, service);
2202 MAYBE_RETURN(maybe_locale_matcher, {});
2203 Intl::MatcherOption locale_matcher = maybe_locale_matcher.FromJust();
2204
2205 std::unique_ptr<char[]> calendar_str = nullptr;
2206 std::unique_ptr<char[]> numbering_system_str = nullptr;
2207 const std::vector<const char*> empty_values = {};
2208 // 6. Let calendar be ? GetOption(options, "calendar",
2209 // "string", undefined, undefined).
2210 Maybe<bool> maybe_calendar = GetStringOption(
2211 isolate, options, "calendar", empty_values, service, &calendar_str);
2212 MAYBE_RETURN(maybe_calendar, {});
2213 if (maybe_calendar.FromJust() && calendar_str != nullptr) {
2214 icu::Locale default_locale;
2215 if (!Intl::IsWellFormedCalendar(calendar_str.get())) {
2217 isolate, NewRangeError(
2218 MessageTemplate::kInvalid, factory->calendar_string(),
2219 factory->NewStringFromAsciiChecked(calendar_str.get())));
2220 }
2221 }
2222
2223 // 8. Let numberingSystem be ? GetOption(options, "numberingSystem",
2224 // "string", undefined, undefined).
2225 Maybe<bool> maybe_numberingSystem = Intl::GetNumberingSystem(
2226 isolate, options, service, &numbering_system_str);
2227 MAYBE_RETURN(maybe_numberingSystem, {});
2228
2229 // 6. Let hour12 be ? GetOption(options, "hour12", "boolean", undefined,
2230 // undefined).
2231 bool hour12;
2232 Maybe<bool> maybe_get_hour12 =
2233 GetBoolOption(isolate, options, "hour12", service, &hour12);
2234 MAYBE_RETURN(maybe_get_hour12, {});
2235
2236 // 7. Let hourCycle be ? GetOption(options, "hourCycle", "string", « "h11",
2237 // "h12", "h23", "h24" », undefined).
2238 Maybe<HourCycle> maybe_hour_cycle = GetHourCycle(isolate, options, service);
2239 MAYBE_RETURN(maybe_hour_cycle, {});
2240 HourCycle hour_cycle = maybe_hour_cycle.FromJust();
2241
2242 // 8. If hour12 is not undefined, then
2243 if (maybe_get_hour12.FromJust()) {
2244 // a. Let hourCycle be null.
2246 }
2247 // 9. Set opt.[[hc]] to hourCycle.
2248
2249 // ecma402/#sec-intl.datetimeformat-internal-slots
2250 // The value of the [[RelevantExtensionKeys]] internal slot is
2251 // « "ca", "nu", "hc" ».
2252 std::set<std::string> relevant_extension_keys = {"nu", "ca", "hc"};
2253
2254 // 10. Let localeData be %DateTimeFormat%.[[LocaleData]].
2255 // 11. Let r be ResolveLocale( %DateTimeFormat%.[[AvailableLocales]],
2256 // requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]],
2257 // localeData).
2258 //
2259 Maybe<Intl::ResolvedLocale> maybe_resolve_locale = Intl::ResolveLocale(
2260 isolate, JSDateTimeFormat::GetAvailableLocales(), requested_locales,
2261 locale_matcher, relevant_extension_keys);
2262 if (maybe_resolve_locale.IsNothing()) {
2263 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
2264 }
2265 Intl::ResolvedLocale r = maybe_resolve_locale.FromJust();
2266
2267 icu::Locale icu_locale = r.icu_locale;
2268 DCHECK(!icu_locale.isBogus());
2269
2270 UErrorCode status = U_ZERO_ERROR;
2271 if (calendar_str != nullptr) {
2272 auto ca_extension_it = r.extensions.find("ca");
2273 if (ca_extension_it != r.extensions.end() &&
2274 ca_extension_it->second != calendar_str.get()) {
2275 icu_locale.setUnicodeKeywordValue("ca", nullptr, status);
2276 DCHECK(U_SUCCESS(status));
2277 }
2278 }
2279 if (numbering_system_str != nullptr) {
2280 auto nu_extension_it = r.extensions.find("nu");
2281 if (nu_extension_it != r.extensions.end() &&
2282 nu_extension_it->second != numbering_system_str.get()) {
2283 icu_locale.setUnicodeKeywordValue("nu", nullptr, status);
2284 DCHECK(U_SUCCESS(status));
2285 }
2286 }
2287
2288 // Need to keep a copy of icu_locale which not changing "ca", "nu", "hc"
2289 // by option.
2290 icu::Locale resolved_locale(icu_locale);
2291
2292 if (calendar_str != nullptr &&
2293 Intl::IsValidCalendar(icu_locale, calendar_str.get())) {
2294 icu_locale.setUnicodeKeywordValue("ca", calendar_str.get(), status);
2295 DCHECK(U_SUCCESS(status));
2296 }
2297
2298 if (numbering_system_str != nullptr &&
2299 Intl::IsValidNumberingSystem(numbering_system_str.get())) {
2300 icu_locale.setUnicodeKeywordValue("nu", numbering_system_str.get(), status);
2301 DCHECK(U_SUCCESS(status));
2302 }
2303
2305 generator_cache = LAZY_INSTANCE_INITIALIZER;
2306
2307 std::unique_ptr<icu::DateTimePatternGenerator> generator(
2308 generator_cache.Pointer()->CreateGenerator(isolate, icu_locale));
2309
2310 // 15.Let hcDefault be dataLocaleData.[[hourCycle]].
2311 HourCycle hc_default = ToHourCycle(generator->getDefaultHourCycle(status));
2312 DCHECK(U_SUCCESS(status));
2313
2314 // 16.Let hc be r.[[hc]].
2317 auto hc_extension_it = r.extensions.find("hc");
2318 if (hc_extension_it != r.extensions.end()) {
2319 hc = ToHourCycle(hc_extension_it->second.c_str());
2320 }
2321 } else {
2322 hc = hour_cycle;
2323 }
2324
2325 // 25. If hour12 is true, then
2326 if (maybe_get_hour12.FromJust()) {
2327 if (hour12) {
2328 // a. Let hc be dataLocaleData.[[hourCycle12]].
2329 hc = DefaultHourCycle12(icu_locale, hc_default);
2330 // 26. Else if hour12 is false, then
2331 } else {
2332 // a. Let hc be dataLocaleData.[[hourCycle24]].
2333 hc = DefaultHourCycle24(icu_locale, hc_default);
2334 }
2335 } else {
2336 // 27. Else,
2337 // a. Assert: hour12 is undefined.
2338 // b. Let hc be r.[[hc]].
2339 // c. If hc is null, set hc to dataLocaleData.[[hourCycle]].
2340 if (hc == HourCycle::kUndefined) {
2341 hc = hc_default;
2342 }
2343 }
2344
2345 // 17. Let timeZone be ? Get(options, "timeZone").
2346 DirectHandle<Object> time_zone_obj;
2348 isolate, time_zone_obj,
2349 Object::GetPropertyOrElement(isolate, options,
2350 isolate->factory()->timeZone_string()));
2351
2352 std::unique_ptr<icu::TimeZone> tz;
2353 if (!IsUndefined(*time_zone_obj, isolate)) {
2356 Object::ToString(isolate, time_zone_obj));
2358 } else {
2359 // 19.a. Else / Let timeZone be DefaultTimeZone().
2360 tz.reset(icu::TimeZone::createDefault());
2361 }
2362
2363 if (tz.get() == nullptr) {
2364 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeZone,
2365 time_zone_obj));
2366 }
2367
2368 std::unique_ptr<icu::Calendar> calendar(
2369 CreateCalendar(isolate, icu_locale, tz.release()));
2370
2371 // 18.b If the result of IsValidTimeZoneName(timeZone) is false, then
2372 // i. Throw a RangeError exception.
2373 if (calendar.get() == nullptr) {
2374 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeZone,
2375 time_zone_obj));
2376 }
2377
2380 std::unique_ptr<icu::SimpleDateFormat> icu_date_format;
2381
2382 // 35. Let hasExplicitFormatComponents be false.
2383 int32_t explicit_format_components =
2384 0; // The fields which are not undefined.
2385 // 36. For each row of Table 1, except the header row, do
2386 bool has_hour_option = false;
2387 std::string skeleton;
2388 for (const PatternData& item : GetPatternData(hc)) {
2389 // Need to read fractionalSecondDigits before reading the timeZoneName
2390 if (item.property == "timeZoneName") {
2391 // Let _value_ be ? GetNumberOption(options, "fractionalSecondDigits", 1,
2392 // 3, *undefined*). The *undefined* is represented by value 0 here.
2393 int fsd;
2395 isolate, fsd,
2396 GetNumberOption(isolate, options,
2397 factory->fractionalSecondDigits_string(), 1, 3, 0),
2398 {});
2399 if (fsd > 0) {
2400 explicit_format_components =
2401 FractionalSecondDigits::update(explicit_format_components, true);
2402 }
2403 // Convert fractionalSecondDigits to skeleton.
2404 for (int i = 0; i < fsd; i++) {
2405 skeleton += "S";
2406 }
2407 }
2408 std::unique_ptr<char[]> input;
2409 // i. Let prop be the name given in the Property column of the row.
2410 // ii. Let value be ? GetOption(options, prop, "string", « the strings
2411 // given in the Values column of the row », undefined).
2412 Maybe<bool> maybe_get_option =
2413 GetStringOption(isolate, options, item.property.c_str(),
2414 item.allowed_values, service, &input);
2415 MAYBE_RETURN(maybe_get_option, {});
2416 if (maybe_get_option.FromJust()) {
2417 // Record which fields are not undefined into explicit_format_components.
2418 if (item.property == "hour") {
2419 has_hour_option = true;
2420 }
2421 DCHECK_NOT_NULL(input.get());
2422 // iii. Set opt.[[<prop>]] to value.
2423 skeleton += item.map.find(input.get())->second;
2424 // e. If value is not undefined, then
2425 // i. Set hasExplicitFormatComponents to true.
2426 explicit_format_components |= 1 << static_cast<int32_t>(item.bitShift);
2427 }
2428 }
2429
2430 // 29. Let matcher be ? GetOption(options, "formatMatcher", "string", «
2431 // "basic", "best fit" », "best fit").
2432 // We implement only best fit algorithm, but still need to check
2433 // if the formatMatcher values are in range.
2434 // c. Let matcher be ? GetOption(options, "formatMatcher", "string",
2435 // « "basic", "best fit" », "best fit").
2436 Maybe<FormatMatcherOption> maybe_format_matcher =
2438 isolate, options, "formatMatcher", service, {"best fit", "basic"},
2441 MAYBE_RETURN(maybe_format_matcher, {});
2442 // TODO(ftang): uncomment the following line and handle format_matcher.
2443 // FormatMatcherOption format_matcher = maybe_format_matcher.FromJust();
2444
2445 // 32. Let dateStyle be ? GetOption(options, "dateStyle", "string", «
2446 // "full", "long", "medium", "short" », undefined).
2448 isolate, options, "dateStyle", service,
2449 {"full", "long", "medium", "short"},
2453 MAYBE_RETURN(maybe_date_style, {});
2454 // 33. Set dateTimeFormat.[[DateStyle]] to dateStyle.
2455 date_style = maybe_date_style.FromJust();
2456
2457 // 34. Let timeStyle be ? GetOption(options, "timeStyle", "string", «
2458 // "full", "long", "medium", "short" »).
2460 isolate, options, "timeStyle", service,
2461 {"full", "long", "medium", "short"},
2465 MAYBE_RETURN(maybe_time_style, {});
2466
2467 // 35. Set dateTimeFormat.[[TimeStyle]] to timeStyle.
2468 time_style = maybe_time_style.FromJust();
2469
2470 // 36. If timeStyle is not undefined, then
2471 HourCycle dateTimeFormatHourCycle = HourCycle::kUndefined;
2473 // a. Set dateTimeFormat.[[HourCycle]] to hc.
2474 dateTimeFormatHourCycle = hc;
2475 }
2476
2477 // 37. If dateStyle or timeStyle are not undefined, then
2480 // a. If hasExplicitFormatComponents is true, then
2481 if (explicit_format_components != 0) {
2482 // i. Throw a TypeError exception.
2484 isolate, NewTypeError(MessageTemplate::kInvalid,
2485 factory->NewStringFromStaticChars("option"),
2486 factory->NewStringFromStaticChars("option")));
2487 }
2488 // b. If required is ~date~ and timeStyle is not *undefined*, then
2489 if (required == RequiredOption::kDate &&
2491 // i. Throw a *TypeError* exception.
2492 THROW_NEW_ERROR(isolate,
2493 NewTypeError(MessageTemplate::kInvalid,
2494 factory->NewStringFromStaticChars("option"),
2495 factory->timeStyle_string()));
2496 }
2497 // c. If required is ~time~ and dateStyle is not *undefined*, then
2498 if (required == RequiredOption::kTime &&
2500 // i. Throw a *TypeError* exception.
2501 THROW_NEW_ERROR(isolate,
2502 NewTypeError(MessageTemplate::kInvalid,
2503 factory->NewStringFromStaticChars("option"),
2504 factory->dateStyle_string()));
2505 }
2506 // b. Let pattern be DateTimeStylePattern(dateStyle, timeStyle,
2507 // dataLocaleData, hc).
2508 isolate->CountUsage(
2510
2511 icu_date_format =
2512 DateTimeStylePattern(date_style, time_style, icu_locale,
2513 dateTimeFormatHourCycle, generator.get());
2514 if (icu_date_format.get() == nullptr) {
2515 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
2516 }
2517 } else {
2518 // a. Let needDefaults be *true*.
2519 bool needDefaults = true;
2520 // b. If required is ~date~ or ~any~, then
2521 if (required == RequiredOption::kDate || required == RequiredOption::kAny) {
2522 // 1. For each property name prop of << *"weekday"*, *"year"*, *"month"*,
2523 // *"day"* >>, do
2524 // 1. Let value be formatOptions.[[<prop>]].
2525 // 1. If value is not *undefined*, let needDefaults be *false*.
2526
2527 needDefaults &= !Weekday::decode(explicit_format_components);
2528 needDefaults &= !Year::decode(explicit_format_components);
2529 needDefaults &= !Month::decode(explicit_format_components);
2530 needDefaults &= !Day::decode(explicit_format_components);
2531 }
2532 // c. If required is ~time~ or ~any~, then
2533 if (required == RequiredOption::kTime || required == RequiredOption::kAny) {
2534 // 1. For each property name prop of &laquo; *"dayPeriod"*, *"hour"*,
2535 // *"minute"*, *"second"*, *"fractionalSecondDigits"* &raquo;, do
2536 // 1. Let value be formatOptions.[[&lt;_prop_&gt;]].
2537 // 1. If value is not *undefined*, let _needDefaults_ be *false*.
2538 needDefaults &= !DayPeriod::decode(explicit_format_components);
2539 needDefaults &= !Hour::decode(explicit_format_components);
2540 needDefaults &= !Minute::decode(explicit_format_components);
2541 needDefaults &= !Second::decode(explicit_format_components);
2542 needDefaults &=
2543 !FractionalSecondDigits::decode(explicit_format_components);
2544 }
2545 // 1. If needDefaults is *true* and _defaults_ is either ~date~ or ~all~,
2546 // then
2547 if (needDefaults && ((DefaultsOption::kDate == defaults) ||
2548 (DefaultsOption::kAll == defaults))) {
2549 // 1. For each property name prop of <<*"year"*, *"month"*, *"day"* >>, do
2550 // 1. Set formatOptions.[[<<prop>>]] to *"numeric"*.
2551 skeleton += "yMd";
2552 }
2553 // 1. If _needDefaults_ is *true* and defaults is either ~time~ or ~all~,
2554 // then
2555 if (needDefaults && ((DefaultsOption::kTime == defaults) ||
2556 (DefaultsOption::kAll == defaults))) {
2557 // 1. For each property name prop of << *"hour"*, *"minute"*, *"second"*
2558 // >>, do
2559 // 1. Set _formatOptions_.[[<<prop>>]] to *"numeric"*.
2560 // See
2561 // https://unicode.org/reports/tr35/tr35.html#UnicodeHourCycleIdentifier
2562 switch (hc) {
2563 case HourCycle::kH12:
2564 skeleton += "hms";
2565 break;
2566 case HourCycle::kH23:
2568 skeleton += "Hms";
2569 break;
2570 case HourCycle::kH11:
2571 skeleton += "Kms";
2572 break;
2573 case HourCycle::kH24:
2574 skeleton += "kms";
2575 break;
2576 }
2577 }
2578 // e. If dateTimeFormat.[[Hour]] is not undefined, then
2579 if (has_hour_option) {
2580 // v. Set dateTimeFormat.[[HourCycle]] to hc.
2581 dateTimeFormatHourCycle = hc;
2582 } else {
2583 // f. Else,
2584 // Set dateTimeFormat.[[HourCycle]] to undefined.
2585 dateTimeFormatHourCycle = HourCycle::kUndefined;
2586 }
2587 icu::UnicodeString skeleton_ustr(skeleton.c_str());
2588 icu_date_format = CreateICUDateFormatFromCache(
2589 icu_locale, skeleton_ustr, generator.get(), dateTimeFormatHourCycle);
2590 if (icu_date_format.get() == nullptr) {
2591 // Remove extensions and try again.
2592 icu_locale = icu::Locale(icu_locale.getBaseName());
2593 icu_date_format = CreateICUDateFormatFromCache(
2594 icu_locale, skeleton_ustr, generator.get(), dateTimeFormatHourCycle);
2595 if (icu_date_format.get() == nullptr) {
2596 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
2597 }
2598 }
2599 }
2600
2601 // The creation of Calendar depends on timeZone so we have to put 13 after 17.
2602 // Also icu_date_format is not created until here.
2603 // 13. Set dateTimeFormat.[[Calendar]] to r.[[ca]].
2604 icu_date_format->adoptCalendar(calendar.release());
2605
2606 // 12.1.1 InitializeDateTimeFormat ( dateTimeFormat, locales, options )
2607 //
2608 // Steps 8-9 set opt.[[hc]] to value *other than undefined*
2609 // if "hour12" is set or "hourCycle" is set in the option.
2610 //
2611 // 9.2.6 ResolveLocale (... )
2612 // Step 8.h / 8.i and 8.k
2613 //
2614 // An hour12 option always overrides an hourCycle option.
2615 // Additionally hour12 and hourCycle both clear out any existing Unicode
2616 // extension key in the input locale.
2617 //
2618 // See details in https://github.com/tc39/test262/pull/2035
2619 if (maybe_get_hour12.FromJust() ||
2620 maybe_hour_cycle.FromJust() != HourCycle::kUndefined) {
2621 auto hc_extension_it = r.extensions.find("hc");
2622 if (hc_extension_it != r.extensions.end()) {
2623 if (dateTimeFormatHourCycle !=
2624 ToHourCycle(hc_extension_it->second.c_str())) {
2625 // Remove -hc- if it does not agree with what we used.
2626 status = U_ZERO_ERROR;
2627 resolved_locale.setUnicodeKeywordValue("hc", nullptr, status);
2628 DCHECK(U_SUCCESS(status));
2629 }
2630 }
2631 }
2632
2633 Maybe<std::string> maybe_locale_str = Intl::ToLanguageTag(resolved_locale);
2634 MAYBE_RETURN(maybe_locale_str, {});
2635 DirectHandle<String> locale_str =
2636 isolate->factory()->NewStringFromAsciiChecked(
2637 maybe_locale_str.FromJust().c_str());
2638
2639 DirectHandle<Managed<icu::Locale>> managed_locale =
2641 isolate, 0, std::shared_ptr<icu::Locale>{icu_locale.clone()});
2642
2645 std::move(icu_date_format));
2646
2647 DirectHandle<Managed<icu::DateIntervalFormat>> managed_interval_format =
2648 Managed<icu::DateIntervalFormat>::From(isolate, 0, nullptr);
2649
2650 // Now all properties are ready, so we can allocate the result object.
2652 isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
2654 date_time_format->set_flags(0);
2656 date_time_format->set_date_style(date_style);
2657 }
2659 date_time_format->set_time_style(time_style);
2660 }
2661 date_time_format->set_hour_cycle(dateTimeFormatHourCycle);
2662 date_time_format->set_locale(*locale_str);
2663 date_time_format->set_icu_locale(*managed_locale);
2664 date_time_format->set_icu_simple_date_format(*managed_format);
2665 date_time_format->set_icu_date_interval_format(*managed_interval_format);
2666 return date_time_format;
2667}
2668
2669namespace {
2670
2671// The list comes from third_party/icu/source/i18n/unicode/udat.h.
2672// They're mapped to DateTimeFormat components listed at
2673// https://tc39.github.io/ecma402/#sec-datetimeformat-abstracts .
2674DirectHandle<String> IcuDateFieldIdToDateType(int32_t field_id,
2675 Isolate* isolate) {
2676 switch (field_id) {
2677 case -1:
2678 return isolate->factory()->literal_string();
2679 case UDAT_YEAR_FIELD:
2680 case UDAT_EXTENDED_YEAR_FIELD:
2681 return isolate->factory()->year_string();
2682 case UDAT_YEAR_NAME_FIELD:
2683 return isolate->factory()->yearName_string();
2684 case UDAT_MONTH_FIELD:
2685 case UDAT_STANDALONE_MONTH_FIELD:
2686 return isolate->factory()->month_string();
2687 case UDAT_DATE_FIELD:
2688 return isolate->factory()->day_string();
2689 case UDAT_HOUR_OF_DAY1_FIELD:
2690 case UDAT_HOUR_OF_DAY0_FIELD:
2691 case UDAT_HOUR1_FIELD:
2692 case UDAT_HOUR0_FIELD:
2693 return isolate->factory()->hour_string();
2694 case UDAT_MINUTE_FIELD:
2695 return isolate->factory()->minute_string();
2696 case UDAT_SECOND_FIELD:
2697 return isolate->factory()->second_string();
2698 case UDAT_DAY_OF_WEEK_FIELD:
2699 case UDAT_DOW_LOCAL_FIELD:
2700 case UDAT_STANDALONE_DAY_FIELD:
2701 return isolate->factory()->weekday_string();
2702 case UDAT_AM_PM_FIELD:
2703 case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
2704 case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
2705 return isolate->factory()->dayPeriod_string();
2706 case UDAT_TIMEZONE_FIELD:
2707 case UDAT_TIMEZONE_RFC_FIELD:
2708 case UDAT_TIMEZONE_GENERIC_FIELD:
2709 case UDAT_TIMEZONE_SPECIAL_FIELD:
2710 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
2711 case UDAT_TIMEZONE_ISO_FIELD:
2712 case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
2713 return isolate->factory()->timeZoneName_string();
2714 case UDAT_ERA_FIELD:
2715 return isolate->factory()->era_string();
2716 case UDAT_FRACTIONAL_SECOND_FIELD:
2717 return isolate->factory()->fractionalSecond_string();
2718 case UDAT_RELATED_YEAR_FIELD:
2719 return isolate->factory()->relatedYear_string();
2720
2721 case UDAT_QUARTER_FIELD:
2722 case UDAT_STANDALONE_QUARTER_FIELD:
2723 default:
2724 // Other UDAT_*_FIELD's cannot show up because there is no way to specify
2725 // them via options of Intl.DateTimeFormat.
2726 UNREACHABLE();
2727 }
2728}
2729
2730MaybeDirectHandle<JSArray> FieldPositionIteratorToArray(
2731 Isolate* isolate, const icu::UnicodeString& formatted,
2732 icu::FieldPositionIterator fp_iter, bool output_source);
2733
2734MaybeDirectHandle<JSArray> FormatMillisecondsByKindToArray(
2735 Isolate* isolate, const icu::SimpleDateFormat& date_format,
2736 PatternKind kind, double x, bool output_source) {
2737 icu::FieldPositionIterator fp_iter;
2738 UErrorCode status = U_ZERO_ERROR;
2739 icu::UnicodeString formatted =
2740 CallICUFormat(date_format, kind, x, &fp_iter, status);
2741 if (U_FAILURE(status)) {
2742 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError));
2743 }
2744 return FieldPositionIteratorToArray(isolate, formatted, fp_iter,
2745 output_source);
2746}
2747
2748MaybeDirectHandle<JSArray> FormatMillisecondsByKindToArrayOutputSource(
2749 Isolate* isolate, const icu::SimpleDateFormat& date_format,
2750 PatternKind kind, double x) {
2751 return FormatMillisecondsByKindToArray(isolate, date_format, kind, x, true);
2752}
2753
2754MaybeDirectHandle<JSArray> FormatToPartsWithTemporalSupport(
2755 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format,
2756 DirectHandle<Object> x, bool output_source, const char* method_name) {
2757 icu::SimpleDateFormat* format =
2758 date_time_format->icu_simple_date_format()->raw();
2759 DCHECK_NOT_NULL(format);
2760
2761 // 1. Let x be ? HandleDateTimeValue(dateTimeFormat, x).
2762 DateTimeValueRecord x_record;
2764 isolate, x_record,
2765 HandleDateTimeValue(isolate, *format, GetCalendar(isolate, *format), x,
2766 method_name),
2767 {});
2768
2769 return FormatMillisecondsByKindToArray(isolate, *format, x_record.kind,
2770 x_record.epoch_milliseconds,
2771 output_source);
2772}
2773
2774MaybeDirectHandle<JSArray> FormatMillisecondsToArray(
2775 Isolate* isolate, const icu::SimpleDateFormat& format, double value,
2776 bool output_source) {
2777 icu::UnicodeString formatted;
2778 icu::FieldPositionIterator fp_iter;
2779 UErrorCode status = U_ZERO_ERROR;
2780 format.format(value, formatted, &fp_iter, status);
2781 if (U_FAILURE(status)) {
2782 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError));
2783 }
2784 return FieldPositionIteratorToArray(isolate, formatted, fp_iter,
2785 output_source);
2786}
2787
2788MaybeDirectHandle<JSArray> FormatMillisecondsToArrayOutputSource(
2789 Isolate* isolate, const icu::SimpleDateFormat& format, double value) {
2790 return FormatMillisecondsToArray(isolate, format, value, true);
2791}
2792} // namespace
2793
2795 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format,
2796 DirectHandle<Object> x, bool output_source, const char* method_name) {
2797 Factory* factory = isolate->factory();
2798 if (v8_flags.harmony_temporal) {
2799 return FormatToPartsWithTemporalSupport(isolate, date_time_format, x,
2800 output_source, method_name);
2801 }
2802
2803 if (IsUndefined(*x, isolate)) {
2804 x = factory->NewNumberFromInt64(JSDate::CurrentTimeValue(isolate));
2805 } else {
2806 ASSIGN_RETURN_ON_EXCEPTION(isolate, x, Object::ToNumber(isolate, x));
2807 }
2808
2809 double date_value = Object::NumberValue(*x);
2810 if (!DateCache::TryTimeClip(&date_value)) {
2811 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
2812 }
2813 return FormatMillisecondsToArray(
2814 isolate, *(date_time_format->icu_simple_date_format()->raw()), date_value,
2815 output_source);
2816}
2817
2818namespace {
2819MaybeDirectHandle<JSArray> FieldPositionIteratorToArray(
2820 Isolate* isolate, const icu::UnicodeString& formatted,
2821 icu::FieldPositionIterator fp_iter, bool output_source) {
2822 Factory* factory = isolate->factory();
2823 icu::FieldPosition fp;
2825 int32_t length = formatted.length();
2826 if (length == 0) return result;
2827
2828 int index = 0;
2829 int32_t previous_end_pos = 0;
2831 while (fp_iter.next(fp)) {
2832 int32_t begin_pos = fp.getBeginIndex();
2833 int32_t end_pos = fp.getEndIndex();
2834
2835 if (previous_end_pos < begin_pos) {
2837 isolate, substring,
2838 Intl::ToString(isolate, formatted, previous_end_pos, begin_pos));
2839 if (output_source) {
2840 Intl::AddElement(isolate, result, index,
2841 IcuDateFieldIdToDateType(-1, isolate), substring,
2842 isolate->factory()->source_string(),
2843 isolate->factory()->shared_string());
2844 } else {
2845 Intl::AddElement(isolate, result, index,
2846 IcuDateFieldIdToDateType(-1, isolate), substring);
2847 }
2848 ++index;
2849 }
2851 isolate, substring,
2852 Intl::ToString(isolate, formatted, begin_pos, end_pos));
2853 if (output_source) {
2854 Intl::AddElement(isolate, result, index,
2855 IcuDateFieldIdToDateType(fp.getField(), isolate),
2856 substring, isolate->factory()->source_string(),
2857 isolate->factory()->shared_string());
2858 } else {
2859 Intl::AddElement(isolate, result, index,
2860 IcuDateFieldIdToDateType(fp.getField(), isolate),
2861 substring);
2862 }
2863 previous_end_pos = end_pos;
2864 ++index;
2865 }
2866 if (previous_end_pos < length) {
2868 isolate, substring,
2869 Intl::ToString(isolate, formatted, previous_end_pos, length));
2870 if (output_source) {
2871 Intl::AddElement(isolate, result, index,
2872 IcuDateFieldIdToDateType(-1, isolate), substring,
2873 isolate->factory()->source_string(),
2874 isolate->factory()->shared_string());
2875 } else {
2876 Intl::AddElement(isolate, result, index,
2877 IcuDateFieldIdToDateType(-1, isolate), substring);
2878 }
2879 }
2881 return result;
2882}
2883
2884} // namespace
2885
2886const std::set<std::string>& JSDateTimeFormat::GetAvailableLocales() {
2888}
2889
2891 switch (hour_cycle()) {
2893 return isolate->factory()->undefined_string();
2894 case HourCycle::kH11:
2895 return isolate->factory()->h11_string();
2896 case HourCycle::kH12:
2897 return isolate->factory()->h12_string();
2898 case HourCycle::kH23:
2899 return isolate->factory()->h23_string();
2900 case HourCycle::kH24:
2901 return isolate->factory()->h24_string();
2902 default:
2903 UNREACHABLE();
2904 }
2905}
2906
2907namespace {
2908
2909Maybe<bool> AddPartForFormatRange(
2910 Isolate* isolate, DirectHandle<JSArray> array,
2911 const icu::UnicodeString& string, int32_t index, int32_t field,
2912 int32_t start, int32_t end, const Intl::FormatRangeSourceTracker& tracker) {
2914 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, substring,
2915 Intl::ToString(isolate, string, start, end),
2916 Nothing<bool>());
2917 Intl::AddElement(isolate, array, index,
2918 IcuDateFieldIdToDateType(field, isolate), substring,
2919 isolate->factory()->source_string(),
2920 Intl::SourceString(isolate, tracker.GetSource(start, end)));
2921 return Just(true);
2922}
2923
2924// If this function return a value, it could be a throw of TypeError, or normal
2925// formatted string. If it return a nullopt the caller should call the fallback
2926// function.
2927std::optional<MaybeDirectHandle<String>> FormattedToString(
2928 Isolate* isolate, const icu::FormattedValue& formatted) {
2929 UErrorCode status = U_ZERO_ERROR;
2930 icu::UnicodeString result = formatted.toString(status);
2931 if (U_FAILURE(status)) {
2932 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError));
2933 }
2934 icu::ConstrainedFieldPosition cfpos;
2935 while (formatted.nextPosition(cfpos, status)) {
2936 if (cfpos.getCategory() == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
2937 return Intl::ToString(isolate, result);
2938 }
2939 }
2940 return std::nullopt;
2941}
2942
2943// A helper function to convert the FormattedDateInterval to a
2944// MaybeHandle<JSArray> for the implementation of formatRangeToParts.
2945// If this function return a value, it could be a throw of TypeError, or normal
2946// formatted parts in JSArray. If it return a nullopt the caller should call
2947// the fallback function.
2948std::optional<MaybeDirectHandle<JSArray>> FormattedDateIntervalToJSArray(
2949 Isolate* isolate, const icu::FormattedValue& formatted) {
2950 UErrorCode status = U_ZERO_ERROR;
2951 icu::UnicodeString result = formatted.toString(status);
2952
2953 Factory* factory = isolate->factory();
2954 DirectHandle<JSArray> array = factory->NewJSArray(0);
2955 icu::ConstrainedFieldPosition cfpos;
2956 int index = 0;
2957 int32_t previous_end_pos = 0;
2958 Intl::FormatRangeSourceTracker tracker;
2959 bool output_range = false;
2960 while (formatted.nextPosition(cfpos, status)) {
2961 int32_t category = cfpos.getCategory();
2962 int32_t field = cfpos.getField();
2963 int32_t start = cfpos.getStart();
2964 int32_t limit = cfpos.getLimit();
2965
2966 if (category == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
2967 DCHECK_LE(field, 2);
2968 output_range = true;
2969 tracker.Add(field, start, limit);
2970 } else {
2971 DCHECK(category == UFIELD_CATEGORY_DATE);
2972 if (start > previous_end_pos) {
2973 // Add "literal" from the previous end position to the start if
2974 // necessary.
2975 Maybe<bool> maybe_added =
2976 AddPartForFormatRange(isolate, array, result, index, -1,
2977 previous_end_pos, start, tracker);
2978 MAYBE_RETURN(maybe_added, Handle<JSArray>());
2979 previous_end_pos = start;
2980 index++;
2981 }
2982 Maybe<bool> maybe_added = AddPartForFormatRange(
2983 isolate, array, result, index, field, start, limit, tracker);
2984 MAYBE_RETURN(maybe_added, Handle<JSArray>());
2985 previous_end_pos = limit;
2986 ++index;
2987 }
2988 }
2989 int32_t end = result.length();
2990 // Add "literal" in the end if necessary.
2991 if (end > previous_end_pos) {
2992 Maybe<bool> maybe_added = AddPartForFormatRange(
2993 isolate, array, result, index, -1, previous_end_pos, end, tracker);
2994 MAYBE_RETURN(maybe_added, Handle<JSArray>());
2995 }
2996
2997 if (U_FAILURE(status)) {
2998 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError));
2999 }
3000
3002 if (output_range) return array;
3003 return std::nullopt;
3004}
3005
3006// The shared code between formatRange and formatRangeToParts
3007template <typename T, std::optional<MaybeDirectHandle<T>> (*Format)(
3008 Isolate*, const icu::FormattedValue&)>
3009std::optional<MaybeDirectHandle<T>> CallICUFormatRange(
3010 Isolate* isolate, const icu::DateIntervalFormat* format,
3011 const icu::Calendar* calendar, double x, double y);
3012
3013// #sec-partitiondatetimerangepattern
3014template <typename T, std::optional<MaybeDirectHandle<T>> (*Format)(
3015 Isolate*, const icu::FormattedValue&)>
3016std::optional<MaybeDirectHandle<T>> PartitionDateTimeRangePattern(
3017 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format, double x,
3018 double y, const char* method_name) {
3019 // 1. Let x be TimeClip(x).
3020 // 2. If x is NaN, throw a RangeError exception.
3021 if (!DateCache::TryTimeClip(&x)) {
3022 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
3023 }
3024 // 3. Let y be TimeClip(y).
3025 // 4. If y is NaN, throw a RangeError exception.
3026 if (!DateCache::TryTimeClip(&y)) {
3027 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
3028 }
3029
3030 std::unique_ptr<icu::DateIntervalFormat> format(LazyCreateDateIntervalFormat(
3031 isolate, date_time_format, PatternKind::kDate));
3032 if (format.get() == nullptr) {
3033 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError));
3034 }
3035
3036 icu::SimpleDateFormat* date_format =
3037 date_time_format->icu_simple_date_format()->raw();
3038 const icu::Calendar* calendar = date_format->getCalendar();
3039
3040 return CallICUFormatRange<T, Format>(isolate, format.get(), calendar, x, y);
3041}
3042
3043template <typename T, std::optional<MaybeDirectHandle<T>> (*Format)(
3044 Isolate*, const icu::FormattedValue&)>
3045std::optional<MaybeDirectHandle<T>> CallICUFormatRange(
3046 Isolate* isolate, const icu::DateIntervalFormat* format,
3047 const icu::Calendar* calendar, double x, double y) {
3048 UErrorCode status = U_ZERO_ERROR;
3049
3050 std::unique_ptr<icu::Calendar> c1(calendar->clone());
3051 std::unique_ptr<icu::Calendar> c2(calendar->clone());
3052 c1->setTime(x, status);
3053 c2->setTime(y, status);
3054 // We need to format by Calendar because we need the Gregorian change
3055 // adjustment already in the SimpleDateFormat to set the correct value of date
3056 // older than Oct 15, 1582.
3057 icu::FormattedDateInterval formatted =
3058 format->formatToValue(*c1, *c2, status);
3059 if (U_FAILURE(status)) {
3060 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError));
3061 }
3062 return Format(isolate, formatted);
3063}
3064
3065template <typename T,
3066 std::optional<MaybeDirectHandle<T>> (*Format)(
3067 Isolate*, const icu::FormattedValue&),
3068 MaybeDirectHandle<T> (*Fallback)(
3069 Isolate*, const icu::SimpleDateFormat&, PatternKind, double)>
3070MaybeDirectHandle<T> FormatRangeCommonWithTemporalSupport(
3071 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format,
3072 DirectHandle<Object> x_obj, DirectHandle<Object> y_obj,
3073 const char* method_name) {
3074 // 5. If either of ! IsTemporalObject(x) or ! IsTemporalObject(y) is true,
3075 // then
3076 if (IsTemporalObject(x_obj) || IsTemporalObject(y_obj)) {
3077 // a. If ! SameTemporalType(x, y) is false, throw a TypeError exception.
3078 if (!SameTemporalType(x_obj, y_obj)) {
3080 isolate,
3081 NewTypeError(MessageTemplate::kInvalidArgumentForTemporal, y_obj));
3082 }
3083 }
3084 // 6. Let x be ? HandleDateTimeValue(dateTimeFormat, x).
3085 icu::SimpleDateFormat* icu_simple_date_format =
3086 date_time_format->icu_simple_date_format()->raw();
3087 DirectHandle<String> date_time_format_calendar =
3088 GetCalendar(isolate, *icu_simple_date_format);
3089 DateTimeValueRecord x_record;
3091 isolate, x_record,
3092 HandleDateTimeValue(isolate, *icu_simple_date_format,
3093 date_time_format_calendar, x_obj, method_name),
3094 {});
3095
3096 // 7. Let y be ? HandleDateTimeValue(dateTimeFormat, y).
3097 DateTimeValueRecord y_record;
3099 isolate, y_record,
3100 HandleDateTimeValue(isolate, *icu_simple_date_format,
3101 date_time_format_calendar, y_obj, method_name),
3102 {});
3103
3104 std::unique_ptr<icu::DateIntervalFormat> format(
3105 LazyCreateDateIntervalFormat(isolate, date_time_format, x_record.kind));
3106 if (format.get() == nullptr) {
3107 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError));
3108 }
3109
3110 const icu::Calendar* calendar =
3111 date_time_format->icu_simple_date_format()->raw()->getCalendar();
3112
3113 std::optional<MaybeDirectHandle<T>> result = CallICUFormatRange<T, Format>(
3114 isolate, format.get(), calendar, x_record.epoch_milliseconds,
3115 y_record.epoch_milliseconds);
3116 if (result.has_value()) return *result;
3117 return Fallback(isolate, *icu_simple_date_format, x_record.kind,
3118 x_record.epoch_milliseconds);
3119}
3120
3121template <typename T,
3122 std::optional<MaybeDirectHandle<T>> (*Format)(
3123 Isolate*, const icu::FormattedValue&),
3124 MaybeDirectHandle<T> (*Fallback)(
3125 Isolate*, const icu::SimpleDateFormat&, double)>
3126MaybeDirectHandle<T> FormatRangeCommon(
3127 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format,
3128 DirectHandle<Object> x_obj, DirectHandle<Object> y_obj,
3129 const char* method_name) {
3130 // 4. Let x be ? ToNumber(startDate).
3131 ASSIGN_RETURN_ON_EXCEPTION(isolate, x_obj, Object::ToNumber(isolate, x_obj));
3132 double x = Object::NumberValue(*x_obj);
3133 // 5. Let y be ? ToNumber(endDate).
3134 ASSIGN_RETURN_ON_EXCEPTION(isolate, y_obj, Object::ToNumber(isolate, y_obj));
3135 double y = Object::NumberValue(*y_obj);
3136
3137 std::optional<MaybeDirectHandle<T>> result =
3138 PartitionDateTimeRangePattern<T, Format>(isolate, date_time_format, x, y,
3139 method_name);
3140 if (result.has_value()) return *result;
3141 return Fallback(isolate, *(date_time_format->icu_simple_date_format()->raw()),
3142 x);
3143}
3144
3145} // namespace
3146
3148 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format,
3149 DirectHandle<Object> x, DirectHandle<Object> y, const char* method_name) {
3150 // Track newer feature formatRange and formatRangeToParts.
3152 if (v8_flags.harmony_temporal) {
3153 // For Temporal enable support.
3154 return FormatRangeCommonWithTemporalSupport<
3155 String, FormattedToString, FormatMillisecondsByKindToString>(
3156 isolate, date_time_format, x, y, method_name);
3157 }
3158 // Pre Temporal implementation.
3159 return FormatRangeCommon<String, FormattedToString, FormatDateTime>(
3160 isolate, date_time_format, x, y, method_name);
3161}
3162
3164 Isolate* isolate, DirectHandle<JSDateTimeFormat> date_time_format,
3165 DirectHandle<Object> x, DirectHandle<Object> y, const char* method_name) {
3166 // Track newer feature formatRange and formatRangeToParts.
3168 if (v8_flags.harmony_temporal) {
3169 // For Temporal enable support.
3170 return FormatRangeCommonWithTemporalSupport<
3171 JSArray, FormattedDateIntervalToJSArray,
3172 FormatMillisecondsByKindToArrayOutputSource>(isolate, date_time_format,
3173 x, y, method_name);
3174 }
3175 // Pre Temporal implementation.
3176 return FormatRangeCommon<JSArray, FormattedDateIntervalToJSArray,
3177 FormatMillisecondsToArrayOutputSource>(
3178 isolate, date_time_format, x, y, method_name);
3179}
3180
3181} // namespace v8::internal
#define T
#define DEFINE_BIT_FIELDS(LIST_MACRO)
Definition bit-field.h:126
Builtins::Kind kind
Definition builtins.cc:40
SourcePosition pos
@ kDateTimeFormatRange
Definition v8-isolate.h:555
@ kDateTimeFormatDateTimeStyle
Definition v8-isolate.h:556
V8_INLINE T FromJust() const &
Definition v8-maybe.h:64
V8_INLINE bool IsNothing() const
Definition v8-maybe.h:35
static MaybeHandle< BigInt > Divide(Isolate *isolate, DirectHandle< BigInt > x, DirectHandle< BigInt > y)
Definition bigint.cc:454
static V8_EXPORT_PRIVATE Handle< BigInt > FromInt64(Isolate *isolate, int64_t n)
Definition bigint.cc:1333
static bool TryTimeClip(double *time)
Definition date.h:70
V8_INLINE bool is_null() const
Definition handles.h:693
Handle< Number > NewNumberFromInt(int32_t value)
Handle< String > NewStringFromAsciiChecked(const char *str, AllocationType allocation=AllocationType::kYoung)
DirectHandle< Number > NewNumberFromInt64(int64_t value)
Handle< JSArray > NewJSArray(ElementsKind elements_kind, int length, int capacity, ArrayStorageAllocationMode mode=ArrayStorageAllocationMode::DONT_INITIALIZE_ARRAY_ELEMENTS, AllocationType allocation=AllocationType::kYoung)
Definition factory.cc:3211
Handle< JSObject > NewJSObject(DirectHandle< JSFunction > constructor, AllocationType allocation=AllocationType::kYoung, NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
Definition factory.cc:2985
Handle< String > NewStringFromStaticChars(const char(&str)[N], AllocationType allocation=AllocationType::kYoung)
Definition factory-inl.h:79
FormatRangeSource GetSource(int32_t start, int32_t limit) const
static void AddElement(Isolate *isolate, DirectHandle< JSArray > array, int index, DirectHandle< String > field_type_string, DirectHandle< String > value)
static bool IsValidCalendar(const icu::Locale &locale, const std::string &value)
static std::string GetNumberingSystem(const icu::Locale &icu_locale)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< Object > LegacyUnwrapReceiver(Isolate *isolate, DirectHandle< JSReceiver > receiver, DirectHandle< JSFunction > constructor, bool has_initialized_slot)
static V8_WARN_UNUSED_RESULT MaybeHandle< String > ToString(Isolate *isolate, const icu::UnicodeString &string)
static Maybe< std::vector< std::string > > CanonicalizeLocaleList(Isolate *isolate, DirectHandle< Object > locales, bool only_return_one_result=false)
static const std::set< std::string > & GetAvailableLocalesForDateFormat()
static DirectHandle< String > DefaultTimeZone(Isolate *isolate)
static Maybe< ResolvedLocale > ResolveLocale(Isolate *isolate, const std::set< std::string > &available_locales, const std::vector< std::string > &requested_locales, MatcherOption options, const std::set< std::string > &relevant_extension_keys)
static V8_WARN_UNUSED_RESULT bool IsValidTimeZoneName(const icu::TimeZone &tz)
static V8_WARN_UNUSED_RESULT Maybe< MatcherOption > GetLocaleMatcher(Isolate *isolate, DirectHandle< JSReceiver > options, const char *method_name)
static bool IsWellFormedCalendar(const std::string &value)
static Maybe< std::string > ToLanguageTag(const icu::Locale &locale)
static bool IsValidNumberingSystem(const std::string &value)
static DirectHandle< String > SourceString(Isolate *isolate, FormatRangeSource source)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSDateTimeFormat > UnwrapDateTimeFormat(Isolate *isolate, Handle< JSReceiver > format_holder)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > FormatRange(Isolate *isolate, DirectHandle< JSDateTimeFormat > date_time_format, DirectHandle< Object > x_date_value, DirectHandle< Object > y_date_value, const char *method_name)
static DirectHandle< Object > TimeZoneId(Isolate *isolate, const icu::TimeZone &tz)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > DateTimeFormat(Isolate *isolate, DirectHandle< JSDateTimeFormat > date_time_format, DirectHandle< Object > date, const char *method_name)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSObject > ResolvedOptions(Isolate *isolate, DirectHandle< JSDateTimeFormat > date_time_format)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSArray > FormatRangeToParts(Isolate *isolate, DirectHandle< JSDateTimeFormat > date_time_format, DirectHandle< Object > x_date_value, DirectHandle< Object > y_date_value, const char *method_name)
static V8_EXPORT_PRIVATE const std::set< std::string > & GetAvailableLocales()
static V8_WARN_UNUSED_RESULT MaybeHandle< String > TimeZoneIdToString(Isolate *isolate, const icu::UnicodeString &id)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > ToLocaleDateTime(Isolate *isolate, DirectHandle< Object > date, DirectHandle< Object > locales, DirectHandle< Object > options, RequiredOption required, DefaultsOption defaults, const char *method_name)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSArray > FormatToParts(Isolate *isolate, DirectHandle< JSDateTimeFormat > date_time_format, DirectHandle< Object > x, bool output_source, const char *method_name)
static V8_WARN_UNUSED_RESULT DirectHandle< String > Calendar(Isolate *isolate, DirectHandle< JSDateTimeFormat > date_time_format)
static std::unique_ptr< icu::TimeZone > CreateTimeZone(Isolate *isolate, DirectHandle< String > time_zone)
static V8_WARN_UNUSED_RESULT DirectHandle< Object > TimeZone(Isolate *isolate, DirectHandle< JSDateTimeFormat > date_time_format)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSDateTimeFormat > CreateDateTimeFormat(Isolate *isolate, DirectHandle< Map > map, DirectHandle< Object > locales, DirectHandle< Object > options, RequiredOption required, DefaultsOption defaults, const char *service)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > TemporalToLocaleString(Isolate *isolate, DirectHandle< JSReceiver > temporal, DirectHandle< Object > locales, DirectHandle< Object > options, const char *method_name)
static V8_EXPORT_PRIVATE std::string CanonicalizeTimeZoneID(const std::string &input)
Handle< String > HourCycleAsString(Isolate *isolate) const
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSDateTimeFormat > New(Isolate *isolate, DirectHandle< Map > map, DirectHandle< Object > locales, DirectHandle< Object > options, const char *service)
static int64_t CurrentTimeValue(Isolate *isolate)
static V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT MaybeHandle< Map > GetDerivedMap(Isolate *isolate, DirectHandle< JSFunction > constructor, DirectHandle< JSReceiver > new_target)
static void ValidateElements(Tagged< JSObject > object)
static V8_WARN_UNUSED_RESULT Maybe< bool > CreateDataProperty(Isolate *isolate, DirectHandle< JSReceiver > object, DirectHandle< Name > key, DirectHandle< Object > value, Maybe< ShouldThrow > should_throw)
static DirectHandle< Managed< CppType > > From(Isolate *isolate, size_t estimated_size, std::shared_ptr< CppType > shared_ptr, AllocationType allocation_type=AllocationType::kYoung)
Definition managed-inl.h:27
static V8_WARN_UNUSED_RESULT HandleType< String >::MaybeType ToString(Isolate *isolate, HandleType< T > input)
static V8_WARN_UNUSED_RESULT MaybeHandle< Object > GetPropertyOrElement(Isolate *isolate, DirectHandle< JSAny > object, DirectHandle< Name > name)
static V8_WARN_UNUSED_RESULT HandleType< Number >::MaybeType ToNumber(Isolate *isolate, HandleType< T > input)
static double NumberValue(Tagged< Number > obj)
base::uc16 Get(uint32_t i) const
Definition string.h:150
static V8_INLINE HandleType< String > Flatten(Isolate *isolate, HandleType< T > string, AllocationType allocation=AllocationType::kYoung)
bool Equals(Tagged< String > other) const
Definition string-inl.h:535
static V8_EXPORT_PRIVATE void FatalProcessOutOfMemory(Isolate *isolate, const char *location, const OOMDetails &details=kNoOOMDetails)
base::Mutex & mutex_
const MapRef map_
int start
int end
#define THROW_NEW_ERROR_RETURN_VALUE(isolate, call, value)
Definition isolate.h:300
#define ASSIGN_RETURN_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:291
#define THROW_NEW_ERROR(isolate, call)
Definition isolate.h:307
#define ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, value)
Definition isolate.h:276
#define MAYBE_RETURN(call, value)
Definition isolate.h:408
#define MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, value)
Definition isolate.h:440
Isolate * isolate
std::vector< PatternMap > pairs
std::map< const std::string, const std::string > map
#define DEFFINE_TRAIT(name, d1, d2)
std::string pattern
int32_t bitShift
double epoch_milliseconds
std::vector< const char * > allowed_values
#define BIT_FIELDS(V, _)
icu::number::FormattedNumber formatted
JSDurationFormat::FieldStyle style
DirectHandle< JSReceiver > options
TimeZoneRecord time_zone
DirectHandle< Object > calendar
DurationRecord record
DateRecord date
ZoneVector< RpoNumber > & result
#define LAZY_INSTANCE_INITIALIZER
int x
Point to
int r
Definition mul-fft.cc:298
STL namespace.
int int32_t
Definition unicode.cc:40
constexpr bool IsInRange(T value, U lower_limit, U higher_limit)
Definition bounds.h:20
LockGuard< Mutex > MutexGuard
Definition mutex.h:219
void Add(RWDigits Z, Digits X, Digits Y)
V8_INLINE const Operation & Get(const Graph &graph, OpIndex index)
Definition graph.h:1231
MaybeDirectHandle< JSTemporalTimeZone > CreateTemporalTimeZone(Isolate *isolate, DirectHandle< String > identifier)
MaybeDirectHandle< JSTemporalPlainDateTime > CreateTemporalDateTime(Isolate *isolate, const DateTimeRecord &date_time, DirectHandle< JSReceiver > calendar)
MaybeDirectHandle< JSTemporalInstant > CreateTemporalInstant(Isolate *isolate, DirectHandle< JSFunction > target, DirectHandle< HeapObject > new_target, DirectHandle< BigInt > epoch_nanoseconds)
MaybeDirectHandle< JSTemporalCalendar > GetBuiltinCalendar(Isolate *isolate, DirectHandle< String > id)
MaybeDirectHandle< JSTemporalInstant > BuiltinTimeZoneGetInstantForCompatible(Isolate *isolate, DirectHandle< JSReceiver > time_zone, DirectHandle< JSTemporalPlainDateTime > date_time, const char *method_name)
DirectHandle< JSTemporalCalendar > GetISO8601Calendar(Isolate *isolate)
constexpr int H
MaybeDirectHandle< String > FormattedToString(Isolate *isolate, const icu::FormattedValue &formatted, const std::vector< std::vector< Part > > *parts, JSDurationFormat::Separator)
bool IsNumber(Tagged< Object > obj)
Tagged(T object) -> Tagged< T >
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
V8_EXPORT_PRIVATE FlagValues v8_flags
V8_WARN_UNUSED_RESULT Maybe< bool > GetBoolOption(Isolate *isolate, DirectHandle< JSReceiver > options, const char *property, const char *method_name, bool *result)
Maybe< int > GetNumberOption(Isolate *isolate, DirectHandle< JSReceiver > options, DirectHandle< String > property, int min, int max, int fallback)
MaybeDirectHandle< JSReceiver > CoerceOptionsToObject(Isolate *isolate, DirectHandle< Object > options, const char *method_name)
std::optional< std::string > GetOffsetTimeZone(Isolate *isolate, DirectHandle< String > time_zone)
Maybe< bool > GetStringOption(Isolate *isolate, DirectHandle< JSReceiver > options, const char *property, const std::vector< const char * > &values, const char *method_name, std::unique_ptr< char[]> *result)
V8_EXPORT_PRIVATE void V8_EXPORT_PRIVATE void const char * format
Definition utils.h:715
!IsContextMap !IsContextMap native_context
Definition map-inl.h:877
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
Local< T > Handle
Maybe< T > Nothing()
Definition v8-maybe.h:112
Maybe< T > Just(const T &t)
Definition v8-maybe.h:117
uint32_t substring
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define USE(...)
Definition macros.h:293
typename LazyStaticInstance< T, CreateTrait, InitOnceTrait, DestroyTrait >::type type