[Intl] Implement Intl.DateTimeFormat.prototype.formatRangeToParts
Design Doc: https://goo.gl/PGUQ1d Use template to share code between formatRange and formatRangeToParts Lazy crate DateIntervalFormat inside formatRange/formatRangeToParts to reduce performance impact. Bug: v8:7729 Change-Id: I130748a5ff7ca11235e6608195d365e58d440580 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1556573 Commit-Queue: Frank Tang <ftang@chromium.org> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org> Cr-Commit-Position: refs/heads/master@{#60930}
This commit is contained in:
parent
a8c73a4865
commit
8034b0568b
@ -19,6 +19,7 @@
|
||||
V(_, day_string, "day") \
|
||||
V(_, dayPeriod_string, "dayPeriod") \
|
||||
V(_, decimal_string, "decimal") \
|
||||
V(_, endRange_string, "endRange") \
|
||||
V(_, era_string, "era") \
|
||||
V(_, first_string, "first") \
|
||||
V(_, format_string, "format") \
|
||||
@ -72,6 +73,8 @@
|
||||
V(_, SegmentIterator_string, "Segment Iterator") \
|
||||
V(_, sensitivity_string, "sensitivity") \
|
||||
V(_, sep_string, "sep") \
|
||||
V(_, shared_string, "shared") \
|
||||
V(_, startRange_string, "startRange") \
|
||||
V(_, strict_string, "strict") \
|
||||
V(_, style_string, "style") \
|
||||
V(_, term_string, "term") \
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "unicode/coll.h"
|
||||
#include "unicode/datefmt.h"
|
||||
#include "unicode/decimfmt.h"
|
||||
#include "unicode/formattedvalue.h"
|
||||
#include "unicode/locid.h"
|
||||
#include "unicode/normalizer2.h"
|
||||
#include "unicode/numfmt.h"
|
||||
@ -1934,5 +1935,16 @@ Handle<String> Intl::NumberFieldToType(Isolate* isolate,
|
||||
}
|
||||
}
|
||||
|
||||
// A helper function to convert the FormattedValue for several Intl objects.
|
||||
MaybeHandle<String> Intl::FormattedToString(
|
||||
Isolate* isolate, const icu::FormattedValue& formatted) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::UnicodeString result = formatted.toString(status);
|
||||
if (U_FAILURE(status)) {
|
||||
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), String);
|
||||
}
|
||||
return Intl::ToString(isolate, result);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -26,6 +26,7 @@ namespace U_ICU_NAMESPACE {
|
||||
class BreakIterator;
|
||||
class Collator;
|
||||
class DecimalFormat;
|
||||
class FormattedValue;
|
||||
class SimpleDateFormat;
|
||||
class UnicodeString;
|
||||
}
|
||||
@ -186,6 +187,10 @@ class Intl {
|
||||
Isolate* isolate, const icu::UnicodeString& string, int32_t begin,
|
||||
int32_t end);
|
||||
|
||||
// Helper function to convert a FormattedValue to String
|
||||
V8_WARN_UNUSED_RESULT static MaybeHandle<String> FormattedToString(
|
||||
Isolate* isolate, const icu::FormattedValue& formatted);
|
||||
|
||||
// Helper function to convert number field id to type string.
|
||||
static Handle<String> NumberFieldToType(Isolate* isolate,
|
||||
Handle<Object> numeric_obj,
|
||||
|
@ -959,14 +959,41 @@ std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormatFromCache(
|
||||
cache.Pointer()->Create(icu_locale, skeleton, generator));
|
||||
}
|
||||
|
||||
std::unique_ptr<icu::DateIntervalFormat> CreateICUDateIntervalFormat(
|
||||
const icu::Locale& icu_locale, const icu::UnicodeString& skeleton) {
|
||||
icu::UnicodeString SkeletonFromDateFormat(
|
||||
const icu::SimpleDateFormat& icu_date_format) {
|
||||
icu::UnicodeString pattern;
|
||||
pattern = icu_date_format.toPattern(pattern);
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::UnicodeString skeleton =
|
||||
icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
return skeleton;
|
||||
}
|
||||
|
||||
icu::DateIntervalFormat* LazyCreateDateIntervalFormat(
|
||||
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format) {
|
||||
Managed<icu::DateIntervalFormat> managed_format =
|
||||
date_time_format->icu_date_interval_format();
|
||||
if (managed_format->get()) {
|
||||
return managed_format->raw();
|
||||
}
|
||||
icu::SimpleDateFormat* icu_simple_date_format =
|
||||
date_time_format->icu_simple_date_format()->raw();
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
std::unique_ptr<icu::DateIntervalFormat> date_interval_format(
|
||||
icu::DateIntervalFormat::createInstance(skeleton, icu_locale, status));
|
||||
if (U_FAILURE(status)) return std::unique_ptr<icu::DateIntervalFormat>();
|
||||
CHECK_NOT_NULL(date_interval_format.get());
|
||||
return date_interval_format;
|
||||
icu::DateIntervalFormat::createInstance(
|
||||
SkeletonFromDateFormat(*icu_simple_date_format),
|
||||
*(date_time_format->icu_locale()->raw()), status));
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
date_interval_format->setTimeZone(icu_simple_date_format->getTimeZone());
|
||||
Handle<Managed<icu::DateIntervalFormat>> managed_interval_format =
|
||||
Managed<icu::DateIntervalFormat>::FromUniquePtr(
|
||||
isolate, 0, std::move(date_interval_format));
|
||||
date_time_format->set_icu_date_interval_format(*managed_interval_format);
|
||||
return (*managed_interval_format)->raw();
|
||||
}
|
||||
|
||||
Intl::HourCycle HourCycleFromPattern(const icu::UnicodeString pattern) {
|
||||
@ -1103,18 +1130,6 @@ std::unique_ptr<icu::SimpleDateFormat> DateTimeStylePattern(
|
||||
generator);
|
||||
}
|
||||
|
||||
icu::UnicodeString SkeletonFromDateFormat(
|
||||
const icu::SimpleDateFormat& icu_date_format) {
|
||||
icu::UnicodeString pattern;
|
||||
pattern = icu_date_format.toPattern(pattern);
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::UnicodeString skeleton =
|
||||
icu::DateTimePatternGenerator::staticGetSkeleton(pattern, status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
return skeleton;
|
||||
}
|
||||
|
||||
class DateTimePatternGeneratorCache {
|
||||
public:
|
||||
// Return a clone copy that the caller have to free.
|
||||
@ -1297,7 +1312,6 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
|
||||
DateTimeStyle date_style = DateTimeStyle::kUndefined;
|
||||
DateTimeStyle time_style = DateTimeStyle::kUndefined;
|
||||
std::unique_ptr<icu::SimpleDateFormat> icu_date_format;
|
||||
std::unique_ptr<icu::DateIntervalFormat> icu_date_interval_format;
|
||||
|
||||
if (FLAG_harmony_intl_datetime_style) {
|
||||
// 28. Let dateStyle be ? GetOption(options, "dateStyle", "string", «
|
||||
@ -1340,10 +1354,6 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
|
||||
time_style != DateTimeStyle::kUndefined) {
|
||||
icu_date_format = DateTimeStylePattern(date_style, time_style, icu_locale,
|
||||
hc, *generator);
|
||||
if (FLAG_harmony_intl_date_format_range) {
|
||||
icu_date_interval_format = CreateICUDateIntervalFormat(
|
||||
icu_locale, SkeletonFromDateFormat(*icu_date_format));
|
||||
}
|
||||
}
|
||||
}
|
||||
// 33. Else,
|
||||
@ -1397,10 +1407,6 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
|
||||
FATAL("Failed to create ICU date format, are ICU data files missing?");
|
||||
}
|
||||
}
|
||||
if (FLAG_harmony_intl_date_format_range) {
|
||||
icu_date_interval_format =
|
||||
CreateICUDateIntervalFormat(icu_locale, skeleton_ustr);
|
||||
}
|
||||
|
||||
// g. If dateTimeFormat.[[Hour]] is not undefined, then
|
||||
if (!has_hour_option) {
|
||||
@ -1449,12 +1455,10 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
|
||||
Managed<icu::SimpleDateFormat>::FromUniquePtr(isolate, 0,
|
||||
std::move(icu_date_format));
|
||||
date_time_format->set_icu_simple_date_format(*managed_format);
|
||||
if (FLAG_harmony_intl_date_format_range) {
|
||||
Handle<Managed<icu::DateIntervalFormat>> managed_interval_format =
|
||||
Managed<icu::DateIntervalFormat>::FromUniquePtr(
|
||||
isolate, 0, std::move(icu_date_interval_format));
|
||||
date_time_format->set_icu_date_interval_format(*managed_interval_format);
|
||||
}
|
||||
|
||||
Handle<Managed<icu::DateIntervalFormat>> managed_interval_format =
|
||||
Managed<icu::DateIntervalFormat>::FromRawPtr(isolate, 0, nullptr);
|
||||
date_time_format->set_icu_date_interval_format(*managed_interval_format);
|
||||
|
||||
return date_time_format;
|
||||
}
|
||||
@ -1591,75 +1595,176 @@ Handle<String> JSDateTimeFormat::HourCycleAsString() const {
|
||||
}
|
||||
}
|
||||
|
||||
MaybeHandle<String> JSDateTimeFormat::FormatRange(
|
||||
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, double x,
|
||||
double y) {
|
||||
// TODO(ftang): Merge the following with FormatRangeToParts after
|
||||
// the landing of ICU64 to make it cleaner.
|
||||
enum Source { kShared, kStartRange, kEndRange };
|
||||
|
||||
namespace {
|
||||
|
||||
class SourceTracker {
|
||||
public:
|
||||
SourceTracker() { start_[0] = start_[1] = limit_[0] = limit_[1] = 0; }
|
||||
void Add(int32_t field, int32_t start, int32_t limit) {
|
||||
CHECK_LT(field, 2);
|
||||
start_[field] = start;
|
||||
limit_[field] = limit;
|
||||
}
|
||||
|
||||
Source GetSource(int32_t start, int32_t limit) const {
|
||||
Source source = Source::kShared;
|
||||
if (FieldContains(0, start, limit)) {
|
||||
source = Source::kStartRange;
|
||||
} else if (FieldContains(1, start, limit)) {
|
||||
source = Source::kEndRange;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t start_[2];
|
||||
int32_t limit_[2];
|
||||
|
||||
bool FieldContains(int32_t field, int32_t start, int32_t limit) const {
|
||||
CHECK_LT(field, 2);
|
||||
return (start_[field] <= start) && (start <= limit_[field]) &&
|
||||
(start_[field] <= limit) && (limit <= limit_[field]);
|
||||
}
|
||||
};
|
||||
|
||||
Handle<String> SourceString(Isolate* isolate, Source source) {
|
||||
switch (source) {
|
||||
case Source::kShared:
|
||||
return ReadOnlyRoots(isolate).shared_string_handle();
|
||||
case Source::kStartRange:
|
||||
return ReadOnlyRoots(isolate).startRange_string_handle();
|
||||
case Source::kEndRange:
|
||||
return ReadOnlyRoots(isolate).endRange_string_handle();
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<bool> AddPartForFormatRange(Isolate* isolate, Handle<JSArray> array,
|
||||
const icu::UnicodeString& string,
|
||||
int32_t index, int32_t field, int32_t start,
|
||||
int32_t end, const SourceTracker& tracker) {
|
||||
Handle<String> substring;
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, substring,
|
||||
Intl::ToString(isolate, string, start, end),
|
||||
Nothing<bool>());
|
||||
Intl::AddElement(isolate, array, index,
|
||||
IcuDateFieldIdToDateType(field, isolate), substring,
|
||||
isolate->factory()->source_string(),
|
||||
SourceString(isolate, tracker.GetSource(start, end)));
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
// A helper function to convert the FormattedDateInterval to a
|
||||
// MaybeHandle<JSArray> for the implementation of formatRangeToParts.
|
||||
MaybeHandle<JSArray> FormattedDateIntervalToJSArray(
|
||||
Isolate* isolate, const icu::FormattedValue& formatted) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::UnicodeString result = formatted.toString(status);
|
||||
|
||||
Factory* factory = isolate->factory();
|
||||
Handle<JSArray> array = factory->NewJSArray(0);
|
||||
icu::ConstrainedFieldPosition cfpos;
|
||||
int index = 0;
|
||||
int32_t previous_end_pos = 0;
|
||||
SourceTracker tracker;
|
||||
while (formatted.nextPosition(cfpos, status)) {
|
||||
int32_t category = cfpos.getCategory();
|
||||
int32_t field = cfpos.getField();
|
||||
int32_t start = cfpos.getStart();
|
||||
int32_t limit = cfpos.getLimit();
|
||||
|
||||
if (category == UFIELD_CATEGORY_DATE_INTERVAL_SPAN) {
|
||||
CHECK_LE(field, 2);
|
||||
tracker.Add(field, start, limit);
|
||||
} else {
|
||||
CHECK(category == UFIELD_CATEGORY_DATE);
|
||||
if (start > previous_end_pos) {
|
||||
// Add "literal" from the previous end position to the start if
|
||||
// necessary.
|
||||
Maybe<bool> maybe_added =
|
||||
AddPartForFormatRange(isolate, array, result, index, -1,
|
||||
previous_end_pos, start, tracker);
|
||||
MAYBE_RETURN(maybe_added, Handle<JSArray>());
|
||||
previous_end_pos = start;
|
||||
index++;
|
||||
}
|
||||
Maybe<bool> maybe_added = AddPartForFormatRange(
|
||||
isolate, array, result, index, field, start, limit, tracker);
|
||||
MAYBE_RETURN(maybe_added, Handle<JSArray>());
|
||||
previous_end_pos = limit;
|
||||
++index;
|
||||
}
|
||||
}
|
||||
int32_t end = result.length();
|
||||
// Add "literal" in the end if necessary.
|
||||
if (end > previous_end_pos) {
|
||||
Maybe<bool> maybe_added = AddPartForFormatRange(
|
||||
isolate, array, result, index, -1, previous_end_pos, end, tracker);
|
||||
MAYBE_RETURN(maybe_added, Handle<JSArray>());
|
||||
}
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), JSArray);
|
||||
}
|
||||
|
||||
JSObject::ValidateElements(*array);
|
||||
return array;
|
||||
}
|
||||
|
||||
// The shared code between formatRange and formatRangeToParts
|
||||
template <typename T>
|
||||
MaybeHandle<T> FormatRangeCommon(
|
||||
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, double x,
|
||||
double y,
|
||||
MaybeHandle<T> (*formatToResult)(Isolate*, const icu::FormattedValue&)) {
|
||||
// #sec-partitiondatetimerangepattern
|
||||
// 1. Let x be TimeClip(x).
|
||||
x = DateCache::TimeClip(x);
|
||||
// 2. If x is NaN, throw a RangeError exception.
|
||||
if (std::isnan(x)) {
|
||||
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue),
|
||||
String);
|
||||
T);
|
||||
}
|
||||
// 3. Let y be TimeClip(y).
|
||||
y = DateCache::TimeClip(y);
|
||||
// 4. If y is NaN, throw a RangeError exception.
|
||||
if (std::isnan(y)) {
|
||||
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue),
|
||||
String);
|
||||
T);
|
||||
}
|
||||
|
||||
icu::DateIntervalFormat* date_interval_format =
|
||||
date_time_format->icu_date_interval_format()->raw();
|
||||
CHECK_NOT_NULL(date_interval_format);
|
||||
icu::DateInterval interval(x, y);
|
||||
|
||||
icu::UnicodeString result;
|
||||
icu::FieldPosition fpos;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
date_interval_format->format(&interval, result, fpos, status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
icu::DateIntervalFormat* format =
|
||||
LazyCreateDateIntervalFormat(isolate, date_time_format);
|
||||
if (format == nullptr) {
|
||||
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), T);
|
||||
}
|
||||
|
||||
return Intl::ToString(isolate, result);
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::FormattedDateInterval formatted =
|
||||
format->formatToValue(interval, status);
|
||||
if (U_FAILURE(status)) {
|
||||
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), T);
|
||||
}
|
||||
return formatToResult(isolate, formatted);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MaybeHandle<String> JSDateTimeFormat::FormatRange(
|
||||
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, double x,
|
||||
double y) {
|
||||
return FormatRangeCommon<String>(isolate, date_time_format, x, y,
|
||||
Intl::FormattedToString);
|
||||
}
|
||||
|
||||
MaybeHandle<JSArray> JSDateTimeFormat::FormatRangeToParts(
|
||||
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, double x,
|
||||
double y) {
|
||||
// TODO(ftang): Merge the following with FormatRangeToParts after
|
||||
// the landing of ICU64 to make it cleaner.
|
||||
|
||||
// #sec-partitiondatetimerangepattern
|
||||
// 1. Let x be TimeClip(x).
|
||||
x = DateCache::TimeClip(x);
|
||||
// 2. If x is NaN, throw a RangeError exception.
|
||||
if (std::isnan(x)) {
|
||||
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue),
|
||||
JSArray);
|
||||
}
|
||||
// 3. Let y be TimeClip(y).
|
||||
y = DateCache::TimeClip(y);
|
||||
// 4. If y is NaN, throw a RangeError exception.
|
||||
if (std::isnan(y)) {
|
||||
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kInvalidTimeValue),
|
||||
JSArray);
|
||||
}
|
||||
|
||||
icu::DateIntervalFormat* date_interval_format =
|
||||
date_time_format->icu_date_interval_format()->raw();
|
||||
CHECK_NOT_NULL(date_interval_format);
|
||||
Factory* factory = isolate->factory();
|
||||
Handle<JSArray> result = factory->NewJSArray(0);
|
||||
|
||||
// TODO(ftang) To be implemented after ICU64 landed that support
|
||||
// DateIntervalFormat::formatToValue() and FormattedDateInterval.
|
||||
|
||||
JSObject::ValidateElements(*result);
|
||||
return result;
|
||||
return FormatRangeCommon<JSArray>(isolate, date_time_format, x, y,
|
||||
FormattedDateIntervalToJSArray);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -296,7 +296,7 @@ Maybe<std::vector<icu::UnicodeString>> ToUnicodeStringArray(
|
||||
template <typename T>
|
||||
MaybeHandle<T> FormatListCommon(
|
||||
Isolate* isolate, Handle<JSListFormat> format, Handle<JSArray> list,
|
||||
MaybeHandle<T> (*formatToResult)(Isolate*, const icu::FormattedList&)) {
|
||||
MaybeHandle<T> (*formatToResult)(Isolate*, const icu::FormattedValue&)) {
|
||||
DCHECK(!list->IsUndefined());
|
||||
// ecma402 #sec-createpartsfromlist
|
||||
// 2. If list contains any element value such that Type(value) is not String,
|
||||
@ -318,18 +318,6 @@ MaybeHandle<T> FormatListCommon(
|
||||
return formatToResult(isolate, formatted);
|
||||
}
|
||||
|
||||
// A helper function to convert the FormattedList to a
|
||||
// MaybeHandle<String> for the implementation of format.
|
||||
MaybeHandle<String> FormattedToString(Isolate* isolate,
|
||||
const icu::FormattedList& formatted) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::UnicodeString result = formatted.toString(status);
|
||||
if (U_FAILURE(status)) {
|
||||
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), String);
|
||||
}
|
||||
return Intl::ToString(isolate, result);
|
||||
}
|
||||
|
||||
Handle<String> IcuFieldIdToType(Isolate* isolate, int32_t field_id) {
|
||||
switch (field_id) {
|
||||
case ULISTFMT_LITERAL_FIELD:
|
||||
@ -345,8 +333,8 @@ Handle<String> IcuFieldIdToType(Isolate* isolate, int32_t field_id) {
|
||||
|
||||
// A helper function to convert the FormattedList to a
|
||||
// MaybeHandle<JSArray> for the implementation of formatToParts.
|
||||
MaybeHandle<JSArray> FormattedToJSArray(Isolate* isolate,
|
||||
const icu::FormattedList& formatted) {
|
||||
MaybeHandle<JSArray> FormattedListToJSArray(
|
||||
Isolate* isolate, const icu::FormattedValue& formatted) {
|
||||
Handle<JSArray> array = isolate->factory()->NewJSArray(0);
|
||||
icu::ConstrainedFieldPosition cfpos;
|
||||
cfpos.constrainCategory(UFIELD_CATEGORY_LIST);
|
||||
@ -375,13 +363,15 @@ MaybeHandle<JSArray> FormattedToJSArray(Isolate* isolate,
|
||||
MaybeHandle<String> JSListFormat::FormatList(Isolate* isolate,
|
||||
Handle<JSListFormat> format,
|
||||
Handle<JSArray> list) {
|
||||
return FormatListCommon<String>(isolate, format, list, FormattedToString);
|
||||
return FormatListCommon<String>(isolate, format, list,
|
||||
Intl::FormattedToString);
|
||||
}
|
||||
|
||||
// ecma42 #sec-formatlisttoparts
|
||||
MaybeHandle<JSArray> JSListFormat::FormatListToParts(
|
||||
Isolate* isolate, Handle<JSListFormat> format, Handle<JSArray> list) {
|
||||
return FormatListCommon<JSArray>(isolate, format, list, FormattedToJSArray);
|
||||
return FormatListCommon<JSArray>(isolate, format, list,
|
||||
FormattedListToJSArray);
|
||||
}
|
||||
|
||||
const std::set<std::string>& JSListFormat::GetAvailableLocales() {
|
||||
|
49
test/intl/date-format/en-format-range-to-parts.js
Normal file
49
test/intl/date-format/en-format-range-to-parts.js
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2019 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-intl-date-format-range
|
||||
|
||||
const date1 = new Date("2019-01-03T03:20");
|
||||
const date2 = new Date("2019-01-05T19:33");
|
||||
const date3 = new Date("2019-01-05T22:57");
|
||||
|
||||
// value: "Jan 3 – 5, 2019"
|
||||
// source: hhhhShhhEhhhhhh
|
||||
// type: mmmldllldllyyyy
|
||||
// h: Shared, S: startRange, E: endRange
|
||||
// m: month, l: literal, d: day, y: year
|
||||
const expected1 = [
|
||||
{type: "month", value: "Jan", source: "shared"},
|
||||
{type: "literal", value: " ", source: "shared"},
|
||||
{type: "day", value: "3", source: "startRange"},
|
||||
{type: "literal", value: " – ", source: "shared"},
|
||||
{type: "day", value: "5", source: "endRange"},
|
||||
{type: "literal", value: ", ", source: "shared"},
|
||||
{type: "year", value: "2019", source: "shared"}
|
||||
];
|
||||
|
||||
var dtf = new Intl.DateTimeFormat(["en"], {year: "numeric", month: "short", day: "numeric"});
|
||||
const ret1 = dtf.formatRangeToParts(date1, date2);
|
||||
assertEquals(expected1, ret1);
|
||||
|
||||
// value: "Jan 5, 7 – 10 PM"
|
||||
// source: hhhhhhhShhhEEhhh
|
||||
// type: mmmldlldlllhhlpp
|
||||
// h: Shared, S: startRange, E: endRange
|
||||
// m: month, l: literal, d: day, h: hour, p: dayPeriod
|
||||
|
||||
const expected2 = [
|
||||
{type: "month", value: "Jan", source: "shared"},
|
||||
{type: "literal", value: " ", source: "shared"},
|
||||
{type: "day", value: "5", source: "shared"},
|
||||
{type: "literal", value: ", ", source: "shared"},
|
||||
{type: "hour", value: "7", source: "startRange"},
|
||||
{type: "literal", value: " – ", source: "shared"},
|
||||
{type: "hour", value: "10", source: "endRange"},
|
||||
{type: "literal", value: " ", source: "shared"},
|
||||
{type: "dayPeriod", value: "PM", source: "shared"}
|
||||
];
|
||||
dtf = new Intl.DateTimeFormat(["en"], {month: "short", day: "numeric", hour: "numeric"});
|
||||
const ret2 = dtf.formatRangeToParts(date2, date3);
|
||||
assertEquals(expected2, ret2);
|
@ -11,8 +11,10 @@ assertFalse(descriptor.enumerable);
|
||||
assertTrue(descriptor.configurable);
|
||||
|
||||
const date1 = new Date("2019-1-3");
|
||||
const date2 = new Date("2019-3-4");
|
||||
const dtf = new Intl.DateTimeFormat();
|
||||
const date2 = new Date("2019-1-5");
|
||||
const date3 = new Date("2019-3-4");
|
||||
const date4 = new Date("2020-3-4");
|
||||
let dtf = new Intl.DateTimeFormat();
|
||||
assertThrows(() => dtf.formatRangeToParts(), RangeError);
|
||||
assertThrows(() => dtf.formatRangeToParts(date1), RangeError);
|
||||
assertThrows(() => dtf.formatRangeToParts(undefined, date2), RangeError);
|
||||
@ -22,3 +24,60 @@ assertThrows(() => dtf.formatRangeToParts(date1, "2019-5-4"), RangeError);
|
||||
assertThrows(() => dtf.formatRangeToParts(date2, date1), RangeError);
|
||||
|
||||
assertDoesNotThrow(() =>dtf.formatRangeToParts(date1, date2));
|
||||
|
||||
function partsToString(parts) {
|
||||
return parts.map(x => x.value).join("");
|
||||
}
|
||||
|
||||
const validSources = ["startRange", "endRange", "shared"];
|
||||
const validTypes = ["literal", "year", "month", "day", "hour", "minute", "second",
|
||||
"weekday", "dayPeriod", "timeZoneName", "era"];
|
||||
|
||||
function assertParts(parts) {
|
||||
const str = partsToString(parts);
|
||||
parts.forEach(function(part) {
|
||||
// Check the range of part.source
|
||||
assertTrue(validSources.includes(part.source),
|
||||
"Invalid source '" + part.source + "' in '" + str + "' for '" + part.value + "'");
|
||||
// Check the range of part.type
|
||||
assertTrue(validTypes.includes(part.type),
|
||||
"Invalid type '" + part.type + "' in '" + str + "' for '" + part.value + "'");
|
||||
// Check the part.value is a string
|
||||
assertEquals("string", typeof part.value, "Invalid value for '" + str + "'");
|
||||
});
|
||||
}
|
||||
|
||||
function verifyFormatRangeToParts(a, b, dtf) {
|
||||
var parts = dtf.formatRangeToParts(a, b);
|
||||
// Check each parts fulfill basic property of the parts.
|
||||
assertParts(parts);
|
||||
// ensure the 'value' in the parts is the same as the output of
|
||||
// the formatRange.
|
||||
assertEquals(dtf.formatRange(a, b), partsToString(parts));
|
||||
}
|
||||
|
||||
verifyFormatRangeToParts(date1, date2, dtf);
|
||||
verifyFormatRangeToParts(date1, date3, dtf);
|
||||
verifyFormatRangeToParts(date1, date4, dtf);
|
||||
verifyFormatRangeToParts(date2, date3, dtf);
|
||||
verifyFormatRangeToParts(date2, date4, dtf);
|
||||
verifyFormatRangeToParts(date3, date4, dtf);
|
||||
|
||||
dtf = new Intl.DateTimeFormat(["en"], {year: "numeric", month: "short", day: "numeric"});
|
||||
|
||||
verifyFormatRangeToParts(date1, date2, dtf);
|
||||
verifyFormatRangeToParts(date1, date3, dtf);
|
||||
verifyFormatRangeToParts(date1, date4, dtf);
|
||||
verifyFormatRangeToParts(date2, date3, dtf);
|
||||
verifyFormatRangeToParts(date2, date4, dtf);
|
||||
verifyFormatRangeToParts(date3, date4, dtf);
|
||||
|
||||
// Test the sequence of ToNumber and TimeClip
|
||||
var secondDateAccessed = false;
|
||||
assertThrows(
|
||||
() =>
|
||||
dtf.formatRangeToParts(
|
||||
new Date(864000000*10000000 + 1), // a date will cause TimeClip return NaN
|
||||
{ get [Symbol.toPrimitive]() { secondDateAccessed = true; return {}} }),
|
||||
TypeError);
|
||||
assertTrue(secondDateAccessed);
|
||||
|
@ -304,47 +304,47 @@ KNOWN_MAPS = {
|
||||
("read_only_space", 0x026e1): (98, "EnumCacheMap"),
|
||||
("read_only_space", 0x02781): (114, "ArrayBoilerplateDescriptionMap"),
|
||||
("read_only_space", 0x02ad1): (101, "InterceptorInfoMap"),
|
||||
("read_only_space", 0x050b9): (89, "AccessCheckInfoMap"),
|
||||
("read_only_space", 0x05109): (90, "AccessorInfoMap"),
|
||||
("read_only_space", 0x05159): (91, "AccessorPairMap"),
|
||||
("read_only_space", 0x051a9): (92, "AliasedArgumentsEntryMap"),
|
||||
("read_only_space", 0x051f9): (93, "AllocationMementoMap"),
|
||||
("read_only_space", 0x05249): (94, "AsmWasmDataMap"),
|
||||
("read_only_space", 0x05299): (95, "AsyncGeneratorRequestMap"),
|
||||
("read_only_space", 0x052e9): (96, "ClassPositionsMap"),
|
||||
("read_only_space", 0x05339): (97, "DebugInfoMap"),
|
||||
("read_only_space", 0x05389): (99, "FunctionTemplateInfoMap"),
|
||||
("read_only_space", 0x053d9): (100, "FunctionTemplateRareDataMap"),
|
||||
("read_only_space", 0x05429): (102, "InterpreterDataMap"),
|
||||
("read_only_space", 0x05479): (103, "ModuleInfoEntryMap"),
|
||||
("read_only_space", 0x054c9): (104, "ModuleMap"),
|
||||
("read_only_space", 0x05519): (105, "ObjectTemplateInfoMap"),
|
||||
("read_only_space", 0x05569): (106, "PromiseCapabilityMap"),
|
||||
("read_only_space", 0x055b9): (107, "PromiseReactionMap"),
|
||||
("read_only_space", 0x05609): (108, "PrototypeInfoMap"),
|
||||
("read_only_space", 0x05659): (109, "ScriptMap"),
|
||||
("read_only_space", 0x056a9): (110, "StackFrameInfoMap"),
|
||||
("read_only_space", 0x056f9): (111, "StackTraceFrameMap"),
|
||||
("read_only_space", 0x05749): (112, "Tuple2Map"),
|
||||
("read_only_space", 0x05799): (113, "Tuple3Map"),
|
||||
("read_only_space", 0x057e9): (115, "WasmDebugInfoMap"),
|
||||
("read_only_space", 0x05839): (116, "WasmExceptionTagMap"),
|
||||
("read_only_space", 0x05889): (117, "WasmExportedFunctionDataMap"),
|
||||
("read_only_space", 0x058d9): (118, "CallableTaskMap"),
|
||||
("read_only_space", 0x05929): (119, "CallbackTaskMap"),
|
||||
("read_only_space", 0x05979): (120, "PromiseFulfillReactionJobTaskMap"),
|
||||
("read_only_space", 0x059c9): (121, "PromiseRejectReactionJobTaskMap"),
|
||||
("read_only_space", 0x05a19): (122, "PromiseResolveThenableJobTaskMap"),
|
||||
("read_only_space", 0x05a69): (123, "FinalizationGroupCleanupJobTaskMap"),
|
||||
("read_only_space", 0x05ab9): (124, "AllocationSiteWithWeakNextMap"),
|
||||
("read_only_space", 0x05b09): (124, "AllocationSiteWithoutWeakNextMap"),
|
||||
("read_only_space", 0x05b59): (159, "LoadHandler1Map"),
|
||||
("read_only_space", 0x05ba9): (159, "LoadHandler2Map"),
|
||||
("read_only_space", 0x05bf9): (159, "LoadHandler3Map"),
|
||||
("read_only_space", 0x05c49): (167, "StoreHandler0Map"),
|
||||
("read_only_space", 0x05c99): (167, "StoreHandler1Map"),
|
||||
("read_only_space", 0x05ce9): (167, "StoreHandler2Map"),
|
||||
("read_only_space", 0x05d39): (167, "StoreHandler3Map"),
|
||||
("read_only_space", 0x05109): (89, "AccessCheckInfoMap"),
|
||||
("read_only_space", 0x05159): (90, "AccessorInfoMap"),
|
||||
("read_only_space", 0x051a9): (91, "AccessorPairMap"),
|
||||
("read_only_space", 0x051f9): (92, "AliasedArgumentsEntryMap"),
|
||||
("read_only_space", 0x05249): (93, "AllocationMementoMap"),
|
||||
("read_only_space", 0x05299): (94, "AsmWasmDataMap"),
|
||||
("read_only_space", 0x052e9): (95, "AsyncGeneratorRequestMap"),
|
||||
("read_only_space", 0x05339): (96, "ClassPositionsMap"),
|
||||
("read_only_space", 0x05389): (97, "DebugInfoMap"),
|
||||
("read_only_space", 0x053d9): (99, "FunctionTemplateInfoMap"),
|
||||
("read_only_space", 0x05429): (100, "FunctionTemplateRareDataMap"),
|
||||
("read_only_space", 0x05479): (102, "InterpreterDataMap"),
|
||||
("read_only_space", 0x054c9): (103, "ModuleInfoEntryMap"),
|
||||
("read_only_space", 0x05519): (104, "ModuleMap"),
|
||||
("read_only_space", 0x05569): (105, "ObjectTemplateInfoMap"),
|
||||
("read_only_space", 0x055b9): (106, "PromiseCapabilityMap"),
|
||||
("read_only_space", 0x05609): (107, "PromiseReactionMap"),
|
||||
("read_only_space", 0x05659): (108, "PrototypeInfoMap"),
|
||||
("read_only_space", 0x056a9): (109, "ScriptMap"),
|
||||
("read_only_space", 0x056f9): (110, "StackFrameInfoMap"),
|
||||
("read_only_space", 0x05749): (111, "StackTraceFrameMap"),
|
||||
("read_only_space", 0x05799): (112, "Tuple2Map"),
|
||||
("read_only_space", 0x057e9): (113, "Tuple3Map"),
|
||||
("read_only_space", 0x05839): (115, "WasmDebugInfoMap"),
|
||||
("read_only_space", 0x05889): (116, "WasmExceptionTagMap"),
|
||||
("read_only_space", 0x058d9): (117, "WasmExportedFunctionDataMap"),
|
||||
("read_only_space", 0x05929): (118, "CallableTaskMap"),
|
||||
("read_only_space", 0x05979): (119, "CallbackTaskMap"),
|
||||
("read_only_space", 0x059c9): (120, "PromiseFulfillReactionJobTaskMap"),
|
||||
("read_only_space", 0x05a19): (121, "PromiseRejectReactionJobTaskMap"),
|
||||
("read_only_space", 0x05a69): (122, "PromiseResolveThenableJobTaskMap"),
|
||||
("read_only_space", 0x05ab9): (123, "FinalizationGroupCleanupJobTaskMap"),
|
||||
("read_only_space", 0x05b09): (124, "AllocationSiteWithWeakNextMap"),
|
||||
("read_only_space", 0x05b59): (124, "AllocationSiteWithoutWeakNextMap"),
|
||||
("read_only_space", 0x05ba9): (159, "LoadHandler1Map"),
|
||||
("read_only_space", 0x05bf9): (159, "LoadHandler2Map"),
|
||||
("read_only_space", 0x05c49): (159, "LoadHandler3Map"),
|
||||
("read_only_space", 0x05c99): (167, "StoreHandler0Map"),
|
||||
("read_only_space", 0x05ce9): (167, "StoreHandler1Map"),
|
||||
("read_only_space", 0x05d39): (167, "StoreHandler2Map"),
|
||||
("read_only_space", 0x05d89): (167, "StoreHandler3Map"),
|
||||
("map_space", 0x00139): (1057, "ExternalMap"),
|
||||
("map_space", 0x00189): (1073, "JSMessageObjectMap"),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user