Commit 73c56a50 authored by Frank Tang's avatar Frank Tang Committed by Commit Bot

[Intl] Implement proposal-intl-DateTimeFormat-formatRange

https://rawgit.com/fabalbon/proposal-intl-DateTimeFormat-formatRange/master/out/

Design Doc https://goo.gl/PGUQ1d

Bug: v8:7729
Change-Id: I38b53ffdf610400b4132a25da99dac4be67bdf4b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1510574Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Commit-Queue: Frank Tang <ftang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60418}
parent c31a45d9
...@@ -4355,6 +4355,37 @@ void Genesis::InitializeGlobal_harmony_weak_refs() { ...@@ -4355,6 +4355,37 @@ void Genesis::InitializeGlobal_harmony_weak_refs() {
} }
#ifdef V8_INTL_SUPPORT #ifdef V8_INTL_SUPPORT
void Genesis::InitializeGlobal_harmony_intl_date_format_range() {
if (!FLAG_harmony_intl_date_format_range) return;
Handle<JSObject> intl = Handle<JSObject>::cast(
JSReceiver::GetProperty(
isolate(),
Handle<JSReceiver>(native_context()->global_object(), isolate()),
factory()->InternalizeUtf8String("Intl"))
.ToHandleChecked());
Handle<JSObject> date_time_format_object = Handle<JSObject>::cast(
JSReceiver::GetProperty(
isolate(), Handle<JSReceiver>(JSReceiver::cast(*intl), isolate()),
factory()->InternalizeUtf8String("DateTimeFormat"))
.ToHandleChecked());
Handle<JSFunction> date_time_format_constructor =
Handle<JSFunction>(JSFunction::cast(*date_time_format_object), isolate());
Handle<JSObject> prototype(
JSObject::cast(date_time_format_constructor->prototype()), isolate_);
SimpleInstallFunction(isolate_, prototype, "formatRange",
Builtins::kDateTimeFormatPrototypeFormatRange, 2,
false);
SimpleInstallFunction(isolate_, prototype, "formatRangeToParts",
Builtins::kDateTimeFormatPrototypeFormatRangeToParts, 2,
false);
}
void Genesis::InitializeGlobal_harmony_locale() { void Genesis::InitializeGlobal_harmony_locale() {
if (!FLAG_harmony_locale) return; if (!FLAG_harmony_locale) return;
......
...@@ -1268,6 +1268,10 @@ namespace internal { ...@@ -1268,6 +1268,10 @@ namespace internal {
CPP(DateTimeFormatInternalFormat) \ CPP(DateTimeFormatInternalFormat) \
/* ecma402 #sec-intl.datetimeformat.prototype.format */ \ /* ecma402 #sec-intl.datetimeformat.prototype.format */ \
CPP(DateTimeFormatPrototypeFormat) \ CPP(DateTimeFormatPrototypeFormat) \
/* ecma402 #sec-intl.datetimeformat.prototype.formatrange */ \
CPP(DateTimeFormatPrototypeFormatRange) \
/* ecma402 #sec-intl.datetimeformat.prototype.formatrangetoparts */ \
CPP(DateTimeFormatPrototypeFormatRangeToParts) \
/* ecma402 #sec-intl.datetimeformat.prototype.formattoparts */ \ /* ecma402 #sec-intl.datetimeformat.prototype.formattoparts */ \
CPP(DateTimeFormatPrototypeFormatToParts) \ CPP(DateTimeFormatPrototypeFormatToParts) \
/* ecma402 #sec-intl.datetimeformat.prototype.resolvedoptions */ \ /* ecma402 #sec-intl.datetimeformat.prototype.resolvedoptions */ \
......
...@@ -157,6 +157,72 @@ BUILTIN(DateTimeFormatPrototypeFormatToParts) { ...@@ -157,6 +157,72 @@ BUILTIN(DateTimeFormatPrototypeFormatToParts) {
isolate, JSDateTimeFormat::FormatToParts(isolate, dtf, date_value)); isolate, JSDateTimeFormat::FormatToParts(isolate, dtf, date_value));
} }
// Common code for DateTimeFormatPrototypeFormtRange(|ToParts)
template <class T>
V8_WARN_UNUSED_RESULT Object DateTimeFormatRange(
BuiltinArguments args, Isolate* isolate, const char* const method,
MaybeHandle<T> (*format)(Isolate*, Handle<JSDateTimeFormat>, double,
double)) {
// 1. Let dtf be this value.
// 2. If Type(dtf) is not Object, throw a TypeError exception.
CHECK_RECEIVER(JSObject, date_format_holder, method);
Factory* factory = isolate->factory();
// 3. If dtf does not have an [[InitializedDateTimeFormat]] internal slot,
// throw a TypeError exception.
if (!date_format_holder->IsJSDateTimeFormat()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
factory->NewStringFromAsciiChecked(method),
date_format_holder));
}
Handle<JSDateTimeFormat> dtf =
Handle<JSDateTimeFormat>::cast(date_format_holder);
// 4. If startDate is undefined or endDate is undefined, throw a RangeError
// exception.
Handle<Object> start_date = args.atOrUndefined(isolate, 1);
Handle<Object> end_date = args.atOrUndefined(isolate, 2);
if (start_date->IsUndefined(isolate) || end_date->IsUndefined(isolate)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
}
// 5. Let x be ? ToNumber(startDate).
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, start_date,
Object::ToNumber(isolate, start_date));
double x = start_date->Number();
// 6. Let y be ? ToNumber(endDate).
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, end_date,
Object::ToNumber(isolate, end_date));
double y = end_date->Number();
// 7. If x is greater than y, throw a RangeError exception.
if (x > y) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
}
// 8. Return ? FormatDateTimeRange(dtf, x, y)
// OR
// 8. Return ? FormatDateTimeRangeToParts(dtf, x, y).
RETURN_RESULT_OR_FAILURE(isolate, format(isolate, dtf, x, y));
}
BUILTIN(DateTimeFormatPrototypeFormatRange) {
const char* const method = "Intl.DateTimeFormat.prototype.formatRange";
HandleScope handle_scope(isolate);
return DateTimeFormatRange<String>(args, isolate, method,
JSDateTimeFormat::FormatRange);
}
BUILTIN(DateTimeFormatPrototypeFormatRangeToParts) {
const char* const method = "Intl.DateTimeFormat.prototype.formatRangeToParts";
HandleScope handle_scope(isolate);
return DateTimeFormatRange<JSArray>(args, isolate, method,
JSDateTimeFormat::FormatRangeToParts);
}
namespace { namespace {
Handle<JSFunction> CreateBoundFunction(Isolate* isolate, Handle<JSFunction> CreateBoundFunction(Isolate* isolate,
Handle<JSObject> object, Handle<JSObject> object,
......
...@@ -220,10 +220,11 @@ DEFINE_IMPLICATION(harmony_private_methods, harmony_private_fields) ...@@ -220,10 +220,11 @@ DEFINE_IMPLICATION(harmony_private_methods, harmony_private_fields)
V(harmony_weak_refs, "harmony weak references") \ V(harmony_weak_refs, "harmony weak references") \
#ifdef V8_INTL_SUPPORT #ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) \ #define HARMONY_INPROGRESS(V) \
HARMONY_INPROGRESS_BASE(V) \ HARMONY_INPROGRESS_BASE(V) \
V(harmony_intl_bigint, "BigInt.prototype.toLocaleString") \ V(harmony_intl_bigint, "BigInt.prototype.toLocaleString") \
V(harmony_intl_datetime_style, "dateStyle timeStyle for DateTimeFormat") V(harmony_intl_datetime_style, "dateStyle timeStyle for DateTimeFormat") \
V(harmony_intl_date_format_range, "DateTimeFormat formatRange")
#else #else
#define HARMONY_INPROGRESS(V) HARMONY_INPROGRESS_BASE(V) #define HARMONY_INPROGRESS(V) HARMONY_INPROGRESS_BASE(V)
#endif #endif
......
...@@ -2151,6 +2151,7 @@ void JSDateTimeFormat::JSDateTimeFormatVerify(Isolate* isolate) { ...@@ -2151,6 +2151,7 @@ void JSDateTimeFormat::JSDateTimeFormatVerify(Isolate* isolate) {
JSObjectVerify(isolate); JSObjectVerify(isolate);
VerifyObjectField(isolate, kICULocaleOffset); VerifyObjectField(isolate, kICULocaleOffset);
VerifyObjectField(isolate, kICUSimpleDateFormatOffset); VerifyObjectField(isolate, kICUSimpleDateFormatOffset);
VerifyObjectField(isolate, kICUDateIntervalFormatOffset);
VerifyObjectField(isolate, kBoundFormatOffset); VerifyObjectField(isolate, kBoundFormatOffset);
VerifyObjectField(isolate, kFlagsOffset); VerifyObjectField(isolate, kFlagsOffset);
} }
......
...@@ -2089,6 +2089,7 @@ void JSDateTimeFormat::JSDateTimeFormatPrint(std::ostream& os) { // NOLINT ...@@ -2089,6 +2089,7 @@ void JSDateTimeFormat::JSDateTimeFormatPrint(std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, *this, "JSDateTimeFormat"); JSObjectPrintHeader(os, *this, "JSDateTimeFormat");
os << "\n - icu locale: " << Brief(icu_locale()); os << "\n - icu locale: " << Brief(icu_locale());
os << "\n - icu simple date format: " << Brief(icu_simple_date_format()); os << "\n - icu simple date format: " << Brief(icu_simple_date_format());
os << "\n - icu date interval format: " << Brief(icu_date_interval_format());
os << "\n - bound format: " << Brief(bound_format()); os << "\n - bound format: " << Brief(bound_format());
os << "\n - hour cycle: " << HourCycleAsString(); os << "\n - hour cycle: " << HourCycleAsString();
JSObjectPrintBody(os, *this); JSObjectPrintBody(os, *this);
......
...@@ -23,6 +23,8 @@ OBJECT_CONSTRUCTORS_IMPL(JSDateTimeFormat, JSObject) ...@@ -23,6 +23,8 @@ OBJECT_CONSTRUCTORS_IMPL(JSDateTimeFormat, JSObject)
ACCESSORS(JSDateTimeFormat, icu_locale, Managed<icu::Locale>, kICULocaleOffset) ACCESSORS(JSDateTimeFormat, icu_locale, Managed<icu::Locale>, kICULocaleOffset)
ACCESSORS(JSDateTimeFormat, icu_simple_date_format, ACCESSORS(JSDateTimeFormat, icu_simple_date_format,
Managed<icu::SimpleDateFormat>, kICUSimpleDateFormatOffset) Managed<icu::SimpleDateFormat>, kICUSimpleDateFormatOffset)
ACCESSORS(JSDateTimeFormat, icu_date_interval_format,
Managed<icu::DateIntervalFormat>, kICUDateIntervalFormatOffset)
ACCESSORS(JSDateTimeFormat, bound_format, Object, kBoundFormatOffset) ACCESSORS(JSDateTimeFormat, bound_format, Object, kBoundFormatOffset)
SMI_ACCESSORS(JSDateTimeFormat, flags, kFlagsOffset) SMI_ACCESSORS(JSDateTimeFormat, flags, kFlagsOffset)
......
...@@ -19,7 +19,9 @@ ...@@ -19,7 +19,9 @@
#include "src/objects/js-date-time-format-inl.h" #include "src/objects/js-date-time-format-inl.h"
#include "unicode/calendar.h" #include "unicode/calendar.h"
#include "unicode/dtitvfmt.h"
#include "unicode/dtptngen.h" #include "unicode/dtptngen.h"
#include "unicode/fieldpos.h"
#include "unicode/gregocal.h" #include "unicode/gregocal.h"
#include "unicode/smpdtfmt.h" #include "unicode/smpdtfmt.h"
#include "unicode/unistr.h" #include "unicode/unistr.h"
...@@ -951,6 +953,16 @@ std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormatFromCache( ...@@ -951,6 +953,16 @@ std::unique_ptr<icu::SimpleDateFormat> CreateICUDateFormatFromCache(
cache.Pointer()->Create(icu_locale, skeleton, generator)); cache.Pointer()->Create(icu_locale, skeleton, generator));
} }
std::unique_ptr<icu::DateIntervalFormat> CreateICUDateIntervalFormat(
const icu::Locale& icu_locale, const icu::UnicodeString& skeleton) {
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;
}
Intl::HourCycle HourCycleFromPattern(const icu::UnicodeString pattern) { Intl::HourCycle HourCycleFromPattern(const icu::UnicodeString pattern) {
bool in_quote = false; bool in_quote = false;
for (int32_t i = 0; i < pattern.length(); i++) { for (int32_t i = 0; i < pattern.length(); i++) {
...@@ -1085,6 +1097,18 @@ std::unique_ptr<icu::SimpleDateFormat> DateTimeStylePattern( ...@@ -1085,6 +1097,18 @@ std::unique_ptr<icu::SimpleDateFormat> DateTimeStylePattern(
generator); 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 { class DateTimePatternGeneratorCache {
public: public:
// Return a clone copy that the caller have to free. // Return a clone copy that the caller have to free.
...@@ -1267,6 +1291,7 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize( ...@@ -1267,6 +1291,7 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
DateTimeStyle date_style = DateTimeStyle::kUndefined; DateTimeStyle date_style = DateTimeStyle::kUndefined;
DateTimeStyle time_style = DateTimeStyle::kUndefined; DateTimeStyle time_style = DateTimeStyle::kUndefined;
std::unique_ptr<icu::SimpleDateFormat> icu_date_format; std::unique_ptr<icu::SimpleDateFormat> icu_date_format;
std::unique_ptr<icu::DateIntervalFormat> icu_date_interval_format;
if (FLAG_harmony_intl_datetime_style) { if (FLAG_harmony_intl_datetime_style) {
// 28. Let dateStyle be ? GetOption(options, "dateStyle", "string", « // 28. Let dateStyle be ? GetOption(options, "dateStyle", "string", «
...@@ -1309,6 +1334,10 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize( ...@@ -1309,6 +1334,10 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
time_style != DateTimeStyle::kUndefined) { time_style != DateTimeStyle::kUndefined) {
icu_date_format = DateTimeStylePattern(date_style, time_style, icu_locale, icu_date_format = DateTimeStylePattern(date_style, time_style, icu_locale,
hc, *generator); hc, *generator);
if (FLAG_harmony_intl_date_format_range) {
icu_date_interval_format = CreateICUDateIntervalFormat(
icu_locale, SkeletonFromDateFormat(*icu_date_format));
}
} }
} }
// 33. Else, // 33. Else,
...@@ -1362,6 +1391,10 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize( ...@@ -1362,6 +1391,10 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
FATAL("Failed to create ICU date format, are ICU data files missing?"); 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 // g. If dateTimeFormat.[[Hour]] is not undefined, then
if (!has_hour_option) { if (!has_hour_option) {
...@@ -1410,6 +1443,12 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize( ...@@ -1410,6 +1443,12 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
Managed<icu::SimpleDateFormat>::FromUniquePtr(isolate, 0, Managed<icu::SimpleDateFormat>::FromUniquePtr(isolate, 0,
std::move(icu_date_format)); std::move(icu_date_format));
date_time_format->set_icu_simple_date_format(*managed_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);
}
return date_time_format; return date_time_format;
} }
...@@ -1468,7 +1507,7 @@ Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) { ...@@ -1468,7 +1507,7 @@ Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
} // namespace } // namespace
MaybeHandle<Object> JSDateTimeFormat::FormatToParts( MaybeHandle<JSArray> JSDateTimeFormat::FormatToParts(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
double date_value) { double date_value) {
Factory* factory = isolate->factory(); Factory* factory = isolate->factory();
...@@ -1482,7 +1521,7 @@ MaybeHandle<Object> JSDateTimeFormat::FormatToParts( ...@@ -1482,7 +1521,7 @@ MaybeHandle<Object> JSDateTimeFormat::FormatToParts(
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
format->format(date_value, formatted, &fp_iter, status); format->format(date_value, formatted, &fp_iter, status);
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object); THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), JSArray);
} }
Handle<JSArray> result = factory->NewJSArray(0); Handle<JSArray> result = factory->NewJSArray(0);
...@@ -1500,14 +1539,14 @@ MaybeHandle<Object> JSDateTimeFormat::FormatToParts( ...@@ -1500,14 +1539,14 @@ MaybeHandle<Object> JSDateTimeFormat::FormatToParts(
ASSIGN_RETURN_ON_EXCEPTION( ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring, isolate, substring,
Intl::ToString(isolate, formatted, previous_end_pos, begin_pos), Intl::ToString(isolate, formatted, previous_end_pos, begin_pos),
Object); JSArray);
Intl::AddElement(isolate, result, index, Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring); IcuDateFieldIdToDateType(-1, isolate), substring);
++index; ++index;
} }
ASSIGN_RETURN_ON_EXCEPTION( ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring, isolate, substring,
Intl::ToString(isolate, formatted, begin_pos, end_pos), Object); Intl::ToString(isolate, formatted, begin_pos, end_pos), JSArray);
Intl::AddElement(isolate, result, index, Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(fp.getField(), isolate), IcuDateFieldIdToDateType(fp.getField(), isolate),
substring); substring);
...@@ -1517,7 +1556,7 @@ MaybeHandle<Object> JSDateTimeFormat::FormatToParts( ...@@ -1517,7 +1556,7 @@ MaybeHandle<Object> JSDateTimeFormat::FormatToParts(
if (previous_end_pos < length) { if (previous_end_pos < length) {
ASSIGN_RETURN_ON_EXCEPTION( ASSIGN_RETURN_ON_EXCEPTION(
isolate, substring, isolate, substring,
Intl::ToString(isolate, formatted, previous_end_pos, length), Object); Intl::ToString(isolate, formatted, previous_end_pos, length), JSArray);
Intl::AddElement(isolate, result, index, Intl::AddElement(isolate, result, index,
IcuDateFieldIdToDateType(-1, isolate), substring); IcuDateFieldIdToDateType(-1, isolate), substring);
} }
...@@ -1546,5 +1585,76 @@ Handle<String> JSDateTimeFormat::HourCycleAsString() const { ...@@ -1546,5 +1585,76 @@ 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.
// #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);
}
// 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);
}
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));
return Intl::ToString(isolate, result);
}
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;
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "src/objects/object-macros.h" #include "src/objects/object-macros.h"
namespace U_ICU_NAMESPACE { namespace U_ICU_NAMESPACE {
class DateIntervalFormat;
class Locale; class Locale;
class SimpleDateFormat; class SimpleDateFormat;
} // namespace U_ICU_NAMESPACE } // namespace U_ICU_NAMESPACE
...@@ -56,10 +57,21 @@ class JSDateTimeFormat : public JSObject { ...@@ -56,10 +57,21 @@ class JSDateTimeFormat : public JSObject {
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
Handle<Object> date); Handle<Object> date);
V8_WARN_UNUSED_RESULT static MaybeHandle<Object> FormatToParts( // ecma402/#sec-Intl.DateTimeFormat.prototype.formatToParts
V8_WARN_UNUSED_RESULT static MaybeHandle<JSArray> FormatToParts(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format, Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
double date_value); double date_value);
// ecma402/#sec-intl.datetimeformat.prototype.formatRange
V8_WARN_UNUSED_RESULT static MaybeHandle<String> FormatRange(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
double x_date_value, double y_date_value);
// ecma402/sec-Intl.DateTimeFormat.prototype.formatRangeToParts
V8_WARN_UNUSED_RESULT static MaybeHandle<JSArray> FormatRangeToParts(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
double x_date_value, double y_date_value);
// ecma-402/#sec-todatetimeoptions // ecma-402/#sec-todatetimeoptions
enum class RequiredOption { kDate, kTime, kAny }; enum class RequiredOption { kDate, kTime, kAny };
enum class DefaultsOption { kDate, kTime, kAll }; enum class DefaultsOption { kDate, kTime, kAll };
...@@ -80,12 +92,13 @@ class JSDateTimeFormat : public JSObject { ...@@ -80,12 +92,13 @@ class JSDateTimeFormat : public JSObject {
enum class DateTimeStyle { kUndefined, kFull, kLong, kMedium, kShort }; enum class DateTimeStyle { kUndefined, kFull, kLong, kMedium, kShort };
// Layout description. // Layout description.
#define JS_DATE_TIME_FORMAT_FIELDS(V) \ #define JS_DATE_TIME_FORMAT_FIELDS(V) \
V(kICULocaleOffset, kTaggedSize) \ V(kICULocaleOffset, kTaggedSize) \
V(kICUSimpleDateFormatOffset, kTaggedSize) \ V(kICUSimpleDateFormatOffset, kTaggedSize) \
V(kBoundFormatOffset, kTaggedSize) \ V(kICUDateIntervalFormatOffset, kTaggedSize) \
V(kFlagsOffset, kTaggedSize) \ V(kBoundFormatOffset, kTaggedSize) \
/* Total size. */ \ V(kFlagsOffset, kTaggedSize) \
/* Total size. */ \
V(kSize, 0) V(kSize, 0)
DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize,
...@@ -130,6 +143,7 @@ class JSDateTimeFormat : public JSObject { ...@@ -130,6 +143,7 @@ class JSDateTimeFormat : public JSObject {
DECL_ACCESSORS(icu_locale, Managed<icu::Locale>) DECL_ACCESSORS(icu_locale, Managed<icu::Locale>)
DECL_ACCESSORS(icu_simple_date_format, Managed<icu::SimpleDateFormat>) DECL_ACCESSORS(icu_simple_date_format, Managed<icu::SimpleDateFormat>)
DECL_ACCESSORS(icu_date_interval_format, Managed<icu::DateIntervalFormat>)
DECL_ACCESSORS(bound_format, Object) DECL_ACCESSORS(bound_format, Object)
DECL_INT_ACCESSORS(flags) DECL_INT_ACCESSORS(flags)
......
// 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
let descriptor = Object.getOwnPropertyDescriptor(
Intl.DateTimeFormat.prototype, "formatRangeToParts");
assertTrue(descriptor.writable);
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();
assertThrows(() => dtf.formatRangeToParts(), RangeError);
assertThrows(() => dtf.formatRangeToParts(date1), RangeError);
assertThrows(() => dtf.formatRangeToParts(undefined, date2), RangeError);
assertThrows(() => dtf.formatRangeToParts(date1, undefined), RangeError);
assertThrows(() => dtf.formatRangeToParts("2019-1-3", date2), RangeError);
assertThrows(() => dtf.formatRangeToParts(date1, "2019-5-4"), RangeError);
assertThrows(() => dtf.formatRangeToParts(date2, date1), RangeError);
assertDoesNotThrow(() =>dtf.formatRangeToParts(date1, date2));
// 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
let descriptor = Object.getOwnPropertyDescriptor(
Intl.DateTimeFormat.prototype, "formatRange");
assertTrue(descriptor.writable);
assertFalse(descriptor.enumerable);
assertTrue(descriptor.configurable);
const date1 = new Date("2019-1-3");
const date2 = new Date("2019-1-5");
const date3 = new Date("2019-3-4");
const date4 = new Date("2020-3-4");
var dtf = new Intl.DateTimeFormat(["en"]);
assertThrows(() => dtf.formatRange(), RangeError);
assertThrows(() => dtf.formatRange(date1), RangeError);
assertThrows(() => dtf.formatRange(undefined, date2), RangeError);
assertThrows(() => dtf.formatRange(date1, undefined), RangeError);
assertThrows(() => dtf.formatRange("2019-1-3", date2), RangeError);
assertThrows(() => dtf.formatRange(date1, "2019-5-4"), RangeError);
assertThrows(() => dtf.formatRange(date2, date1), RangeError);
assertDoesNotThrow(() =>dtf.formatRange(date1, date2));
assertEquals("1/3/2019 – 1/5/2019", dtf.formatRange(date1, date2));
assertEquals("1/3/2019 – 3/4/2019", dtf.formatRange(date1, date3));
assertEquals("1/3/2019 – 3/4/2020", dtf.formatRange(date1, date4));
assertEquals("1/5/2019 – 3/4/2019", dtf.formatRange(date2, date3));
assertEquals("1/5/2019 – 3/4/2020", dtf.formatRange(date2, date4));
assertEquals("3/4/2019 – 3/4/2020", dtf.formatRange(date3, date4));
dtf = new Intl.DateTimeFormat(["en"], {year: "numeric", month: "short", day: "numeric"});
assertEquals("Jan 3 – 5, 2019", dtf.formatRange(date1, date2));
assertEquals("Jan 3 – Mar 4, 2019", dtf.formatRange(date1, date3));
assertEquals("Jan 3, 2019 – Mar 4, 2020", dtf.formatRange(date1, date4));
assertEquals("Jan 5 – Mar 4, 2019", dtf.formatRange(date2, date3));
assertEquals("Jan 5, 2019 – Mar 4, 2020", dtf.formatRange(date2, date4));
assertEquals("Mar 4, 2019 – Mar 4, 2020", dtf.formatRange(date3, date4));
// Test the sequence of ToNumber and TimeClip
var secondDateAccessed = false;
assertThrows(
() =>
dtf.formatRange(
new Date(864000000*10000000 + 1), // a date will cause TimeClip return NaN
{ get [Symbol.toPrimitive]() { secondDateAccessed = true; return {}} }),
TypeError);
assertTrue(secondDateAccessed);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment