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