[Intl] Implement Intl.RelativeTimeFormat.prototype.{format, formatToParts}

Spec: http://tc39.github.io/proposal-intl-relative-time/

Design Doc: go/add-intl.relativetimeformat-to-v8

Test: test262/intl402/RelativeTimeFormat/*, intl/relative-time-format/*

R=cira@chromium.org, gsathya@chromium.org

Bug: v8:7869
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: Ied95d601cf707db5d555f9d963b9b1f206e37331
Reviewed-on: https://chromium-review.googlesource.com/1124728
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Commit-Queue: Frank Tang <ftang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54520}
This commit is contained in:
Frank Tang 2018-07-17 13:19:17 -07:00 committed by Commit Bot
parent 26c7aa8c86
commit b819afeb4f
14 changed files with 1026 additions and 52 deletions

View File

@ -4601,6 +4601,11 @@ void Genesis::InitializeGlobal_harmony_intl_relative_time_format() {
SimpleInstallFunction(isolate(), prototype, "resolvedOptions",
Builtins::kRelativeTimeFormatPrototypeResolvedOptions,
0, false);
SimpleInstallFunction(isolate(), prototype, "format",
Builtins::kRelativeTimeFormatPrototypeFormat, 2, false);
SimpleInstallFunction(isolate(), prototype, "formatToParts",
Builtins::kRelativeTimeFormatPrototypeFormatToParts, 2,
false);
}
#endif // V8_INTL_SUPPORT

View File

@ -1349,10 +1349,14 @@ namespace internal {
CPP(NumberFormatInternalFormatNumber) \
/* ecma402 #sec-intl.numberformat.prototype.format */ \
CPP(NumberFormatPrototypeFormatNumber) \
/* ecma402 #sec-intl-relativetimeformat-constructor */ \
/* ecma402 #sec-intl.RelativeTimeFormat.constructor */ \
CPP(RelativeTimeFormatConstructor) \
/* ecma402 #sec-intl.relativetimeformat.prototype.resolvedoptions */ \
CPP(RelativeTimeFormatPrototypeResolvedOptions)
/* ecma402 #sec-intl.RelativeTimeFormat.prototype.resolvedOptions */ \
CPP(RelativeTimeFormatPrototypeResolvedOptions) \
/* ecma402 #sec-intl.RelativeTimeFormat.prototype.format */ \
CPP(RelativeTimeFormatPrototypeFormat) \
/* ecma402 #sec-intl.RelativeTimeFormat.prototype.formatToParts */ \
CPP(RelativeTimeFormatPrototypeFormatToParts)
#else
#define BUILTIN_LIST(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
BUILTIN_LIST_BASE(CPP, API, TFJ, TFC, TFS, TFH, ASM) \

View File

@ -6,6 +6,8 @@
#error Internationalization is expected to be enabled.
#endif // V8_INTL_SUPPORT
#include <cmath>
#include "src/builtins/builtins-intl.h"
#include "src/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"
@ -22,10 +24,12 @@
#include "unicode/fpositer.h"
#include "unicode/normalizer2.h"
#include "unicode/numfmt.h"
#include "unicode/reldatefmt.h"
#include "unicode/smpdtfmt.h"
#include "unicode/udat.h"
#include "unicode/ufieldpositer.h"
#include "unicode/unistr.h"
#include "unicode/ureldatefmt.h"
#include "unicode/ustring.h"
namespace v8 {
@ -212,11 +216,11 @@ Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
}
}
bool AddElement(Handle<JSArray> array, int index,
Handle<String> field_type_string,
const icu::UnicodeString& formatted, int32_t begin, int32_t end,
Isolate* isolate) {
HandleScope scope(isolate);
MaybeHandle<JSObject> InnerAddElement(Isolate* isolate, Handle<JSArray> array,
int index,
Handle<String> field_type_string,
const icu::UnicodeString& formatted,
int32_t begin, int32_t end) {
Factory* factory = isolate->factory();
Handle<JSObject> element = factory->NewJSObject(isolate->object_function());
Handle<String> value;
@ -224,16 +228,42 @@ bool AddElement(Handle<JSArray> array, int index,
field_type_string, NONE);
icu::UnicodeString field(formatted.tempSubStringBetween(begin, end));
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
ASSIGN_RETURN_ON_EXCEPTION(
isolate, value,
factory->NewStringFromTwoByte(Vector<const uint16_t>(
reinterpret_cast<const uint16_t*>(field.getBuffer()),
field.length())),
false);
JSObject);
JSObject::AddProperty(isolate, element, factory->value_string(), value, NONE);
JSObject::AddDataElement(array, index, element, NONE);
return true;
return element;
}
Maybe<bool> AddElement(Isolate* isolate, Handle<JSArray> array, int index,
Handle<String> field_type_string,
const icu::UnicodeString& formatted, int32_t begin,
int32_t end) {
RETURN_ON_EXCEPTION_VALUE(
isolate,
InnerAddElement(isolate, array, index, field_type_string, formatted,
begin, end),
Nothing<bool>());
return Just(true);
}
Maybe<bool> AddElement(Isolate* isolate, Handle<JSArray> array, int index,
Handle<String> field_type_string,
const icu::UnicodeString& formatted, int32_t begin,
int32_t end, Handle<String> name, Handle<String> value) {
Handle<JSObject> element;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, element,
InnerAddElement(isolate, array, index, field_type_string, formatted,
begin, end),
Nothing<bool>());
JSObject::AddProperty(isolate, element, name, value, NONE);
return Just(true);
}
bool cmp_NumberFormatSpan(const NumberFormatSpan& a,
@ -289,10 +319,10 @@ Object* FormatNumberToParts(Isolate* isolate, icu::NumberFormat* fmt,
part.field_id == -1
? isolate->factory()->literal_string()
: IcuNumberFieldIdToNumberType(part.field_id, number, isolate);
if (!AddElement(result, index, field_type_string, formatted, part.begin_pos,
part.end_pos, isolate)) {
return ReadOnlyRoots(isolate).undefined_value();
}
Maybe<bool> maybe_added_element =
AddElement(isolate, result, index, field_type_string, formatted,
part.begin_pos, part.end_pos);
MAYBE_RETURN(maybe_added_element, ReadOnlyRoots(isolate).undefined_value());
++index;
}
JSObject::ValidateElements(*result);
@ -322,25 +352,26 @@ Object* FormatDateToParts(Isolate* isolate, icu::DateFormat* format,
int32_t end_pos = fp.getEndIndex();
if (previous_end_pos < begin_pos) {
if (!AddElement(result, index, IcuDateFieldIdToDateType(-1, isolate),
formatted, previous_end_pos, begin_pos, isolate)) {
return ReadOnlyRoots(isolate).undefined_value();
}
Maybe<bool> maybe_added_element = AddElement(
isolate, result, index, IcuDateFieldIdToDateType(-1, isolate),
formatted, previous_end_pos, begin_pos);
MAYBE_RETURN(maybe_added_element,
ReadOnlyRoots(isolate).undefined_value());
++index;
}
if (!AddElement(result, index,
IcuDateFieldIdToDateType(fp.getField(), isolate), formatted,
begin_pos, end_pos, isolate)) {
return ReadOnlyRoots(isolate).undefined_value();
}
Maybe<bool> maybe_added_element =
AddElement(isolate, result, index,
IcuDateFieldIdToDateType(fp.getField(), isolate), formatted,
begin_pos, end_pos);
MAYBE_RETURN(maybe_added_element, ReadOnlyRoots(isolate).undefined_value());
previous_end_pos = end_pos;
++index;
}
if (previous_end_pos < length) {
if (!AddElement(result, index, IcuDateFieldIdToDateType(-1, isolate),
formatted, previous_end_pos, length, isolate)) {
return ReadOnlyRoots(isolate).undefined_value();
}
Maybe<bool> maybe_added_element = AddElement(
isolate, result, index, IcuDateFieldIdToDateType(-1, isolate),
formatted, previous_end_pos, length);
MAYBE_RETURN(maybe_added_element, ReadOnlyRoots(isolate).undefined_value());
}
JSObject::ValidateElements(*result);
return *result;
@ -676,6 +707,211 @@ BUILTIN(LocalePrototypeMinimize) {
isolate->factory()->NewJSObjectWithNullProto()));
}
namespace {
MaybeHandle<JSArray> GenerateRelativeTimeFormatParts(
Isolate* isolate, icu::UnicodeString formatted,
icu::UnicodeString integer_part, Handle<String> unit) {
Factory* factory = isolate->factory();
Handle<JSArray> array = factory->NewJSArray(0);
int32_t found = formatted.indexOf(integer_part);
if (found < 0) {
// Cannot find the integer_part in the formatted.
// Return [{'type': 'literal', 'value': formatted}]
Maybe<bool> maybe_added_element =
AddElement(isolate, array,
0, // index
factory->literal_string(), // field_type_string
formatted,
0, // begin
formatted.length()); // end
MAYBE_RETURN(maybe_added_element, MaybeHandle<JSArray>());
} else {
// Found the formatted integer in the result.
int index = 0;
// array.push({
// 'type': 'literal',
// 'value': formatted.substring(0, found)})
if (found > 0) {
Maybe<bool> maybe_added_element =
AddElement(isolate, array, index++,
factory->literal_string(), // field_type_string
formatted,
0, // begin
found); // end
MAYBE_RETURN(maybe_added_element, MaybeHandle<JSArray>());
}
// array.push({
// 'type': 'integer',
// 'value': formatted.substring(found, found + integer_part.length),
// 'unit': unit})
Maybe<bool> maybe_added_element =
AddElement(isolate, array, index++,
factory->integer_string(), // field_type_string
formatted,
found, // begin
found + integer_part.length(), // end
factory->unit_string(), unit);
MAYBE_RETURN(maybe_added_element, MaybeHandle<JSArray>());
// array.push({
// 'type': 'literal',
// 'value': formatted.substring(
// found + integer_part.length, formatted.length)})
if (found + integer_part.length() < formatted.length()) {
Maybe<bool> maybe_added_element =
AddElement(isolate, array, index,
factory->literal_string(), // field_type_string
formatted,
found + integer_part.length(), // begin
formatted.length()); // end
MAYBE_RETURN(maybe_added_element, MaybeHandle<JSArray>());
}
}
return array;
}
bool GetURelativeDateTimeUnit(Handle<String> unit,
URelativeDateTimeUnit* unit_enum) {
std::unique_ptr<char[]> unit_str = unit->ToCString();
if ((strcmp("second", unit_str.get()) == 0) ||
(strcmp("seconds", unit_str.get()) == 0)) {
*unit_enum = UDAT_REL_UNIT_SECOND;
} else if ((strcmp("minute", unit_str.get()) == 0) ||
(strcmp("minutes", unit_str.get()) == 0)) {
*unit_enum = UDAT_REL_UNIT_MINUTE;
} else if ((strcmp("hour", unit_str.get()) == 0) ||
(strcmp("hours", unit_str.get()) == 0)) {
*unit_enum = UDAT_REL_UNIT_HOUR;
} else if ((strcmp("day", unit_str.get()) == 0) ||
(strcmp("days", unit_str.get()) == 0)) {
*unit_enum = UDAT_REL_UNIT_DAY;
} else if ((strcmp("week", unit_str.get()) == 0) ||
(strcmp("weeks", unit_str.get()) == 0)) {
*unit_enum = UDAT_REL_UNIT_WEEK;
} else if ((strcmp("month", unit_str.get()) == 0) ||
(strcmp("months", unit_str.get()) == 0)) {
*unit_enum = UDAT_REL_UNIT_MONTH;
} else if ((strcmp("quarter", unit_str.get()) == 0) ||
(strcmp("quarters", unit_str.get()) == 0)) {
*unit_enum = UDAT_REL_UNIT_QUARTER;
} else if ((strcmp("year", unit_str.get()) == 0) ||
(strcmp("years", unit_str.get()) == 0)) {
*unit_enum = UDAT_REL_UNIT_YEAR;
} else {
return false;
}
return true;
}
Object* RelativeTimeFormatPrototypeFormatCommon(
BuiltinArguments args, Isolate* isolate,
Handle<JSRelativeTimeFormat> format_holder, const char* func_name,
bool to_parts) {
Factory* factory = isolate->factory();
Handle<Object> value_obj = args.atOrUndefined(isolate, 1);
Handle<Object> unit_obj = args.atOrUndefined(isolate, 2);
// 3. Let value be ? ToNumber(value).
Handle<Object> value;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value,
Object::ToNumber(isolate, value_obj));
double number = value->Number();
// 4. Let unit be ? ToString(unit).
Handle<String> unit;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, unit,
Object::ToString(isolate, unit_obj));
// 4. If isFinite(value) is false, then throw a RangeError exception.
if (!std::isfinite(number)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(
MessageTemplate::kNotFiniteNumber,
isolate->factory()->NewStringFromAsciiChecked(func_name)));
}
icu::RelativeDateTimeFormatter* formatter =
JSRelativeTimeFormat::UnpackFormatter(isolate, format_holder);
CHECK_NOT_NULL(formatter);
URelativeDateTimeUnit unit_enum;
if (!GetURelativeDateTimeUnit(unit, &unit_enum)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewRangeError(MessageTemplate::kInvalidUnit,
isolate->factory()->NewStringFromAsciiChecked(func_name),
unit));
}
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeString formatted;
if (unit_enum == UDAT_REL_UNIT_QUARTER) {
// ICU have not yet implement UDAT_REL_UNIT_QUARTER.
} else {
if (format_holder->numeric() == JSRelativeTimeFormat::Numeric::ALWAYS) {
formatter->formatNumeric(number, unit_enum, formatted, status);
} else {
DCHECK_EQ(JSRelativeTimeFormat::Numeric::AUTO, format_holder->numeric());
formatter->format(number, unit_enum, formatted, status);
}
}
if (U_FAILURE(status)) {
// Internal ICU error.
return ReadOnlyRoots(isolate).undefined_value();
}
if (to_parts) {
icu::UnicodeString integer;
icu::FieldPosition pos;
formatter->getNumberFormat().format(std::abs(number), integer, pos, status);
if (U_FAILURE(status)) {
// Internal ICU error.
return ReadOnlyRoots(isolate).undefined_value();
}
Handle<JSArray> elements;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, elements,
GenerateRelativeTimeFormatParts(isolate, formatted, integer, unit));
return *elements;
}
RETURN_RESULT_OR_FAILURE(
isolate, factory->NewStringFromTwoByte(Vector<const uint16_t>(
reinterpret_cast<const uint16_t*>(formatted.getBuffer()),
formatted.length())));
}
} // namespace
BUILTIN(RelativeTimeFormatPrototypeFormat) {
HandleScope scope(isolate);
// 1. Let relativeTimeFormat be the this value.
// 2. If Type(relativeTimeFormat) is not Object or relativeTimeFormat does not
// have an [[InitializedRelativeTimeFormat]] internal slot whose value is
// true, throw a TypeError exception.
CHECK_RECEIVER(JSRelativeTimeFormat, format_holder,
"Intl.RelativeTimeFormat.prototype.format");
return RelativeTimeFormatPrototypeFormatCommon(args, isolate, format_holder,
"format", false);
}
BUILTIN(RelativeTimeFormatPrototypeFormatToParts) {
HandleScope scope(isolate);
// 1. Let relativeTimeFormat be the this value.
// 2. If Type(relativeTimeFormat) is not Object or relativeTimeFormat does not
// have an [[InitializedRelativeTimeFormat]] internal slot whose value is
// true, throw a TypeError exception.
CHECK_RECEIVER(JSRelativeTimeFormat, format_holder,
"Intl.RelativeTimeFormat.prototype.formatToParts");
return RelativeTimeFormatPrototypeFormatCommon(args, isolate, format_holder,
"formatToParts", true);
}
// Locale getters.
BUILTIN(LocalePrototypeLanguage) {
HandleScope scope(isolate);

View File

@ -372,6 +372,7 @@ class ErrorUtils : public AllStatic {
"% is not a function or its return value is not iterable") \
T(NotCallableOrAsyncIterable, \
"% is not a function or its return value is not async iterable") \
T(NotFiniteNumber, "Value need to be finite number for %()") \
T(NotIterable, "% is not iterable") \
T(NotAsyncIterable, "% is not async iterable") \
T(NotPropertyName, "% is not a valid property name") \

View File

@ -362,7 +362,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(18),
B(LdaConstant), U8(14),
B(Star), R(19),

View File

@ -123,7 +123,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(19),
B(LdaConstant), U8(11),
B(Star), R(20),
@ -377,7 +377,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(19),
B(LdaConstant), U8(11),
B(Star), R(20),
@ -653,7 +653,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(19),
B(LdaConstant), U8(11),
B(Star), R(20),
@ -885,7 +885,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(17),
B(LdaConstant), U8(9),
B(Star), R(18),

View File

@ -85,7 +85,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(12),
B(LdaConstant), U8(7),
B(Star), R(13),
@ -217,7 +217,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(13),
B(LdaConstant), U8(7),
B(Star), R(14),
@ -361,7 +361,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(12),
B(LdaConstant), U8(7),
B(Star), R(13),
@ -495,7 +495,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(11),
B(LdaConstant), U8(9),
B(Star), R(12),

View File

@ -89,7 +89,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(14),
B(LdaConstant), U8(6),
B(Star), R(15),
@ -256,7 +256,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(14),
B(LdaConstant), U8(11),
B(Star), R(15),
@ -401,7 +401,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(12),
B(LdaConstant), U8(8),
B(Star), R(13),
@ -550,7 +550,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(17),
B(LdaConstant), U8(8),
B(Star), R(18),
@ -697,7 +697,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(15),
B(LdaConstant), U8(9),
B(Star), R(16),
@ -859,7 +859,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(14),
B(LdaConstant), U8(12),
B(Star), R(15),
@ -1007,7 +1007,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(20),
B(LdaConstant), U8(6),
B(Star), R(21),
@ -1218,7 +1218,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(19),
B(LdaConstant), U8(7),
B(Star), R(20),

View File

@ -203,7 +203,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(149),
B(Wide), B(LdaSmi), I16(150),
B(Star), R(14),
B(LdaConstant), U8(13),
B(Star), R(15),

View File

@ -0,0 +1,502 @@
// Copyright 2018 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-relative-time-format
// The following test are not part of the comformance. Just some output in
// English to verify the format does return something reasonable for English.
// It may be changed when we update the CLDR data.
// NOTE: These are UNSPECIFIED behavior in
// http://tc39.github.io/proposal-intl-relative-time/
let longAuto = new Intl.RelativeTimeFormat(
"en", {style: "long", localeMatcher: 'lookup', numeric: 'auto'});
assertEquals('03 seconds ago', longAuto.format(-3, 'second'));
assertEquals('02 seconds ago', longAuto.format(-2, 'second'));
assertEquals('01 second ago', longAuto.format(-1, 'second'));
assertEquals('now', longAuto.format(0, 'second'));
assertEquals('now', longAuto.format(-0, 'second'));
assertEquals('in 01 second', longAuto.format(1, 'second'));
assertEquals('in 02 seconds', longAuto.format(2, 'second'));
assertEquals('in 345 seconds', longAuto.format(345, 'second'));
assertEquals('03 minutes ago', longAuto.format(-3, 'minute'));
assertEquals('02 minutes ago', longAuto.format(-2, 'minute'));
assertEquals('01 minute ago', longAuto.format(-1, 'minute'));
assertEquals('in 00 minutes', longAuto.format(0, 'minute'));
assertEquals('00 minutes ago', longAuto.format(-0, 'minute'));
assertEquals('in 01 minute', longAuto.format(1, 'minute'));
assertEquals('in 02 minutes', longAuto.format(2, 'minute'));
assertEquals('in 345 minutes', longAuto.format(345, 'minute'));
assertEquals('03 hours ago', longAuto.format(-3, 'hour'));
assertEquals('02 hours ago', longAuto.format(-2, 'hour'));
assertEquals('01 hour ago', longAuto.format(-1, 'hour'));
assertEquals('in 00 hours', longAuto.format(0, 'hour'));
assertEquals('00 hours ago', longAuto.format(-0, 'hour'));
assertEquals('in 01 hour', longAuto.format(1, 'hour'));
assertEquals('in 02 hours', longAuto.format(2, 'hour'));
assertEquals('in 345 hours', longAuto.format(345, 'hour'));
assertEquals('03 days ago', longAuto.format(-3, 'day'));
assertEquals('02 days ago', longAuto.format(-2, 'day'));
assertEquals('yesterday', longAuto.format(-1, 'day'));
assertEquals('today', longAuto.format(0, 'day'));
assertEquals('today', longAuto.format(-0, 'day'));
assertEquals('tomorrow', longAuto.format(1, 'day'));
assertEquals('in 02 days', longAuto.format(2, 'day'));
assertEquals('in 345 days', longAuto.format(345, 'day'));
assertEquals('03 weeks ago', longAuto.format(-3, 'week'));
assertEquals('02 weeks ago', longAuto.format(-2, 'week'));
assertEquals('last week', longAuto.format(-1, 'week'));
assertEquals('this week', longAuto.format(0, 'week'));
assertEquals('this week', longAuto.format(-0, 'week'));
assertEquals('next week', longAuto.format(1, 'week'));
assertEquals('in 02 weeks', longAuto.format(2, 'week'));
assertEquals('in 345 weeks', longAuto.format(345, 'week'));
assertEquals('03 months ago', longAuto.format(-3, 'month'));
assertEquals('02 months ago', longAuto.format(-2, 'month'));
assertEquals('last month', longAuto.format(-1, 'month'));
assertEquals('this month', longAuto.format(0, 'month'));
assertEquals('this month', longAuto.format(-0, 'month'));
assertEquals('next month', longAuto.format(1, 'month'));
assertEquals('in 02 months', longAuto.format(2, 'month'));
assertEquals('in 345 months', longAuto.format(345, 'month'));
// "quarter" is not working in ICU now
// Tracked by ICU bug in http://bugs.icu-project.org/trac/ticket/12171
/*
assertEquals('03 quarters ago', longAuto.format(-3, 'quarter'));
assertEquals('02 quarters ago', longAuto.format(-2, 'quarter'));
assertEquals('last quarter', longAuto.format(-1, 'quarter'));
assertEquals('this quarter', longAuto.format(0, 'quarter'));
assertEquals('this quarter', longAuto.format(-0, 'quarter'));
assertEquals('next quarter', longAuto.format(1, 'quarter'));
assertEquals('in 02 quarters', longAuto.format(2, 'quarter'));
assertEquals('in 345 quarters', longAuto.format(345, 'quarter'));
*/
assertEquals('03 years ago', longAuto.format(-3, 'year'));
assertEquals('02 years ago', longAuto.format(-2, 'year'));
assertEquals('last year', longAuto.format(-1, 'year'));
assertEquals('this year', longAuto.format(0, 'year'));
assertEquals('this year', longAuto.format(-0, 'year'));
assertEquals('next year', longAuto.format(1, 'year'));
assertEquals('in 02 years', longAuto.format(2, 'year'));
assertEquals('in 345 years', longAuto.format(345, 'year'));
let shortAuto = new Intl.RelativeTimeFormat(
"en", {style: "short", localeMatcher: 'lookup', numeric: 'auto'});
assertEquals('03 sec. ago', shortAuto.format(-3, 'second'));
assertEquals('02 sec. ago', shortAuto.format(-2, 'second'));
assertEquals('01 sec. ago', shortAuto.format(-1, 'second'));
assertEquals('now', shortAuto.format(0, 'second'));
assertEquals('now', shortAuto.format(-0, 'second'));
assertEquals('in 01 sec.', shortAuto.format(1, 'second'));
assertEquals('in 02 sec.', shortAuto.format(2, 'second'));
assertEquals('in 345 sec.', shortAuto.format(345, 'second'));
assertEquals('03 min. ago', shortAuto.format(-3, 'minute'));
assertEquals('02 min. ago', shortAuto.format(-2, 'minute'));
assertEquals('01 min. ago', shortAuto.format(-1, 'minute'));
assertEquals('in 00 min.', shortAuto.format(0, 'minute'));
assertEquals('00 min. ago', shortAuto.format(-0, 'minute'));
assertEquals('in 01 min.', shortAuto.format(1, 'minute'));
assertEquals('in 02 min.', shortAuto.format(2, 'minute'));
assertEquals('in 345 min.', shortAuto.format(345, 'minute'));
assertEquals('03 hr. ago', shortAuto.format(-3, 'hour'));
assertEquals('02 hr. ago', shortAuto.format(-2, 'hour'));
assertEquals('01 hr. ago', shortAuto.format(-1, 'hour'));
assertEquals('in 00 hr.', shortAuto.format(0, 'hour'));
assertEquals('00 hr. ago', shortAuto.format(-0, 'hour'));
assertEquals('in 01 hr.', shortAuto.format(1, 'hour'));
assertEquals('in 02 hr.', shortAuto.format(2, 'hour'));
assertEquals('in 345 hr.', shortAuto.format(345, 'hour'));
assertEquals('03 days ago', shortAuto.format(-3, 'day'));
assertEquals('02 days ago', shortAuto.format(-2, 'day'));
assertEquals('yesterday', shortAuto.format(-1, 'day'));
assertEquals('today', shortAuto.format(0, 'day'));
assertEquals('today', shortAuto.format(-0, 'day'));
assertEquals('tomorrow', shortAuto.format(1, 'day'));
assertEquals('in 02 days', shortAuto.format(2, 'day'));
assertEquals('in 345 days', shortAuto.format(345, 'day'));
assertEquals('03 wk. ago', shortAuto.format(-3, 'week'));
assertEquals('02 wk. ago', shortAuto.format(-2, 'week'));
assertEquals('last wk.', shortAuto.format(-1, 'week'));
assertEquals('this wk.', shortAuto.format(0, 'week'));
assertEquals('this wk.', shortAuto.format(-0, 'week'));
assertEquals('next wk.', shortAuto.format(1, 'week'));
assertEquals('in 02 wk.', shortAuto.format(2, 'week'));
assertEquals('in 345 wk.', shortAuto.format(345, 'week'));
assertEquals('03 mo. ago', shortAuto.format(-3, 'month'));
assertEquals('02 mo. ago', shortAuto.format(-2, 'month'));
assertEquals('last mo.', shortAuto.format(-1, 'month'));
assertEquals('this mo.', shortAuto.format(0, 'month'));
assertEquals('this mo.', shortAuto.format(-0, 'month'));
assertEquals('next mo.', shortAuto.format(1, 'month'));
assertEquals('in 02 mo.', shortAuto.format(2, 'month'));
assertEquals('in 345 mo.', shortAuto.format(345, 'month'));
// "quarter" is not working in ICU now
/*
assertEquals('03 qtrs. ago', shortAuto.format(-3, 'quarter'));
assertEquals('02 qtrs. ago', shortAuto.format(-2, 'quarter'));
assertEquals('last qtr.', shortAuto.format(-1, 'quarter'));
assertEquals('this qtr.', shortAuto.format(0, 'quarter'));
assertEquals('this qtr.', shortAuto.format(-0, 'quarter'));
assertEquals('next qtr.', shortAuto.format(1, 'quarter'));
assertEquals('in 02 qtrs.', shortAuto.format(2, 'quarter'));
assertEquals('in 345 qtrs.', shortAuto.format(345, 'quarter'));
*/
assertEquals('03 yr. ago', shortAuto.format(-3, 'year'));
assertEquals('02 yr. ago', shortAuto.format(-2, 'year'));
assertEquals('last yr.', shortAuto.format(-1, 'year'));
assertEquals('this yr.', shortAuto.format(0, 'year'));
assertEquals('this yr.', shortAuto.format(-0, 'year'));
assertEquals('next yr.', shortAuto.format(1, 'year'));
assertEquals('in 02 yr.', shortAuto.format(2, 'year'));
assertEquals('in 345 yr.', shortAuto.format(345, 'year'));
// Somehow in the 'en' locale, there are no valeu for -narrow
let narrowAuto = new Intl.RelativeTimeFormat(
"en", {style: "narrow", localeMatcher: 'lookup', numeric: 'auto'});
assertEquals('03 sec. ago', narrowAuto.format(-3, 'second'));
assertEquals('02 sec. ago', narrowAuto.format(-2, 'second'));
assertEquals('01 sec. ago', narrowAuto.format(-1, 'second'));
assertEquals('now', narrowAuto.format(0, 'second'));
assertEquals('now', narrowAuto.format(-0, 'second'));
assertEquals('in 01 sec.', narrowAuto.format(1, 'second'));
assertEquals('in 02 sec.', narrowAuto.format(2, 'second'));
assertEquals('in 345 sec.', narrowAuto.format(345, 'second'));
assertEquals('03 min. ago', narrowAuto.format(-3, 'minute'));
assertEquals('02 min. ago', narrowAuto.format(-2, 'minute'));
assertEquals('01 min. ago', narrowAuto.format(-1, 'minute'));
assertEquals('in 00 min.', narrowAuto.format(0, 'minute'));
assertEquals('00 min. ago', narrowAuto.format(-0, 'minute'));
assertEquals('in 01 min.', narrowAuto.format(1, 'minute'));
assertEquals('in 02 min.', narrowAuto.format(2, 'minute'));
assertEquals('in 345 min.', narrowAuto.format(345, 'minute'));
assertEquals('03 hr. ago', narrowAuto.format(-3, 'hour'));
assertEquals('02 hr. ago', narrowAuto.format(-2, 'hour'));
assertEquals('01 hr. ago', narrowAuto.format(-1, 'hour'));
assertEquals('in 00 hr.', narrowAuto.format(0, 'hour'));
assertEquals('00 hr. ago', narrowAuto.format(-0, 'hour'));
assertEquals('in 01 hr.', narrowAuto.format(1, 'hour'));
assertEquals('in 02 hr.', narrowAuto.format(2, 'hour'));
assertEquals('in 345 hr.', narrowAuto.format(345, 'hour'));
assertEquals('03 days ago', narrowAuto.format(-3, 'day'));
assertEquals('02 days ago', narrowAuto.format(-2, 'day'));
assertEquals('yesterday', narrowAuto.format(-1, 'day'));
assertEquals('today', narrowAuto.format(0, 'day'));
assertEquals('today', narrowAuto.format(-0, 'day'));
assertEquals('tomorrow', narrowAuto.format(1, 'day'));
assertEquals('in 02 days', narrowAuto.format(2, 'day'));
assertEquals('in 345 days', narrowAuto.format(345, 'day'));
assertEquals('03 wk. ago', narrowAuto.format(-3, 'week'));
assertEquals('02 wk. ago', narrowAuto.format(-2, 'week'));
assertEquals('last wk.', narrowAuto.format(-1, 'week'));
assertEquals('this wk.', narrowAuto.format(0, 'week'));
assertEquals('this wk.', narrowAuto.format(-0, 'week'));
assertEquals('next wk.', narrowAuto.format(1, 'week'));
assertEquals('in 02 wk.', narrowAuto.format(2, 'week'));
assertEquals('in 345 wk.', narrowAuto.format(345, 'week'));
assertEquals('03 mo. ago', narrowAuto.format(-3, 'month'));
assertEquals('02 mo. ago', narrowAuto.format(-2, 'month'));
assertEquals('last mo.', narrowAuto.format(-1, 'month'));
assertEquals('this mo.', narrowAuto.format(0, 'month'));
assertEquals('this mo.', narrowAuto.format(-0, 'month'));
assertEquals('next mo.', narrowAuto.format(1, 'month'));
assertEquals('in 02 mo.', narrowAuto.format(2, 'month'));
assertEquals('in 345 mo.', narrowAuto.format(345, 'month'));
// "quarter" is not working in ICU now
/*
assertEquals('03 qtrs. ago', narrowAuto.format(-3, 'quarter'));
assertEquals('02 qtrs. ago', narrowAuto.format(-2, 'quarter'));
assertEquals('last qtr.', narrowAuto.format(-1, 'quarter'));
assertEquals('this qtr.', narrowAuto.format(0, 'quarter'));
assertEquals('this qtr.', narrowAuto.format(-0, 'quarter'));
assertEquals('next qtr.', narrowAuto.format(1, 'quarter'));
assertEquals('in 02 qtrs.', narrowAuto.format(2, 'quarter'));
assertEquals('in 345 qtrs.', narrowAuto.format(345, 'quarter'));
*/
assertEquals('03 yr. ago', narrowAuto.format(-3, 'year'));
assertEquals('02 yr. ago', narrowAuto.format(-2, 'year'));
assertEquals('last yr.', narrowAuto.format(-1, 'year'));
assertEquals('this yr.', narrowAuto.format(0, 'year'));
assertEquals('this yr.', narrowAuto.format(-0, 'year'));
assertEquals('next yr.', narrowAuto.format(1, 'year'));
assertEquals('in 02 yr.', narrowAuto.format(2, 'year'));
assertEquals('in 345 yr.', narrowAuto.format(345, 'year'));
let longAlways = new Intl.RelativeTimeFormat(
"en", {style: "long", localeMatcher: 'lookup', numeric: 'always'});
assertEquals('03 seconds ago', longAlways.format(-3, 'second'));
assertEquals('02 seconds ago', longAlways.format(-2, 'second'));
assertEquals('01 second ago', longAlways.format(-1, 'second'));
assertEquals('in 00 seconds', longAlways.format(0, 'second'));
assertEquals('00 seconds ago', longAlways.format(-0, 'second'));
assertEquals('in 01 second', longAlways.format(1, 'second'));
assertEquals('in 02 seconds', longAlways.format(2, 'second'));
assertEquals('in 345 seconds', longAlways.format(345, 'second'));
assertEquals('03 minutes ago', longAlways.format(-3, 'minute'));
assertEquals('02 minutes ago', longAlways.format(-2, 'minute'));
assertEquals('01 minute ago', longAlways.format(-1, 'minute'));
assertEquals('in 00 minutes', longAlways.format(0, 'minute'));
assertEquals('00 minutes ago', longAlways.format(-0, 'minute'));
assertEquals('in 01 minute', longAlways.format(1, 'minute'));
assertEquals('in 02 minutes', longAlways.format(2, 'minute'));
assertEquals('in 345 minutes', longAlways.format(345, 'minute'));
assertEquals('03 hours ago', longAlways.format(-3, 'hour'));
assertEquals('02 hours ago', longAlways.format(-2, 'hour'));
assertEquals('01 hour ago', longAlways.format(-1, 'hour'));
assertEquals('in 00 hours', longAlways.format(0, 'hour'));
assertEquals('00 hours ago', longAlways.format(-0, 'hour'));
assertEquals('in 01 hour', longAlways.format(1, 'hour'));
assertEquals('in 02 hours', longAlways.format(2, 'hour'));
assertEquals('in 345 hours', longAlways.format(345, 'hour'));
assertEquals('03 days ago', longAlways.format(-3, 'day'));
assertEquals('02 days ago', longAlways.format(-2, 'day'));
assertEquals('01 day ago', longAlways.format(-1, 'day'));
assertEquals('in 00 days', longAlways.format(0, 'day'));
assertEquals('00 days ago', longAlways.format(-0, 'day'));
assertEquals('in 01 day', longAlways.format(1, 'day'));
assertEquals('in 02 days', longAlways.format(2, 'day'));
assertEquals('in 345 days', longAlways.format(345, 'day'));
assertEquals('03 weeks ago', longAlways.format(-3, 'week'));
assertEquals('02 weeks ago', longAlways.format(-2, 'week'));
assertEquals('01 week ago', longAlways.format(-1, 'week'));
assertEquals('in 00 weeks', longAlways.format(0, 'week'));
assertEquals('00 weeks ago', longAlways.format(-0, 'week'));
assertEquals('in 01 week', longAlways.format(1, 'week'));
assertEquals('in 02 weeks', longAlways.format(2, 'week'));
assertEquals('in 345 weeks', longAlways.format(345, 'week'));
assertEquals('03 months ago', longAlways.format(-3, 'month'));
assertEquals('02 months ago', longAlways.format(-2, 'month'));
assertEquals('01 month ago', longAlways.format(-1, 'month'));
assertEquals('in 00 months', longAlways.format(0, 'month'));
assertEquals('00 months ago', longAlways.format(-0, 'month'));
assertEquals('in 01 month', longAlways.format(1, 'month'));
assertEquals('in 02 months', longAlways.format(2, 'month'));
assertEquals('in 345 months', longAlways.format(345, 'month'));
// "quarter" is not working in ICU now
/*
assertEquals('03 quarters ago', longAlways.format(-3, 'quarter'));
assertEquals('02 quarters ago', longAlways.format(-2, 'quarter'));
assertEquals('01 quarter ago', longAlways.format(-1, 'quarter'));
assertEquals('in 00 quarters', longAlways.format(0, 'quarter'));
assertEquals('00 quarters ago', longAlways.format(-0, 'quarter'));
assertEquals('in 01 quarter', longAlways.format(1, 'quarter'));
assertEquals('in 02 quarters', longAlways.format(2, 'quarter'));
assertEquals('in 345 quarters', longAlways.format(345, 'quarter'));
*/
assertEquals('03 years ago', longAlways.format(-3, 'year'));
assertEquals('02 years ago', longAlways.format(-2, 'year'));
assertEquals('01 year ago', longAlways.format(-1, 'year'));
assertEquals('in 00 years', longAlways.format(0, 'year'));
assertEquals('00 years ago', longAlways.format(-0, 'year'));
assertEquals('in 01 year', longAlways.format(1, 'year'));
assertEquals('in 02 years', longAlways.format(2, 'year'));
assertEquals('in 345 years', longAlways.format(345, 'year'));
let shortAlways = new Intl.RelativeTimeFormat(
"en", {style: "short", localeMatcher: 'lookup', numeric: 'always'});
assertEquals('03 sec. ago', shortAlways.format(-3, 'second'));
assertEquals('02 sec. ago', shortAlways.format(-2, 'second'));
assertEquals('01 sec. ago', shortAlways.format(-1, 'second'));
assertEquals('in 00 sec.', shortAlways.format(0, 'second'));
assertEquals('00 sec. ago', shortAlways.format(-0, 'second'));
assertEquals('in 01 sec.', shortAlways.format(1, 'second'));
assertEquals('in 02 sec.', shortAlways.format(2, 'second'));
assertEquals('in 345 sec.', shortAlways.format(345, 'second'));
assertEquals('03 min. ago', shortAlways.format(-3, 'minute'));
assertEquals('02 min. ago', shortAlways.format(-2, 'minute'));
assertEquals('01 min. ago', shortAlways.format(-1, 'minute'));
assertEquals('in 00 min.', shortAlways.format(0, 'minute'));
assertEquals('00 min. ago', shortAlways.format(-0, 'minute'));
assertEquals('in 01 min.', shortAlways.format(1, 'minute'));
assertEquals('in 02 min.', shortAlways.format(2, 'minute'));
assertEquals('in 345 min.', shortAlways.format(345, 'minute'));
assertEquals('03 hr. ago', shortAlways.format(-3, 'hour'));
assertEquals('02 hr. ago', shortAlways.format(-2, 'hour'));
assertEquals('01 hr. ago', shortAlways.format(-1, 'hour'));
assertEquals('in 00 hr.', shortAlways.format(0, 'hour'));
assertEquals('00 hr. ago', shortAlways.format(-0, 'hour'));
assertEquals('in 01 hr.', shortAlways.format(1, 'hour'));
assertEquals('in 02 hr.', shortAlways.format(2, 'hour'));
assertEquals('in 345 hr.', shortAlways.format(345, 'hour'));
assertEquals('03 days ago', shortAlways.format(-3, 'day'));
assertEquals('02 days ago', shortAlways.format(-2, 'day'));
assertEquals('01 day ago', shortAlways.format(-1, 'day'));
assertEquals('in 00 days', shortAlways.format(0, 'day'));
assertEquals('00 days ago', shortAlways.format(-0, 'day'));
assertEquals('in 01 day', shortAlways.format(1, 'day'));
assertEquals('in 02 days', shortAlways.format(2, 'day'));
assertEquals('in 345 days', shortAlways.format(345, 'day'));
assertEquals('03 wk. ago', shortAlways.format(-3, 'week'));
assertEquals('02 wk. ago', shortAlways.format(-2, 'week'));
assertEquals('01 wk. ago', shortAlways.format(-1, 'week'));
assertEquals('in 00 wk.', shortAlways.format(0, 'week'));
assertEquals('00 wk. ago', shortAlways.format(-0, 'week'));
assertEquals('in 01 wk.', shortAlways.format(1, 'week'));
assertEquals('in 02 wk.', shortAlways.format(2, 'week'));
assertEquals('in 345 wk.', shortAlways.format(345, 'week'));
assertEquals('03 mo. ago', shortAlways.format(-3, 'month'));
assertEquals('02 mo. ago', shortAlways.format(-2, 'month'));
assertEquals('01 mo. ago', shortAlways.format(-1, 'month'));
assertEquals('in 00 mo.', shortAlways.format(0, 'month'));
assertEquals('00 mo. ago', shortAlways.format(-0, 'month'));
assertEquals('in 01 mo.', shortAlways.format(1, 'month'));
assertEquals('in 02 mo.', shortAlways.format(2, 'month'));
assertEquals('in 345 mo.', shortAlways.format(345, 'month'));
// "quarter" is not working in ICU now
/*
assertEquals('03 qtrs. ago', shortAlways.format(-3, 'quarter'));
assertEquals('02 qtrs. ago', shortAlways.format(-2, 'quarter'));
assertEquals('01 qtr. ago', shortAlways.format(-1, 'quarter'));
assertEquals('in 00 qtrs.', shortAlways.format(0, 'quarter'));
assertEquals('00 qtr. ago', shortAlways.format(-0, 'quarter'));
assertEquals('in 01 qtr.', shortAlways.format(1, 'quarter'));
assertEquals('in 02 qtrs.', shortAlways.format(2, 'quarter'));
assertEquals('in 345 qtrs.', shortAlways.format(345, 'quarter'));
*/
assertEquals('03 yr. ago', shortAlways.format(-3, 'year'));
assertEquals('02 yr. ago', shortAlways.format(-2, 'year'));
assertEquals('01 yr. ago', shortAlways.format(-1, 'year'));
assertEquals('in 00 yr.', shortAlways.format(0, 'year'));
assertEquals('00 yr. ago', shortAlways.format(-0, 'year'));
assertEquals('in 01 yr.', shortAlways.format(1, 'year'));
assertEquals('in 02 yr.', shortAlways.format(2, 'year'));
assertEquals('in 345 yr.', shortAlways.format(345, 'year'));
// Somehow in the 'en' locale, there are no valeu for -narrow
let narrowAlways = new Intl.RelativeTimeFormat(
"en", {style: "narrow", localeMatcher: 'lookup', numeric: 'always'});
assertEquals('03 sec. ago', narrowAlways.format(-3, 'second'));
assertEquals('02 sec. ago', narrowAlways.format(-2, 'second'));
assertEquals('01 sec. ago', narrowAlways.format(-1, 'second'));
assertEquals('in 00 sec.', narrowAlways.format(0, 'second'));
assertEquals('00 sec. ago', narrowAlways.format(-0, 'second'));
assertEquals('in 01 sec.', narrowAlways.format(1, 'second'));
assertEquals('in 02 sec.', narrowAlways.format(2, 'second'));
assertEquals('in 345 sec.', narrowAlways.format(345, 'second'));
assertEquals('03 min. ago', narrowAlways.format(-3, 'minute'));
assertEquals('02 min. ago', narrowAlways.format(-2, 'minute'));
assertEquals('01 min. ago', narrowAlways.format(-1, 'minute'));
assertEquals('in 00 min.', narrowAlways.format(0, 'minute'));
assertEquals('00 min. ago', narrowAlways.format(-0, 'minute'));
assertEquals('in 01 min.', narrowAlways.format(1, 'minute'));
assertEquals('in 02 min.', narrowAlways.format(2, 'minute'));
assertEquals('in 345 min.', narrowAlways.format(345, 'minute'));
assertEquals('03 hr. ago', narrowAlways.format(-3, 'hour'));
assertEquals('02 hr. ago', narrowAlways.format(-2, 'hour'));
assertEquals('01 hr. ago', narrowAlways.format(-1, 'hour'));
assertEquals('in 00 hr.', narrowAlways.format(0, 'hour'));
assertEquals('00 hr. ago', narrowAlways.format(-0, 'hour'));
assertEquals('in 01 hr.', narrowAlways.format(1, 'hour'));
assertEquals('in 02 hr.', narrowAlways.format(2, 'hour'));
assertEquals('in 345 hr.', narrowAlways.format(345, 'hour'));
assertEquals('03 days ago', narrowAlways.format(-3, 'day'));
assertEquals('02 days ago', narrowAlways.format(-2, 'day'));
assertEquals('01 day ago', narrowAlways.format(-1, 'day'));
assertEquals('in 00 days', narrowAlways.format(0, 'day'));
assertEquals('00 days ago', narrowAlways.format(-0, 'day'));
assertEquals('in 01 day', narrowAlways.format(1, 'day'));
assertEquals('in 02 days', narrowAlways.format(2, 'day'));
assertEquals('in 345 days', narrowAlways.format(345, 'day'));
assertEquals('03 wk. ago', narrowAlways.format(-3, 'week'));
assertEquals('02 wk. ago', narrowAlways.format(-2, 'week'));
assertEquals('01 wk. ago', narrowAlways.format(-1, 'week'));
assertEquals('in 00 wk.', narrowAlways.format(0, 'week'));
assertEquals('00 wk. ago', narrowAlways.format(-0, 'week'));
assertEquals('in 01 wk.', narrowAlways.format(1, 'week'));
assertEquals('in 02 wk.', narrowAlways.format(2, 'week'));
assertEquals('in 345 wk.', narrowAlways.format(345, 'week'));
assertEquals('03 mo. ago', narrowAlways.format(-3, 'month'));
assertEquals('02 mo. ago', narrowAlways.format(-2, 'month'));
assertEquals('01 mo. ago', narrowAlways.format(-1, 'month'));
assertEquals('in 00 mo.', narrowAlways.format(0, 'month'));
assertEquals('00 mo. ago', narrowAlways.format(-0, 'month'));
assertEquals('in 01 mo.', narrowAlways.format(1, 'month'));
assertEquals('in 02 mo.', narrowAlways.format(2, 'month'));
assertEquals('in 345 mo.', narrowAlways.format(345, 'month'));
// "quarter" is not working in ICU now
/*
assertEquals('03 qtrs. ago', narrowAlways.format(-3, 'quarter'));
assertEquals('02 qtrs. ago', narrowAlways.format(-2, 'quarter'));
assertEquals('01 qtr. ago', narrowAlways.format(-1, 'quarter'));
assertEquals('in 00 qtrs.', narrowAlways.format(0, 'quarter'));
assertEquals('00 qtr. ago', narrowAlways.format(-0, 'quarter'));
assertEquals('in 01 qtr.', narrowAlways.format(1, 'quarter'));
assertEquals('in 02 qtrs.', narrowAlways.format(2, 'quarter'));
assertEquals('in 345 qtrs.', narrowAlways.format(345, 'quarter'));
*/
assertEquals('03 yr. ago', narrowAlways.format(-3, 'year'));
assertEquals('02 yr. ago', narrowAlways.format(-2, 'year'));
assertEquals('01 yr. ago', narrowAlways.format(-1, 'year'));
assertEquals('in 00 yr.', narrowAlways.format(0, 'year'));
assertEquals('00 yr. ago', narrowAlways.format(-0, 'year'));
assertEquals('in 01 yr.', narrowAlways.format(1, 'year'));
assertEquals('in 02 yr.', narrowAlways.format(2, 'year'));
assertEquals('in 345 yr.', narrowAlways.format(345, 'year'));
var styleNumericCombinations = [
longAuto, shortAuto, narrowAuto, longAlways,
shortAlways, narrowAlways ];
var validUnits = [
'second', 'minute', 'hour', 'day', 'week', 'month', 'quarter', 'year'];
// Test these all throw RangeError
for (var i = 0; i < styleNumericCombinations.length; i++) {
for (var j = 0; j < validUnits.length; j++) {
assertThrows(() => styleNumericCombinations[i].format(NaN, validUnits[j]),
RangeError);
assertThrows(() => styleNumericCombinations[i].format(NaN, validUnits[j] + 's'),
RangeError);
assertThrows(() => styleNumericCombinations[i].format(NaN, validUnits[j]),
RangeError);
assertThrows(() => styleNumericCombinations[i].format(NaN, validUnits[j] + 's'),
RangeError);
}
}

View File

@ -0,0 +1,68 @@
// Copyright 2018 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-relative-time-format
// The following test are not part of the comformance. Just some output in
// English to verify the format does return something reasonable for English.
// It may be changed when we update the CLDR data.
// NOTE: These are UNSPECIFIED behavior in
// http://tc39.github.io/proposal-intl-relative-time/
// From Sample code in https://github.com/tc39/proposal-intl-relative-time#intlrelativetimeformatprototypeformattopartsvalue-unit
// // Format relative time using the day unit.
// rtf.formatToParts(-1, "day");
// // > [{ type: "literal", value: "yesterday"}]
let longAuto = new Intl.RelativeTimeFormat(
"en", {style: "long", localeMatcher: 'lookup', numeric: 'auto'});
var parts = longAuto.formatToParts(-1, "day");
assertEquals(1, parts.length);
assertEquals(2, Object.getOwnPropertyNames(parts[0]).length);
assertEquals('literal', parts[0].type);
assertEquals('yesterday', parts[0].value);
// From Sample code in https://github.com/tc39/proposal-intl-relative-time#intlrelativetimeformatprototypeformattopartsvalue-unit
// rtf.formatToParts(100, "day");
// // > [{ type: "literal", value: "in " }, { type: "integer", value: "100", unit: "day" }, { type: "literal", value: " days" }]
let longAlways = new Intl.RelativeTimeFormat(
"en", {style: "long", localeMatcher: 'lookup', numeric: 'always'});
parts = longAlways.formatToParts(100, "day");
assertEquals(3, parts.length);
assertEquals(2, Object.getOwnPropertyNames(parts[0]).length);
assertEquals('literal', parts[0].type);
assertEquals('in ', parts[0].value);
assertEquals(3, Object.getOwnPropertyNames(parts[1]).length);
assertEquals('integer', parts[1].type);
assertEquals('100', parts[1].value);
assertEquals('day', parts[1].unit);
assertEquals(2, Object.getOwnPropertyNames(parts[2]).length);
assertEquals('literal', parts[2].type);
assertEquals(' days', parts[2].value);
assertThrows(() => longAlways.format(NaN, 'second'), RangeError);
assertThrows(() => longAuto.format(NaN, 'second'), RangeError);
parts = longAlways.formatToParts(-10, "day");
assertEquals(2, parts.length);
assertEquals(3, Object.getOwnPropertyNames(parts[0]).length);
assertEquals('integer', parts[0].type);
assertEquals('10', parts[0].value);
assertEquals('day', parts[0].unit);
assertEquals(2, Object.getOwnPropertyNames(parts[1]).length);
assertEquals('literal', parts[1].type);
assertEquals(' days ago', parts[1].value);
parts = longAlways.formatToParts(-0, "day");
assertEquals(2, parts.length);
assertEquals(3, Object.getOwnPropertyNames(parts[0]).length);
assertEquals('integer', parts[0].type);
assertEquals('00', parts[0].value);
assertEquals('day', parts[0].unit);
assertEquals(2, Object.getOwnPropertyNames(parts[1]).length);
assertEquals('literal', parts[1].type);
assertEquals(' days ago', parts[1].value);

View File

@ -0,0 +1,82 @@
// Copyright 2018 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-relative-time-format
// Make sure that RelativeTimeFormat exposes all required properties. Those not specified
// should have undefined value.
// http://tc39.github.io/proposal-intl-relative-time/
let rtf = new Intl.RelativeTimeFormat();
// Test 1.4.4 Intl.RelativeTimeFormat.prototype.formatToParts( value, unit )
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'seconds')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'second')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'minutes')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'minute')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'hours')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'hour')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'days')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'day')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'weeks')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'week')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'months')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'month')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'quarters')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'quarter')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'years')));
assertEquals(true, Array.isArray(rtf.formatToParts(-1, 'year')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'seconds')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'second')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'minutes')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'minute')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'hours')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'hour')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'days')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'day')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'weeks')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'week')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'months')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'month')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'quarters')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'quarter')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'years')));
assertEquals(true, Array.isArray(rtf.formatToParts(-0, 'year')));
assertThrows(() => rtf.formatToParts(-1, 'decades'), RangeError);
assertThrows(() => rtf.formatToParts(-1, 'decade'), RangeError);
assertThrows(() => rtf.formatToParts(-1, 'centuries'), RangeError);
assertThrows(() => rtf.formatToParts(-1, 'century'), RangeError);
assertThrows(() => rtf.formatToParts(-1, 'milliseconds'), RangeError);
assertThrows(() => rtf.formatToParts(-1, 'millisecond'), RangeError);
assertThrows(() => rtf.formatToParts(-1, 'microseconds'), RangeError);
assertThrows(() => rtf.formatToParts(-1, 'microsecond'), RangeError);
assertThrows(() => rtf.formatToParts(-1, 'nanoseconds'), RangeError);
assertThrows(() => rtf.formatToParts(-1, 'nanosecond'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'seconds'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'second'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'minutes'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'minute'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'hours'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'hour'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'days'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'day'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'weeks'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'week'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'months'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'month'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'years'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'year'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'quarters'), RangeError);
assertThrows(() => rtf.formatToParts(NaN, 'quarter'), RangeError);
assertEquals(true, Array.isArray(rtf.formatToParts(100, 'day')));
rtf.formatToParts(100, 'day').forEach(function(part) {
assertEquals(true, part.type == 'literal' || part.type == 'integer');
assertEquals('string', typeof part.value);
if (part.type == 'integer') {
assertEquals('string', typeof part.unit);
}
});

View File

@ -0,0 +1,82 @@
// Copyright 2018 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-relative-time-format
// Make sure that RelativeTimeFormat exposes all required properties. Those not specified
// should have undefined value.
// http://tc39.github.io/proposal-intl-relative-time/
let rtf = new Intl.RelativeTimeFormat();
// Test 1.4.3 Intl.RelativeTimeFormat.prototype.format( value, unit )
assertEquals('string', typeof rtf.format(-1, 'seconds'));
assertEquals('string', typeof rtf.format(-1, 'second'));
assertEquals('string', typeof rtf.format(-1, 'minutes'));
assertEquals('string', typeof rtf.format(-1, 'minute'));
assertEquals('string', typeof rtf.format(-1, 'hours'));
assertEquals('string', typeof rtf.format(-1, 'hour'));
assertEquals('string', typeof rtf.format(-1, 'days'));
assertEquals('string', typeof rtf.format(-1, 'day'));
assertEquals('string', typeof rtf.format(-1, 'weeks'));
assertEquals('string', typeof rtf.format(-1, 'week'));
assertEquals('string', typeof rtf.format(-1, 'months'));
assertEquals('string', typeof rtf.format(-1, 'month'));
assertEquals('string', typeof rtf.format(-1, 'years'));
assertEquals('string', typeof rtf.format(-1, 'year'));
assertEquals('string', typeof rtf.format(-1, 'quarter'));
assertEquals('string', typeof rtf.format(-1, 'quarters'));
assertEquals('string', typeof rtf.format(-0, 'seconds'));
assertEquals('string', typeof rtf.format(-0, 'second'));
assertEquals('string', typeof rtf.format(-0, 'minutes'));
assertEquals('string', typeof rtf.format(-0, 'minute'));
assertEquals('string', typeof rtf.format(-0, 'hours'));
assertEquals('string', typeof rtf.format(-0, 'hour'));
assertEquals('string', typeof rtf.format(-0, 'days'));
assertEquals('string', typeof rtf.format(-0, 'day'));
assertEquals('string', typeof rtf.format(-0, 'weeks'));
assertEquals('string', typeof rtf.format(-0, 'week'));
assertEquals('string', typeof rtf.format(-0, 'months'));
assertEquals('string', typeof rtf.format(-0, 'month'));
assertEquals('string', typeof rtf.format(-0, 'years'));
assertEquals('string', typeof rtf.format(-0, 'year'));
assertEquals('string', typeof rtf.format(-0, 'quarter'));
assertEquals('string', typeof rtf.format(-0, 'quarters'));
assertThrows(() => rtf.format(NaN, 'seconds'), RangeError);
assertThrows(() => rtf.format(NaN, 'second'), RangeError);
assertThrows(() => rtf.format(NaN, 'minutes'), RangeError);
assertThrows(() => rtf.format(NaN, 'minute'), RangeError);
assertThrows(() => rtf.format(NaN, 'hours'), RangeError);
assertThrows(() => rtf.format(NaN, 'hour'), RangeError);
assertThrows(() => rtf.format(NaN, 'days'), RangeError);
assertThrows(() => rtf.format(NaN, 'day'), RangeError);
assertThrows(() => rtf.format(NaN, 'weeks'), RangeError);
assertThrows(() => rtf.format(NaN, 'week'), RangeError);
assertThrows(() => rtf.format(NaN, 'months'), RangeError);
assertThrows(() => rtf.format(NaN, 'month'), RangeError);
assertThrows(() => rtf.format(NaN, 'years'), RangeError);
assertThrows(() => rtf.format(NaN, 'year'), RangeError);
assertThrows(() => rtf.format(NaN, 'quarters'), RangeError);
assertThrows(() => rtf.format(NaN, 'quarter'), RangeError);
assertThrows(() => rtf.format(-1, 'decades'), RangeError);
assertThrows(() => rtf.format(-1, 'decade'), RangeError);
assertThrows(() => rtf.format(-1, 'centuries'), RangeError);
assertThrows(() => rtf.format(-1, 'century'), RangeError);
assertThrows(() => rtf.format(-1, 'milliseconds'), RangeError);
assertThrows(() => rtf.format(-1, 'millisecond'), RangeError);
assertThrows(() => rtf.format(-1, 'microseconds'), RangeError);
assertThrows(() => rtf.format(-1, 'microsecond'), RangeError);
assertThrows(() => rtf.format(-1, 'nanoseconds'), RangeError);
assertThrows(() => rtf.format(-1, 'nanosecond'), RangeError);
assertEquals('string', typeof rtf.format(5, 'day'));
assertEquals('string', typeof rtf.format('5', 'day'));
assertEquals('string', typeof rtf.format('-5', 'day'));
assertEquals('string', typeof rtf.format('534', 'day'));
assertEquals('string', typeof rtf.format('-534', 'day'));
//assertThrows(() => rtf.format('xyz', 'day'), RangeError);

View File

@ -475,12 +475,6 @@
'intl402/RelativeTimeFormat/constructor/supportedLocalesOf/length': [FAIL],
'intl402/RelativeTimeFormat/constructor/supportedLocalesOf/name': [FAIL],
'intl402/RelativeTimeFormat/constructor/supportedLocalesOf/prop-desc': [FAIL],
'intl402/RelativeTimeFormat/prototype/format/length': [FAIL],
'intl402/RelativeTimeFormat/prototype/format/name': [FAIL],
'intl402/RelativeTimeFormat/prototype/format/prop-desc': [FAIL],
'intl402/RelativeTimeFormat/prototype/formatToParts/length': [FAIL],
'intl402/RelativeTimeFormat/prototype/formatToParts/name': [FAIL],
'intl402/RelativeTimeFormat/prototype/formatToParts/prop-desc': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=7813
'built-ins/Array/prototype/lastIndexOf/calls-only-has-on-prototype-after-length-zeroed': [FAIL],