Fix Date BiDi format

1. Add `toISOString` to `v8::Date`.
2. Switch serialization to `ISOString`.

Bug: v8:13043
Change-Id: I8a852f4a4a46bb3b8e5d52ef3cdffde7a408b403
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3749203
Auto-Submit: Maksim Sadym <sadym@chromium.org>
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81647}
This commit is contained in:
Maksim Sadym 2022-07-11 12:14:46 +00:00 committed by V8 LUCI CQ
parent 110fa66e13
commit 126d477925
8 changed files with 78 additions and 55 deletions

View File

@ -27,6 +27,11 @@ class V8_EXPORT Date : public Object {
*/
double ValueOf() const;
/**
* Generates ISO string representation.
*/
v8::Local<v8::String> ToISOString() const;
V8_INLINE static Date* Cast(Value* value) {
#ifdef V8_ENABLE_CHECKS
CheckCast(value);

View File

@ -7280,6 +7280,20 @@ double v8::Date::ValueOf() const {
return jsdate->value().Number();
}
v8::Local<v8::String> v8::Date::ToISOString() const {
i::Handle<i::Object> obj = Utils::OpenHandle(this);
i::Handle<i::JSDate> jsdate = i::Handle<i::JSDate>::cast(obj);
i::Isolate* i_isolate = jsdate->GetIsolate();
API_RCS_SCOPE(i_isolate, Date, NumberValue);
i::DateBuffer buffer =
i::ToDateString(jsdate->value().Number(), i_isolate->date_cache(),
i::ToDateStringMode::kISODateAndTime);
i::Handle<i::String> str = i_isolate->factory()
->NewStringFromUtf8(base::VectorOf(buffer))
.ToHandleChecked();
return Utils::ToLocal(str);
}
// Assert that the static TimeZoneDetection cast in
// DateTimeConfigurationChangeNotification is valid.
#define TIME_ZONE_DETECTION_ASSERT_EQ(value) \

View File

@ -684,22 +684,10 @@ BUILTIN(DatePrototypeToISOString) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
}
int64_t const time_ms = static_cast<int64_t>(time_val);
int year, month, day, weekday, hour, min, sec, ms;
isolate->date_cache()->BreakDownTime(time_ms, &year, &month, &day, &weekday,
&hour, &min, &sec, &ms);
char buffer[128];
if (year >= 0 && year <= 9999) {
SNPrintF(base::ArrayVector(buffer), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
year, month + 1, day, hour, min, sec, ms);
} else if (year < 0) {
SNPrintF(base::ArrayVector(buffer), "-%06d-%02d-%02dT%02d:%02d:%02d.%03dZ",
-year, month + 1, day, hour, min, sec, ms);
} else {
SNPrintF(base::ArrayVector(buffer), "+%06d-%02d-%02dT%02d:%02d:%02d.%03dZ",
year, month + 1, day, hour, min, sec, ms);
}
return *isolate->factory()->NewStringFromAsciiChecked(buffer);
DateBuffer buffer = ToDateString(time_val, isolate->date_cache(),
ToDateStringMode::kISODateAndTime);
RETURN_RESULT_OR_FAILURE(
isolate, isolate->factory()->NewStringFromUtf8(base::VectorOf(buffer)));
}
// ES6 section 20.3.4.41 Date.prototype.toString ( )

View File

@ -559,9 +559,10 @@ DateBuffer ToDateString(double time_val, DateCache* date_cache,
return FormatDate("Invalid Date");
}
int64_t time_ms = static_cast<int64_t>(time_val);
int64_t local_time_ms = mode != ToDateStringMode::kUTCDateAndTime
? date_cache->ToLocal(time_ms)
: time_ms;
int64_t local_time_ms = (mode == ToDateStringMode::kUTCDateAndTime ||
mode == ToDateStringMode::kISODateAndTime)
? time_ms
: date_cache->ToLocal(time_ms);
int year, month, day, weekday, hour, min, sec, ms;
date_cache->BreakDownTime(local_time_ms, &year, &month, &day, &weekday, &hour,
&min, &sec, &ms);
@ -590,6 +591,17 @@ DateBuffer ToDateString(double time_val, DateCache* date_cache,
: "%s, %02d %s %04d %02d:%02d:%02d GMT",
kShortWeekDays[weekday], day, kShortMonths[month], year,
hour, min, sec);
case ToDateStringMode::kISODateAndTime:
if (year >= 0 && year <= 9999) {
return FormatDate("%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", year,
month + 1, day, hour, min, sec, ms);
} else if (year < 0) {
return FormatDate("-%06d-%02d-%02dT%02d:%02d:%02d.%03dZ", -year,
month + 1, day, hour, min, sec, ms);
} else {
return FormatDate("+%06d-%02d-%02dT%02d:%02d:%02d.%03dZ", year,
month + 1, day, hour, min, sec, ms);
}
}
UNREACHABLE();
}

View File

@ -255,6 +255,7 @@ enum class ToDateStringMode {
kLocalTime,
kLocalDateAndTime,
kUTCDateAndTime,
kISODateAndTime
};
// ES6 section 20.3.4.41.1 ToDateString(tv)

View File

@ -40,12 +40,10 @@ std::unique_ptr<protocol::Value> DescriptionForDate(
v8::Local<v8::Context> context, v8::Local<v8::Date> date) {
v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch tryCatch(isolate);
v8::Local<v8::String> description;
bool success = date->ToString(context).ToLocal(&description);
DCHECK(success);
USE(success);
return protocol::StringValue::create(toProtocolString(isolate, description));
v8::Local<v8::String> dateISOString = date->ToISOString();
return protocol::StringValue::create(
toProtocolString(isolate, dateISOString));
}
String16 _descriptionForRegExpFlags(v8::Local<v8::RegExp> value) {

View File

@ -306,14 +306,39 @@ Runtime.callFunctionOn
}
Running test: Date
testing date: Thu Apr 07 2022 16:16:25 GMT+1100
Expected date in GMT: Thu, 07 Apr 2022 05:16:25 GMT
Date type as expected: true
Date value as expected: true
testing date: Thu Apr 07 2022 16:16:25 GMT-1100
Expected date in GMT: Fri, 08 Apr 2022 03:16:25 GMT
Date type as expected: true
Date value as expected: true
testing expression: new Date('Thu Apr 07 2022 16:17:18 GMT')
Runtime.evaluate
{
type : date
value : 2022-04-07T16:17:18.000Z
}
Runtime.callFunctionOn
{
type : date
value : 2022-04-07T16:17:18.000Z
}
testing expression: new Date('Thu Apr 07 2022 16:17:18 GMT+1100')
Runtime.evaluate
{
type : date
value : 2022-04-07T05:17:18.000Z
}
Runtime.callFunctionOn
{
type : date
value : 2022-04-07T05:17:18.000Z
}
testing expression: new Date('Thu Apr 07 2022 16:17:18 GMT-1100')
Runtime.evaluate
{
type : date
value : 2022-04-08T03:17:18.000Z
}
Runtime.callFunctionOn
{
type : date
value : 2022-04-08T03:17:18.000Z
}
Running test: Error
testing expression: [new Error(), new Error('qwe')]

View File

@ -35,9 +35,9 @@ InspectorTest.runAsyncTestSuite([
await testExpression("[new RegExp('ab+c'), new RegExp('ab+c', 'ig')]");
},
async function Date() {
// Serialization depends on the timezone, so0 manual vreification is needed.
await testDate("Thu Apr 07 2022 16:16:25 GMT+1100");
await testDate("Thu Apr 07 2022 16:16:25 GMT-1100");
await testExpression("new Date('Thu Apr 07 2022 16:17:18 GMT')");
await testExpression("new Date('Thu Apr 07 2022 16:17:18 GMT+1100')");
await testExpression("new Date('Thu Apr 07 2022 16:17:18 GMT-1100')");
},
async function Error() {
await testExpression("[new Error(), new Error('qwe')]");
@ -73,26 +73,6 @@ InspectorTest.runAsyncTestSuite([
await testExpression("{key_level_1: {key_level_2: {key_level_3: 'value_level_3'}}}");
}]);
async function testDate(dateStr) {
// TODO(sadym): make the test timezone-agnostic. Current approach is not 100% valid, as it relies on the `date.ToString` implementation.
InspectorTest.logMessage("testing date: " + dateStr);
const serializedDate = (await serializeViaEvaluate("new Date('" + dateStr + "')")).result.result.webDriverValue;
// Expected format: {
// type: "date"
// value: "Fri Apr 08 2022 03:16:25 GMT+0000 (Coordinated Universal Time)"
// }
const expectedDateStr = new Date(dateStr).toString();
InspectorTest.logMessage("Expected date in GMT: " + (new Date(dateStr).toGMTString()));
InspectorTest.logMessage("Date type as expected: " + (serializedDate.type === "date"));
if (serializedDate.value === expectedDateStr) {
InspectorTest.logMessage("Date value as expected: " + (serializedDate.value === expectedDateStr));
} else {
InspectorTest.logMessage("Error. Eexpected " + expectedDateStr + ", but was " + serializedDate.value);
}
}
async function serializeViaEvaluate(expression) {
return await Protocol.Runtime.evaluate({
expression: "("+expression+")",