mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:57:45 +00:00
LibJS: Reject '-000000' as extended year
This is a normative change in the Temporal spec.
See: e60ef9e
This commit is contained in:
parent
8215b99df1
commit
19a2b32065
7 changed files with 60 additions and 17 deletions
|
@ -232,6 +232,7 @@
|
||||||
M(TemporalInvalidDurationString, "Invalid duration string '{}'") \
|
M(TemporalInvalidDurationString, "Invalid duration string '{}'") \
|
||||||
M(TemporalInvalidDurationStringFractionNotLast, "Invalid duration string '{}': fractional {} must not be proceeded by {}") \
|
M(TemporalInvalidDurationStringFractionNotLast, "Invalid duration string '{}': fractional {} must not be proceeded by {}") \
|
||||||
M(TemporalInvalidEpochNanoseconds, "Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17") \
|
M(TemporalInvalidEpochNanoseconds, "Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17") \
|
||||||
|
M(TemporalInvalidExtendedYearNegativeZero, "Invalid extended year, must not be negative zero") \
|
||||||
M(TemporalInvalidInstantString, "Invalid instant string '{}'") \
|
M(TemporalInvalidInstantString, "Invalid instant string '{}'") \
|
||||||
M(TemporalInvalidISODate, "Invalid ISO date") \
|
M(TemporalInvalidISODate, "Invalid ISO date") \
|
||||||
M(TemporalInvalidMonthCode, "Invalid month code") \
|
M(TemporalInvalidMonthCode, "Invalid month code") \
|
||||||
|
|
|
@ -1142,43 +1142,47 @@ ThrowCompletionOr<ISODateTime> parse_iso_date_time(GlobalObject& global_object,
|
||||||
else
|
else
|
||||||
normalized_year = year_part.value_or("0");
|
normalized_year = year_part.value_or("0");
|
||||||
|
|
||||||
// 7. Set year to ! ToIntegerOrInfinity(year).
|
// 7. If ! SameValue(year, "-000000") is true, throw a RangeError exception.
|
||||||
|
if (normalized_year == "-000000"sv)
|
||||||
|
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidExtendedYearNegativeZero);
|
||||||
|
|
||||||
|
// 8. Set year to ! ToIntegerOrInfinity(year).
|
||||||
auto year = *normalized_year.to_int<i32>();
|
auto year = *normalized_year.to_int<i32>();
|
||||||
|
|
||||||
u8 month;
|
u8 month;
|
||||||
// 8. If month is undefined, then
|
// 9. If month is undefined, then
|
||||||
if (!month_part.has_value()) {
|
if (!month_part.has_value()) {
|
||||||
// a. Set month to 1.
|
// a. Set month to 1.
|
||||||
month = 1;
|
month = 1;
|
||||||
}
|
}
|
||||||
// 9. Else,
|
// 10. Else,
|
||||||
else {
|
else {
|
||||||
// a. Set month to ! ToIntegerOrInfinity(month).
|
// a. Set month to ! ToIntegerOrInfinity(month).
|
||||||
month = *month_part->to_uint<u8>();
|
month = *month_part->to_uint<u8>();
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 day;
|
u8 day;
|
||||||
// 10. If day is undefined, then
|
// 11. If day is undefined, then
|
||||||
if (!day_part.has_value()) {
|
if (!day_part.has_value()) {
|
||||||
// a. Set day to 1.
|
// a. Set day to 1.
|
||||||
day = 1;
|
day = 1;
|
||||||
}
|
}
|
||||||
// 11. Else,
|
// 12. Else,
|
||||||
else {
|
else {
|
||||||
// a. Set day to ! ToIntegerOrInfinity(day).
|
// a. Set day to ! ToIntegerOrInfinity(day).
|
||||||
day = *day_part->to_uint<u8>();
|
day = *day_part->to_uint<u8>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 12. Set hour to ! ToIntegerOrInfinity(hour).
|
// 13. Set hour to ! ToIntegerOrInfinity(hour).
|
||||||
u8 hour = *hour_part.value_or("0"sv).to_uint<u8>();
|
u8 hour = *hour_part.value_or("0"sv).to_uint<u8>();
|
||||||
|
|
||||||
// 13. Set minute to ! ToIntegerOrInfinity(minute).
|
// 14. Set minute to ! ToIntegerOrInfinity(minute).
|
||||||
u8 minute = *minute_part.value_or("0"sv).to_uint<u8>();
|
u8 minute = *minute_part.value_or("0"sv).to_uint<u8>();
|
||||||
|
|
||||||
// 14. Set second to ! ToIntegerOrInfinity(second).
|
// 15. Set second to ! ToIntegerOrInfinity(second).
|
||||||
u8 second = *second_part.value_or("0"sv).to_uint<u8>();
|
u8 second = *second_part.value_or("0"sv).to_uint<u8>();
|
||||||
|
|
||||||
// 15. If second is 60, then
|
// 16. If second is 60, then
|
||||||
if (second == 60) {
|
if (second == 60) {
|
||||||
// a. Set second to 59.
|
// a. Set second to 59.
|
||||||
second = 59;
|
second = 59;
|
||||||
|
@ -1187,7 +1191,7 @@ ThrowCompletionOr<ISODateTime> parse_iso_date_time(GlobalObject& global_object,
|
||||||
u16 millisecond;
|
u16 millisecond;
|
||||||
u16 microsecond;
|
u16 microsecond;
|
||||||
u16 nanosecond;
|
u16 nanosecond;
|
||||||
// 16. If fraction is not undefined, then
|
// 17. If fraction is not undefined, then
|
||||||
if (fraction_part.has_value()) {
|
if (fraction_part.has_value()) {
|
||||||
// a. Set fraction to the string-concatenation of the previous value of fraction and the string "000000000".
|
// a. Set fraction to the string-concatenation of the previous value of fraction and the string "000000000".
|
||||||
auto fraction = String::formatted("{}000000000", *fraction_part);
|
auto fraction = String::formatted("{}000000000", *fraction_part);
|
||||||
|
@ -1201,7 +1205,7 @@ ThrowCompletionOr<ISODateTime> parse_iso_date_time(GlobalObject& global_object,
|
||||||
// g. Set nanosecond to ! ToIntegerOrInfinity(nanosecond).
|
// g. Set nanosecond to ! ToIntegerOrInfinity(nanosecond).
|
||||||
nanosecond = *fraction.substring(7, 3).to_uint<u16>();
|
nanosecond = *fraction.substring(7, 3).to_uint<u16>();
|
||||||
}
|
}
|
||||||
// 17. Else,
|
// 18. Else,
|
||||||
else {
|
else {
|
||||||
// a. Let millisecond be 0.
|
// a. Let millisecond be 0.
|
||||||
millisecond = 0;
|
millisecond = 0;
|
||||||
|
@ -1211,15 +1215,15 @@ ThrowCompletionOr<ISODateTime> parse_iso_date_time(GlobalObject& global_object,
|
||||||
nanosecond = 0;
|
nanosecond = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 18. If ! IsValidISODate(year, month, day) is false, throw a RangeError exception.
|
// 19. If ! IsValidISODate(year, month, day) is false, throw a RangeError exception.
|
||||||
if (!is_valid_iso_date(year, month, day))
|
if (!is_valid_iso_date(year, month, day))
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidISODate);
|
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidISODate);
|
||||||
|
|
||||||
// 19. If ! IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is false, throw a RangeError exception.
|
// 20. If ! IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is false, throw a RangeError exception.
|
||||||
if (!is_valid_time(hour, minute, second, millisecond, microsecond, nanosecond))
|
if (!is_valid_time(hour, minute, second, millisecond, microsecond, nanosecond))
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidTime);
|
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidTime);
|
||||||
|
|
||||||
// 20. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day, [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond, [[Calendar]]: calendar }.
|
// 21. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day, [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond, [[Calendar]]: calendar }.
|
||||||
return ISODateTime { .year = year, .month = month, .day = day, .hour = hour, .minute = minute, .second = second, .millisecond = millisecond, .microsecond = microsecond, .nanosecond = nanosecond, .calendar = Optional<String>(move(calendar_part)) };
|
return ISODateTime { .year = year, .month = month, .day = day, .hour = hour, .minute = minute, .second = second, .millisecond = millisecond, .microsecond = microsecond, .nanosecond = nanosecond, .calendar = Optional<String>(move(calendar_part)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1678,16 +1682,24 @@ ThrowCompletionOr<TemporalYearMonth> parse_temporal_year_month_string(GlobalObje
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidYearMonthString, iso_string);
|
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidYearMonthString, iso_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. If isoString contains a UTCDesignator, then
|
// FIXME: I don't think this check makes sense - the TemporalYearMonthString syntax check above
|
||||||
|
// should rule out a string that's '-000000'; it requires a month with a non-zero digit
|
||||||
|
// in it, and the normalized year is checked separately in ParseISODateTime below.
|
||||||
|
// :yakshrug:
|
||||||
|
// 3. If ! SameValue(isoString, "-000000") is true, throw a RangeError exception.
|
||||||
|
if (iso_string == "-000000"sv)
|
||||||
|
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidExtendedYearNegativeZero);
|
||||||
|
|
||||||
|
// 4. If isoString contains a UTCDesignator, then
|
||||||
if (parse_result->utc_designator.has_value()) {
|
if (parse_result->utc_designator.has_value()) {
|
||||||
// a. Throw a RangeError exception.
|
// a. Throw a RangeError exception.
|
||||||
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidYearMonthStringUTCDesignator, iso_string);
|
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidYearMonthStringUTCDesignator, iso_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Let result be ? ParseISODateTime(isoString).
|
// 5. Let result be ? ParseISODateTime(isoString).
|
||||||
auto result = TRY(parse_iso_date_time(global_object, *parse_result));
|
auto result = TRY(parse_iso_date_time(global_object, *parse_result));
|
||||||
|
|
||||||
// 5. Return the Record { [[Year]]: result.[[Year]], [[Month]]: result.[[Month]], [[Day]]: result.[[Day]], [[Calendar]]: result.[[Calendar]] }.
|
// 6. Return the Record { [[Year]]: result.[[Year]], [[Month]]: result.[[Month]], [[Day]]: result.[[Day]], [[Calendar]]: result.[[Calendar]] }.
|
||||||
return TemporalYearMonth { .year = result.year, .month = result.month, .day = result.day, .calendar = move(result.calendar) };
|
return TemporalYearMonth { .year = result.year, .month = result.month, .day = result.day, .calendar = move(result.calendar) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,4 +49,10 @@ describe("errors", () => {
|
||||||
Temporal.PlainDate.from("foo");
|
Temporal.PlainDate.from("foo");
|
||||||
}).toThrowWithMessage(RangeError, "Invalid date string 'foo'");
|
}).toThrowWithMessage(RangeError, "Invalid date string 'foo'");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("extended year must not be negative zero", () => {
|
||||||
|
expect(() => {
|
||||||
|
Temporal.PlainDate.from("-000000-01-01");
|
||||||
|
}).toThrowWithMessage(RangeError, "Invalid extended year, must not be negative zero");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -186,4 +186,10 @@ describe("errors", () => {
|
||||||
"Invalid date time string '2021-07-06T23:42:01Z': must not contain a UTC designator"
|
"Invalid date time string '2021-07-06T23:42:01Z': must not contain a UTC designator"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("extended year must not be negative zero", () => {
|
||||||
|
expect(() => {
|
||||||
|
Temporal.PlainDateTime.from("-000000-01-01");
|
||||||
|
}).toThrowWithMessage(RangeError, "Invalid extended year, must not be negative zero");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -75,4 +75,10 @@ describe("errors", () => {
|
||||||
"Invalid month day string '2021-07-06T23:42:01Z': must not contain a UTC designator"
|
"Invalid month day string '2021-07-06T23:42:01Z': must not contain a UTC designator"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("extended year must not be negative zero", () => {
|
||||||
|
expect(() => {
|
||||||
|
Temporal.PlainMonthDay.from("-000000-01-01");
|
||||||
|
}).toThrowWithMessage(RangeError, "Invalid extended year, must not be negative zero");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -65,6 +65,12 @@ describe("errors", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("extended year must not be negative zero", () => {
|
||||||
|
expect(() => {
|
||||||
|
Temporal.PlainTime.from("-000000-01-01T00:00:00");
|
||||||
|
}).toThrowWithMessage(RangeError, "Invalid extended year, must not be negative zero");
|
||||||
|
});
|
||||||
|
|
||||||
test("ambiguous string must contain a time designator", () => {
|
test("ambiguous string must contain a time designator", () => {
|
||||||
const values = [
|
const values = [
|
||||||
// YYYY-MM or HHMM-UU
|
// YYYY-MM or HHMM-UU
|
||||||
|
|
|
@ -101,4 +101,10 @@ describe("errors", () => {
|
||||||
"Invalid year month string '2021-07-06T23:42:01Z': must not contain a UTC designator"
|
"Invalid year month string '2021-07-06T23:42:01Z': must not contain a UTC designator"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("extended year must not be negative zero", () => {
|
||||||
|
expect(() => {
|
||||||
|
Temporal.PlainYearMonth.from("-000000-01");
|
||||||
|
}).toThrowWithMessage(RangeError, "Invalid extended year, must not be negative zero");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue