commit 94e008929aede14ae1b4c6dadc2f6da4a0152701
parent 0624561f4306c2efb34f28da4cd41473b7c78459
Author: André Bargull <andre.bargull@gmail.com>
Date: Thu, 27 Nov 2025 10:03:33 +0000
Bug 1998672 - Part 2: Validate unit-valued options after all options are read. r=spidermonkey-reviewers,dminor
Implements the changes from:
- https://github.com/tc39/proposal-temporal/pull/3130
- https://github.com/tc39/proposal-temporal/pull/3135
Differential Revision: https://phabricator.services.mozilla.com/D272019
Diffstat:
8 files changed, 370 insertions(+), 280 deletions(-)
diff --git a/js/src/builtin/temporal/Duration.cpp b/js/src/builtin/temporal/Duration.cpp
@@ -3414,12 +3414,12 @@ static bool Duration_subtract(JSContext* cx, unsigned argc, Value* vp) {
static bool Duration_round(JSContext* cx, const CallArgs& args) {
auto duration = ToDuration(&args.thisv().toObject().as<DurationObject>());
- // Step 17. (Reordered)
+ // Step 18. (Reordered)
auto existingLargestUnit = DefaultTemporalLargestUnit(duration);
- // Steps 3-25.
- auto smallestUnit = TemporalUnit::Auto;
- TemporalUnit largestUnit;
+ // Steps 3-26.
+ auto smallestUnit = TemporalUnit::Unset;
+ auto largestUnit = TemporalUnit::Unset;
auto roundingMode = TemporalRoundingMode::HalfExpand;
auto roundingIncrement = Increment{1};
Rooted<PlainDate> plainRelativeTo(cx);
@@ -3432,26 +3432,31 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
// Step 15.
Rooted<JSString*> paramString(cx, args[0].toString());
if (!GetTemporalUnitValuedOption(
- cx, paramString, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::DateTime, &smallestUnit)) {
+ cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) {
return false;
}
- // Step 16. (Not applicable)
+ // Step 16.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::DateTime)) {
+ return false;
+ }
- // Step 17. (Moved above)
+ // Step 17. (Not applicable)
- // Step 18.
+ // Step 18. (Moved above)
+
+ // Step 19.
auto defaultLargestUnit = std::min(existingLargestUnit, smallestUnit);
- // Step 19. (Not applicable)
+ // Step 20. (Not applicable)
- // Step 19.a. (Not applicable)
+ // Step 20.a. (Not applicable)
- // Step 19.b.
+ // Step 20.b.
largestUnit = defaultLargestUnit;
- // Steps 20-25. (Not applicable)
+ // Steps 21-26. (Not applicable)
} else {
// Steps 3 and 5.
Rooted<JSObject*> options(
@@ -3467,29 +3472,11 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
bool largestUnitPresent = true;
// Steps 8-9.
- //
- // Inlined GetTemporalUnitValuedOption and GetOption so we can more easily
- // detect an absent "largestUnit" value.
- Rooted<Value> largestUnitValue(cx);
- if (!GetProperty(cx, options, options, cx->names().largestUnit,
- &largestUnitValue)) {
+ if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::LargestUnit,
+ &largestUnit)) {
return false;
}
- if (!largestUnitValue.isUndefined()) {
- Rooted<JSString*> largestUnitStr(cx, JS::ToString(cx, largestUnitValue));
- if (!largestUnitStr) {
- return false;
- }
-
- largestUnit = TemporalUnit::Auto;
- if (!GetTemporalUnitValuedOption(
- cx, largestUnitStr, TemporalUnitKey::LargestUnit,
- TemporalUnitGroup::DateTime, &largestUnit)) {
- return false;
- }
- }
-
// Steps 10-12.
if (!GetTemporalRelativeToOption(cx, options, &plainRelativeTo,
&zonedRelativeTo)) {
@@ -3509,64 +3496,69 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
// Step 15.
if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::DateTime,
&smallestUnit)) {
return false;
}
// Step 16.
- if (smallestUnit == TemporalUnit::Auto) {
- // Step 16.a.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::DateTime)) {
+ return false;
+ }
+
+ // Step 17.
+ if (smallestUnit == TemporalUnit::Unset) {
+ // Step 17.a.
smallestUnitPresent = false;
- // Step 16.b.
+ // Step 17.b.
smallestUnit = TemporalUnit::Nanosecond;
}
- // Step 17. (Moved above)
+ // Step 18. (Moved above)
- // Step 18.
+ // Step 19.
auto defaultLargestUnit = std::min(existingLargestUnit, smallestUnit);
- // Steps 19-20.
- if (largestUnitValue.isUndefined()) {
- // Step 19.a.
+ // Steps 20-21.
+ if (largestUnit == TemporalUnit::Unset) {
+ // Step 20.a.
largestUnitPresent = false;
- // Step 19.b.
+ // Step 20.b.
largestUnit = defaultLargestUnit;
} else if (largestUnit == TemporalUnit::Auto) {
- // Step 20.a
+ // Step 21.a
largestUnit = defaultLargestUnit;
}
- // Step 21.
+ // Step 22.
if (!smallestUnitPresent && !largestUnitPresent) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_DURATION_MISSING_UNIT_SPECIFIER);
return false;
}
- // Step 22.
+ // Step 23.
if (largestUnit > smallestUnit) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_INVALID_UNIT_RANGE);
return false;
}
- // Steps 23-24.
+ // Steps 24-25.
if (smallestUnit > TemporalUnit::Day) {
- // Step 23.
+ // Step 24.
auto maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit);
- // Step 24.
+ // Step 25.
if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
false)) {
return false;
}
}
- // Step 25.
+ // Step 26.
if (roundingIncrement > Increment{1} && largestUnit != smallestUnit &&
smallestUnit <= TemporalUnit::Day) {
Int32ToCStringBuf cbuf;
@@ -3580,21 +3572,21 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
}
}
- // Step 26.
+ // Step 27.
if (zonedRelativeTo) {
- // Step 26.a.
+ // Step 27.a.
auto internalDuration = ToInternalDurationRecord(duration);
- // Steps 26.b-d. (Not applicable in our implementation.)
+ // Steps 27.b-d. (Not applicable in our implementation.)
- // Step 26.e.
+ // Step 27.e.
EpochNanoseconds targetEpochNs;
if (!AddZonedDateTime(cx, zonedRelativeTo, internalDuration,
&targetEpochNs)) {
return false;
}
- // Step 26.f.
+ // Step 27.f.
if (!DifferenceZonedDateTimeWithRounding(cx, zonedRelativeTo, targetEpochNs,
{
smallestUnit,
@@ -3606,10 +3598,10 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 26.g.
+ // Step 27.g.
largestUnit = std::max(largestUnit, TemporalUnit::Hour);
- // Step 26.h
+ // Step 27.h
Duration result;
if (!TemporalDurationFromInternal(cx, internalDuration, largestUnit,
&result)) {
@@ -3625,18 +3617,18 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
return true;
}
- // Step 27.
+ // Step 28.
if (plainRelativeTo) {
- // Step 27.a.
+ // Step 28.a.
auto internalDuration = ToInternalDurationRecordWith24HourDays(duration);
- // Step 27.b.
+ // Step 28.b.
auto targetTime = AddTime(Time{}, internalDuration.time);
- // Step 27.c.
+ // Step 28.c.
auto calendar = plainRelativeTo.calendar();
- // Step 27.d.
+ // Step 28.d.
auto dateDuration = DateDuration{
internalDuration.date.years,
internalDuration.date.months,
@@ -3645,17 +3637,17 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
};
MOZ_ASSERT(IsValidDuration(dateDuration));
- // 27.e.
+ // Step 28.e.
ISODate targetDate;
if (!CalendarDateAdd(cx, calendar, plainRelativeTo, dateDuration,
TemporalOverflow::Constrain, &targetDate)) {
return false;
}
- // Step 27.f.
+ // Step 28.f.
auto isoDateTime = ISODateTime{plainRelativeTo, {}};
- // Step 27.g.
+ // Step 28.g.
auto targetDateTime = ISODateTime{targetDate, targetTime.time};
// DifferencePlainDateTimeWithRounding, step 2.
@@ -3666,7 +3658,7 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 27.h.
+ // Step 28.h.
if (!DifferencePlainDateTimeWithRounding(cx, isoDateTime, targetDateTime,
calendar,
{
@@ -3679,7 +3671,7 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 27.i
+ // Step 28.i
Duration result;
if (!TemporalDurationFromInternal(cx, internalDuration, largestUnit,
&result)) {
@@ -3695,7 +3687,7 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
return true;
}
- // Step 28.
+ // Step 29.
if (existingLargestUnit < TemporalUnit::Day ||
largestUnit < TemporalUnit::Day) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
@@ -3704,16 +3696,16 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 29.
+ // Step 30.
MOZ_ASSERT(smallestUnit >= TemporalUnit::Day);
- // Step 30.
+ // Step 31.
auto internalDuration = ToInternalDurationRecordWith24HourDays(duration);
MOZ_ASSERT(internalDuration.date == DateDuration{});
- // Steps 31-32.
+ // Steps 32-33.
if (smallestUnit == TemporalUnit::Day) {
- // Steps 31.a-b.
+ // Steps 32.a-b.
constexpr auto nsPerDay = ToNanoseconds(TemporalUnit::Day);
auto rounded =
RoundNumberToIncrement(internalDuration.time.toNanoseconds(), nsPerDay,
@@ -3722,7 +3714,7 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
"rounded days fits in int64");
auto days = static_cast<int64_t>(rounded);
- // Step 31.c. (Inlined CreateDateDurationRecord)
+ // Step 32.c. (Inlined CreateDateDurationRecord)
if (std::abs(days) > TimeDuration::max().toDays()) {
JS_ReportErrorNumberASCII(
cx, GetErrorMessage, nullptr,
@@ -3732,21 +3724,21 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) {
auto dateDuration = DateDuration{0, 0, 0, days};
MOZ_ASSERT(IsValidDuration(dateDuration));
- // Step 31.d.
+ // Step 32.d.
internalDuration = {dateDuration, {}};
} else {
- // Step 32.a.
+ // Step 33.a.
TimeDuration timeDuration;
if (!RoundTimeDuration(cx, internalDuration.time, roundingIncrement,
smallestUnit, roundingMode, &timeDuration)) {
return false;
}
- // Step 32.b.
+ // Step 33.b.
internalDuration = {{}, timeDuration};
}
- // Step 32.
+ // Step 34.
Duration result;
if (!TemporalDurationFromInternal(cx, internalDuration, largestUnit,
&result)) {
@@ -3781,7 +3773,7 @@ static bool Duration_total(JSContext* cx, const CallArgs& args) {
// Steps 3-10.
Rooted<PlainDate> plainRelativeTo(cx);
Rooted<ZonedDateTime> zonedRelativeTo(cx);
- auto unit = TemporalUnit::Auto;
+ auto unit = TemporalUnit::Unset;
if (args.get(0).isString()) {
// Step 4. (Not applicable in our implementation.)
@@ -3791,7 +3783,7 @@ static bool Duration_total(JSContext* cx, const CallArgs& args) {
// Step 10.
Rooted<JSString*> paramString(cx, args[0].toString());
if (!GetTemporalUnitValuedOption(cx, paramString, TemporalUnitKey::Unit,
- TemporalUnitGroup::DateTime, &unit)) {
+ &unit)) {
return false;
}
} else {
@@ -3811,48 +3803,54 @@ static bool Duration_total(JSContext* cx, const CallArgs& args) {
// Step 10.
if (!GetTemporalUnitValuedOption(cx, totalOf, TemporalUnitKey::Unit,
- TemporalUnitGroup::DateTime, &unit)) {
+ &unit)) {
return false;
}
- if (unit == TemporalUnit::Auto) {
+ if (unit == TemporalUnit::Unset) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_MISSING_OPTION, "unit");
return false;
}
}
- // Steps 11-13.
+ // Step 11.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::Unit, unit,
+ TemporalUnitGroup::DateTime)) {
+ return false;
+ }
+
+ // Steps 12-14.
double total;
if (zonedRelativeTo) {
- // Step 11.a.
+ // Step 12.a.
auto internalDuration = ToInternalDurationRecord(duration);
- // Steps 11.b-d. (Not applicable in our implementation.)
+ // Steps 12.b-d. (Not applicable in our implementation.)
- // Step 11.e.
+ // Step 12.e.
EpochNanoseconds targetEpochNs;
if (!AddZonedDateTime(cx, zonedRelativeTo, internalDuration,
&targetEpochNs)) {
return false;
}
- // Step 11.f.
+ // Step 12.f.
if (!DifferenceZonedDateTimeWithTotal(cx, zonedRelativeTo, targetEpochNs,
unit, &total)) {
return false;
}
} else if (plainRelativeTo) {
- // Step 12.a.
+ // Step 13.a.
auto internalDuration = ToInternalDurationRecordWith24HourDays(duration);
- // Step 12.b.
+ // Step 13.b.
auto targetTime = AddTime(Time{}, internalDuration.time);
- // Step 12.c.
+ // Step 13.c.
auto calendar = plainRelativeTo.calendar();
- // Step 12.d.
+ // Step 13.d.
auto dateDuration = DateDuration{
internalDuration.date.years,
internalDuration.date.months,
@@ -3861,17 +3859,17 @@ static bool Duration_total(JSContext* cx, const CallArgs& args) {
};
MOZ_ASSERT(IsValidDuration(dateDuration));
- // Step 12.e.
+ // Step 13.e.
ISODate targetDate;
if (!CalendarDateAdd(cx, calendar, plainRelativeTo, dateDuration,
TemporalOverflow::Constrain, &targetDate)) {
return false;
}
- // Step 12.f.
+ // Step 13.f.
auto isoDateTime = ISODateTime{plainRelativeTo, {}};
- // Step 12.g.
+ // Step 13.g.
auto targetDateTime = ISODateTime{targetDate, targetTime.time};
// DifferencePlainDateTimeWithTotal, step 2.
@@ -3882,13 +3880,13 @@ static bool Duration_total(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 12.h.
+ // Step 13.h.
if (!DifferencePlainDateTimeWithTotal(cx, isoDateTime, targetDateTime,
calendar, unit, &total)) {
return false;
}
} else {
- // Steps 13.a-b.
+ // Steps 14.a-b.
if (duration.years != 0 || duration.months != 0 || duration.weeks != 0 ||
unit < TemporalUnit::Day) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
@@ -3897,14 +3895,14 @@ static bool Duration_total(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 13.c.
+ // Step 14.c.
auto internalDuration = ToInternalDurationRecordWith24HourDays(duration);
- // Step 13.d.
+ // Step 14.d.
total = TotalTimeDuration(internalDuration.time, unit);
}
- // Step 14.
+ // Step 15.
args.rval().setNumber(total);
return true;
}
@@ -3924,7 +3922,6 @@ static bool Duration_total(JSContext* cx, unsigned argc, Value* vp) {
static bool Duration_toString(JSContext* cx, const CallArgs& args) {
auto duration = ToDuration(&args.thisv().toObject().as<DurationObject>());
- // Steps 3-9.
SecondsStringPrecision precision = {Precision::Auto(),
TemporalUnit::Nanosecond, Increment{1}};
auto roundingMode = TemporalRoundingMode::Trunc;
@@ -3948,13 +3945,19 @@ static bool Duration_toString(JSContext* cx, const CallArgs& args) {
}
// Step 7.
- auto smallestUnit = TemporalUnit::Auto;
+ auto smallestUnit = TemporalUnit::Unset;
if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::Time, &smallestUnit)) {
+ &smallestUnit)) {
return false;
}
// Step 8.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::Time)) {
+ return false;
+ }
+
+ // Step 9.
if (smallestUnit == TemporalUnit::Hour ||
smallestUnit == TemporalUnit::Minute) {
const char* smallestUnitStr =
@@ -3965,35 +3968,35 @@ static bool Duration_toString(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 9.
+ // Step 10.
precision = ToSecondsStringPrecision(smallestUnit, digits);
}
MOZ_ASSERT(precision.unit >= TemporalUnit::Minute);
- // Steps 10-16.
+ // Steps 11-17.
auto roundedDuration = duration;
if (precision.unit != TemporalUnit::Nanosecond ||
precision.increment != Increment{1}) {
- // Step 11.
+ // Step 12.
auto largestUnit = DefaultTemporalLargestUnit(duration);
- // Step 12.
+ // Step 13.
auto internalDuration = ToInternalDurationRecord(duration);
- // Step 13.
+ // Step 14.
TimeDuration timeDuration;
if (!RoundTimeDuration(cx, internalDuration.time, precision.increment,
precision.unit, roundingMode, &timeDuration)) {
return false;
}
- // Step 14.
+ // Step 15.
internalDuration = {internalDuration.date, timeDuration};
- // Step 15.
+ // Step 16.
auto roundedLargestUnit = std::min(largestUnit, TemporalUnit::Second);
- // Step 16.
+ // Step 17.
if (!TemporalDurationFromInternal(cx, internalDuration, roundedLargestUnit,
&roundedDuration)) {
return false;
@@ -4001,7 +4004,7 @@ static bool Duration_toString(JSContext* cx, const CallArgs& args) {
MOZ_ASSERT(IsValidDuration(roundedDuration));
}
- // Steps 10.a. and 17.
+ // Steps 11.a. and 18.
JSString* str =
TemporalDurationToString(cx, roundedDuration, precision.precision);
if (!str) {
diff --git a/js/src/builtin/temporal/Instant.cpp b/js/src/builtin/temporal/Instant.cpp
@@ -951,8 +951,8 @@ static bool Instant_since(JSContext* cx, unsigned argc, Value* vp) {
static bool Instant_round(JSContext* cx, const CallArgs& args) {
auto epochNs = args.thisv().toObject().as<InstantObject>().epochNanoseconds();
- // Steps 3-16.
- auto smallestUnit = TemporalUnit::Auto;
+ // Steps 3-17.
+ auto smallestUnit = TemporalUnit::Unset;
auto roundingMode = TemporalRoundingMode::HalfExpand;
auto roundingIncrement = Increment{1};
if (args.get(0).isString()) {
@@ -960,13 +960,18 @@ static bool Instant_round(JSContext* cx, const CallArgs& args) {
// Step 9.
Rooted<JSString*> paramString(cx, args[0].toString());
- if (!GetTemporalUnitValuedOption(cx, paramString,
- TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::Time, &smallestUnit)) {
+ if (!GetTemporalUnitValuedOption(
+ cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) {
return false;
}
- // Steps 10-16. (Not applicable in our implementation.)
+ // Step 10.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::Time)) {
+ return false;
+ }
+
+ // Steps 11-17. (Not applicable in our implementation.)
} else {
// Steps 3 and 5.
Rooted<JSObject*> options(
@@ -987,30 +992,37 @@ static bool Instant_round(JSContext* cx, const CallArgs& args) {
// Step 9.
if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::Time, &smallestUnit)) {
+ &smallestUnit)) {
return false;
}
- if (smallestUnit == TemporalUnit::Auto) {
+
+ if (smallestUnit == TemporalUnit::Unset) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
return false;
}
- // Steps 10-15.
+ // Step 10.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::Time)) {
+ return false;
+ }
+
+ // Steps 11-16.
int64_t maximum = UnitsPerDay(smallestUnit);
- // Step 16.
+ // Step 17.
if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
true)) {
return false;
}
}
- // Step 17.
+ // Step 18.
auto roundedNs = RoundTemporalInstant(epochNs, roundingIncrement,
smallestUnit, roundingMode);
- // Step 18.
+ // Step 19.
auto* result = CreateTemporalInstant(cx, roundedNs);
if (!result) {
return false;
@@ -1084,42 +1096,49 @@ static bool Instant_toString(JSContext* cx, const CallArgs& args) {
}
// Step 7.
- auto smallestUnit = TemporalUnit::Auto;
+ auto smallestUnit = TemporalUnit::Unset;
if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::Time, &smallestUnit)) {
+ &smallestUnit)) {
return false;
}
// Step 8.
- if (smallestUnit == TemporalUnit::Hour) {
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
- JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
- "smallestUnit");
+ Rooted<Value> timeZoneValue(cx);
+ if (!GetProperty(cx, options, options, cx->names().timeZone,
+ &timeZoneValue)) {
return false;
}
// Step 9.
- Rooted<Value> value(cx);
- if (!GetProperty(cx, options, options, cx->names().timeZone, &value)) {
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::Time)) {
return false;
}
// Step 10.
- if (!value.isUndefined()) {
- if (!ToTemporalTimeZone(cx, value, &timeZone)) {
+ if (smallestUnit == TemporalUnit::Hour) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
+ "smallestUnit");
+ return false;
+ }
+
+ // Step 11.
+ if (!timeZoneValue.isUndefined()) {
+ if (!ToTemporalTimeZone(cx, timeZoneValue, &timeZone)) {
return false;
}
}
- // Step 11.
+ // Step 12.
precision = ToSecondsStringPrecision(smallestUnit, digits);
}
- // Steps 12-13.
+ // Steps 13-14.
auto roundedNs = RoundTemporalInstant(epochNs, precision.increment,
precision.unit, roundingMode);
- // Step 14.
+ // Step 15.
JSString* str =
TemporalInstantToString(cx, roundedNs, timeZone, precision.precision);
if (!str) {
diff --git a/js/src/builtin/temporal/PlainDateTime.cpp b/js/src/builtin/temporal/PlainDateTime.cpp
@@ -1725,8 +1725,8 @@ static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) {
auto dateTime = temporalDateTime->dateTime();
Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
- // Steps 3-12.
- auto smallestUnit = TemporalUnit::Auto;
+ // Steps 3-13.
+ auto smallestUnit = TemporalUnit::Unset;
auto roundingMode = TemporalRoundingMode::HalfExpand;
auto roundingIncrement = Increment{1};
if (args.get(0).isString()) {
@@ -1735,15 +1735,19 @@ static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) {
// Step 9.
Rooted<JSString*> paramString(cx, args[0].toString());
if (!GetTemporalUnitValuedOption(
- cx, paramString, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::DayTime, &smallestUnit)) {
+ cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) {
return false;
}
+ // Step 10.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::DayTime)) {
+ return false;
+ }
MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
smallestUnit <= TemporalUnit::Nanosecond);
- // Steps 6-8 and 10-12. (Implicit)
+ // Steps 6-8 and 11-13. (Implicit)
} else {
// Steps 3 and 5.
Rooted<JSObject*> roundTo(
@@ -1764,21 +1768,25 @@ static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) {
// Step 9.
if (!GetTemporalUnitValuedOption(cx, roundTo, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::DayTime,
&smallestUnit)) {
return false;
}
- if (smallestUnit == TemporalUnit::Auto) {
+ if (smallestUnit == TemporalUnit::Unset) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
return false;
}
+ // Step 10.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::DayTime)) {
+ return false;
+ }
MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
smallestUnit <= TemporalUnit::Nanosecond);
- // Steps 10-11.
+ // Steps 11-12.
auto maximum = Increment{1};
bool inclusive = true;
if (smallestUnit > TemporalUnit::Day) {
@@ -1786,14 +1794,14 @@ static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) {
inclusive = false;
}
- // Step 12.
+ // Step 13.
if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
inclusive)) {
return false;
}
}
- // Step 13.
+ // Step 14.
if (smallestUnit == TemporalUnit::Nanosecond &&
roundingIncrement == Increment{1}) {
auto* obj = CreateTemporalDateTime(cx, dateTime, calendar);
@@ -1805,11 +1813,11 @@ static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) {
return true;
}
- // Step 14.
+ // Step 15.
auto result =
RoundISODateTime(dateTime, roundingIncrement, smallestUnit, roundingMode);
- // Step 15.
+ // Step 16.
auto* obj = CreateTemporalDateTime(cx, result, calendar);
if (!obj) {
return false;
@@ -1896,13 +1904,19 @@ static bool PlainDateTime_toString(JSContext* cx, const CallArgs& args) {
}
// Step 8.
- auto smallestUnit = TemporalUnit::Auto;
+ auto smallestUnit = TemporalUnit::Unset;
if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::Time, &smallestUnit)) {
+ &smallestUnit)) {
return false;
}
// Step 9.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::Time)) {
+ return false;
+ }
+
+ // Step 10.
if (smallestUnit == TemporalUnit::Hour) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
@@ -1910,22 +1924,22 @@ static bool PlainDateTime_toString(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 10.
+ // Step 11.
precision = ToSecondsStringPrecision(smallestUnit, digits);
}
- // Step 11.
+ // Step 12.
auto result =
RoundISODateTime(dt, precision.increment, precision.unit, roundingMode);
- // Step 12.
+ // Step 13.
if (!ISODateTimeWithinLimits(result)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
return false;
}
- // Step 13.
+ // Step 14.
JSString* str = ISODateTimeToString(cx, result, calendar, precision.precision,
showCalendar);
if (!str) {
diff --git a/js/src/builtin/temporal/PlainTime.cpp b/js/src/builtin/temporal/PlainTime.cpp
@@ -1347,8 +1347,8 @@ static bool PlainTime_round(JSContext* cx, const CallArgs& args) {
auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>();
auto time = temporalTime->time();
- // Steps 3-12.
- auto smallestUnit = TemporalUnit::Auto;
+ // Steps 3-13.
+ auto smallestUnit = TemporalUnit::Unset;
auto roundingMode = TemporalRoundingMode::HalfExpand;
auto roundingIncrement = Increment{1};
if (args.get(0).isString()) {
@@ -1356,13 +1356,18 @@ static bool PlainTime_round(JSContext* cx, const CallArgs& args) {
// Step 9.
Rooted<JSString*> paramString(cx, args[0].toString());
- if (!GetTemporalUnitValuedOption(cx, paramString,
- TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::Time, &smallestUnit)) {
+ if (!GetTemporalUnitValuedOption(
+ cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) {
return false;
}
- // Steps 6-8 and 10-12. (Implicit)
+ // Step 10.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::Time)) {
+ return false;
+ }
+
+ // Steps 6-8 and 11-13. (Implicit)
} else {
// Steps 3 and 5.
Rooted<JSObject*> options(
@@ -1383,30 +1388,36 @@ static bool PlainTime_round(JSContext* cx, const CallArgs& args) {
// Step 9.
if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::Time, &smallestUnit)) {
+ &smallestUnit)) {
return false;
}
- if (smallestUnit == TemporalUnit::Auto) {
+ if (smallestUnit == TemporalUnit::Unset) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
return false;
}
- // Steps 10-11.
+ // Step 10.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::Time)) {
+ return false;
+ }
+
+ // Steps 11-12.
auto maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit);
- // Step 12.
+ // Step 13.
if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
false)) {
return false;
}
}
- // Step 13.
+ // Step 14.
auto result = RoundTime(time, roundingIncrement, smallestUnit, roundingMode);
- // Step 14.
+ // Step 15.
auto* obj = CreateTemporalTime(cx, result.time);
if (!obj) {
return false;
@@ -1481,13 +1492,19 @@ static bool PlainTime_toString(JSContext* cx, const CallArgs& args) {
}
// Step 7.
- auto smallestUnit = TemporalUnit::Auto;
+ auto smallestUnit = TemporalUnit::Unset;
if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::Time, &smallestUnit)) {
+ &smallestUnit)) {
return false;
}
// Step 8.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::Time)) {
+ return false;
+ }
+
+ // Step 9.
if (smallestUnit == TemporalUnit::Hour) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
@@ -1495,15 +1512,15 @@ static bool PlainTime_toString(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 9.
+ // Step 10.
precision = ToSecondsStringPrecision(smallestUnit, digits);
}
- // Step 10.
+ // Step 11.
auto roundedTime =
RoundTime(time, precision.increment, precision.unit, roundingMode);
- // Step 11.
+ // Step 12.
JSString* str = TimeRecordToString(cx, roundedTime.time, precision.precision);
if (!str) {
return false;
diff --git a/js/src/builtin/temporal/Temporal.cpp b/js/src/builtin/temporal/Temporal.cpp
@@ -201,6 +201,7 @@ static bool ToTemporalUnit(JSContext* cx, JSLinearString* str,
};
static constexpr UnitMap mapping[] = {
+ {"auto", TemporalUnit::Auto},
{"year", TemporalUnit::Year},
{"years", TemporalUnit::Year},
{"month", TemporalUnit::Month},
@@ -272,71 +273,79 @@ static std::pair<TemporalUnit, TemporalUnit> AllowedValues(
}
/**
- * GetTemporalUnitValuedOption ( normalizedOptions, key, unitGroup, default [ ,
- * extraValues ] )
+ * GetTemporalUnitValuedOption ( options, key, default )
*/
bool js::temporal::GetTemporalUnitValuedOption(JSContext* cx,
Handle<JSObject*> options,
TemporalUnitKey key,
- TemporalUnitGroup unitGroup,
TemporalUnit* unit) {
- // Steps 1-8. (Not applicable in our implementation.)
+ // Steps 1-5. (Not applicable in our implementation.)
- // Step 9.
+ // Step 6.
Rooted<JSString*> value(cx);
if (!GetStringOption(cx, options, ToPropertyName(cx, key), &value)) {
return false;
}
+ // Step 7.
+ //
// Caller should fill in the fallback.
if (!value) {
return true;
}
- return GetTemporalUnitValuedOption(cx, value, key, unitGroup, unit);
+ // Steps 8-9.
+ auto* linear = value->ensureLinear(cx);
+ if (!linear) {
+ return false;
+ }
+ return ToTemporalUnit(cx, linear, key, unit);
}
/**
- * GetTemporalUnitValuedOption ( normalizedOptions, key, unitGroup, default [ ,
- * extraValues ] )
+ * GetTemporalUnitValuedOption ( options, key, default )
*/
bool js::temporal::GetTemporalUnitValuedOption(JSContext* cx,
Handle<JSString*> value,
TemporalUnitKey key,
- TemporalUnitGroup unitGroup,
TemporalUnit* unit) {
- // Steps 1-9. (Not applicable in our implementation.)
-
- // Step 10. (Handled in caller.)
+ // Steps 1-7. (Not applicable in our implementation.)
- Rooted<JSLinearString*> linear(cx, value->ensureLinear(cx));
+ // Steps 8-9.
+ auto* linear = value->ensureLinear(cx);
if (!linear) {
return false;
}
+ return ToTemporalUnit(cx, linear, key, unit);
+}
- // Caller should fill in the fallback.
- if (key == TemporalUnitKey::LargestUnit) {
- if (StringEqualsLiteral(linear, "auto")) {
- return true;
- }
+/**
+ * ValidateTemporalUnitValue ( value, unitGroup [ , extraValues ] )
+ */
+bool js::temporal::ValidateTemporalUnitValue(JSContext* cx, TemporalUnitKey key,
+ TemporalUnit unit,
+ TemporalUnitGroup unitGroup) {
+ // Step 1.
+ if (unit == TemporalUnit::Unset) {
+ return true;
}
- // Step 11.
- if (!ToTemporalUnit(cx, linear, key, unit)) {
- return false;
+ // Step 2. (Partial)
+ if (key == TemporalUnitKey::LargestUnit && unit == TemporalUnit::Auto) {
+ return true;
}
+ // Steps 2-5.
auto allowedValues = AllowedValues(unitGroup);
- if (*unit < allowedValues.first || *unit > allowedValues.second) {
- if (auto chars = QuoteString(cx, linear, '"')) {
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
- JSMSG_INVALID_OPTION_VALUE, ToCString(key),
- chars.get());
- }
- return false;
+ if (allowedValues.first <= unit && unit <= allowedValues.second) {
+ return true;
}
- return true;
+ // Step 6.
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_INVALID_OPTION_VALUE, ToCString(key),
+ TemporalUnitToString(unit));
+ return false;
}
/**
@@ -821,7 +830,7 @@ bool js::temporal::GetTemporalFractionalSecondDigitsOption(
*/
SecondsStringPrecision js::temporal::ToSecondsStringPrecision(
TemporalUnit smallestUnit, Precision fractionalDigitCount) {
- MOZ_ASSERT(smallestUnit == TemporalUnit::Auto ||
+ MOZ_ASSERT(smallestUnit == TemporalUnit::Unset ||
smallestUnit >= TemporalUnit::Minute);
MOZ_ASSERT(fractionalDigitCount == Precision::Auto() ||
fractionalDigitCount.value() <= 9);
@@ -848,9 +857,10 @@ SecondsStringPrecision js::temporal::ToSecondsStringPrecision(
case TemporalUnit::Nanosecond:
return {Precision{9}, TemporalUnit::Nanosecond, Increment{1}};
- case TemporalUnit::Auto:
+ case TemporalUnit::Unset:
break;
+ case TemporalUnit::Auto:
case TemporalUnit::Year:
case TemporalUnit::Month:
case TemporalUnit::Week:
@@ -1260,46 +1270,69 @@ bool js::temporal::GetDifferenceSettings(
TemporalUnitGroup unitGroup, TemporalUnit smallestAllowedUnit,
TemporalUnit fallbackSmallestUnit, TemporalUnit smallestLargestDefaultUnit,
DifferenceSettings* result) {
+ MOZ_ASSERT(TemporalUnit::Auto < fallbackSmallestUnit &&
+ fallbackSmallestUnit <= smallestAllowedUnit);
+ MOZ_ASSERT(TemporalUnit::Auto < smallestLargestDefaultUnit &&
+ smallestLargestDefaultUnit <= smallestAllowedUnit);
+
// Steps 1-2.
auto largestUnit = TemporalUnit::Auto;
if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::LargestUnit,
- unitGroup, &largestUnit)) {
+ &largestUnit)) {
return false;
}
// Step 3.
- if (largestUnit > smallestAllowedUnit) {
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
- JSMSG_TEMPORAL_INVALID_UNIT_OPTION,
- TemporalUnitToString(largestUnit), "largestUnit");
+ auto roundingIncrement = Increment{1};
+ if (!GetRoundingIncrementOption(cx, options, &roundingIncrement)) {
return false;
}
// Step 4.
- auto roundingIncrement = Increment{1};
- if (!GetRoundingIncrementOption(cx, options, &roundingIncrement)) {
+ auto roundingMode = TemporalRoundingMode::Trunc;
+ if (!GetRoundingModeOption(cx, options, &roundingMode)) {
return false;
}
// Step 5.
- auto roundingMode = TemporalRoundingMode::Trunc;
- if (!GetRoundingModeOption(cx, options, &roundingMode)) {
+ auto smallestUnit = fallbackSmallestUnit;
+ if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
+ &smallestUnit)) {
return false;
}
// Step 6.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::LargestUnit, largestUnit,
+ unitGroup)) {
+ return false;
+ }
+
+ // Step 7. (Not applicable in our implementation.)
+ MOZ_ASSERT(largestUnit != TemporalUnit::Unset);
+
+ // Step 8.
+ if (largestUnit > smallestAllowedUnit) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TEMPORAL_INVALID_UNIT_OPTION,
+ TemporalUnitToString(largestUnit), "largestUnit");
+ return false;
+ }
+
+ // Step 9.
if (operation == TemporalDifference::Since) {
roundingMode = NegateRoundingMode(roundingMode);
}
- // Step 7.
- auto smallestUnit = fallbackSmallestUnit;
- if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
- unitGroup, &smallestUnit)) {
+ // Step 10.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, unitGroup)) {
return false;
}
- // Step 8.
+ // Step 11. (Not applicable in our implementation.)
+ MOZ_ASSERT(smallestUnit != TemporalUnit::Unset);
+
+ // Step 12.
if (smallestUnit > smallestAllowedUnit) {
JS_ReportErrorNumberASCII(
cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_INVALID_UNIT_OPTION,
@@ -1307,34 +1340,34 @@ bool js::temporal::GetDifferenceSettings(
return false;
}
- // Step 9. (Inlined call to LargerOfTwoTemporalUnits)
+ // Step 13. (Inlined call to LargerOfTwoTemporalUnits)
auto defaultLargestUnit = std::min(smallestLargestDefaultUnit, smallestUnit);
- // Step 10.
+ // Step 14.
if (largestUnit == TemporalUnit::Auto) {
largestUnit = defaultLargestUnit;
}
- // Step 11.
+ // Step 15.
if (largestUnit > smallestUnit) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_INVALID_UNIT_RANGE);
return false;
}
- // Steps 12-13.
+ // Steps 16-17.
if (smallestUnit > TemporalUnit::Day) {
- // Step 12.
+ // Step 16.
auto maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit);
- // Step 13.
+ // Step 17.
if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
false)) {
return false;
}
}
- // Step 14.
+ // Step 18.
*result = {smallestUnit, largestUnit, roundingMode, roundingIncrement};
return true;
}
diff --git a/js/src/builtin/temporal/Temporal.h b/js/src/builtin/temporal/Temporal.h
@@ -141,22 +141,23 @@ enum class TemporalUnitKey {
};
/**
- * GetTemporalUnitValuedOption ( normalizedOptions, key, unitGroup, default [ ,
- * extraValues ] )
+ * GetTemporalUnitValuedOption ( options, key, default )
*/
bool GetTemporalUnitValuedOption(JSContext* cx, JS::Handle<JSObject*> options,
- TemporalUnitKey key,
- TemporalUnitGroup unitGroup,
- TemporalUnit* unit);
+ TemporalUnitKey key, TemporalUnit* unit);
/**
* GetTemporalUnitValuedOption ( normalizedOptions, key, unitGroup, default [ ,
* extraValues ] )
*/
bool GetTemporalUnitValuedOption(JSContext* cx, JS::Handle<JSString*> value,
- TemporalUnitKey key,
- TemporalUnitGroup unitGroup,
- TemporalUnit* unit);
+ TemporalUnitKey key, TemporalUnit* unit);
+
+/**
+ * ValidateTemporalUnitValue ( value, unitGroup [ , extraValues ] )
+ */
+bool ValidateTemporalUnitValue(JSContext* cx, TemporalUnitKey key,
+ TemporalUnit unit, TemporalUnitGroup unitGroup);
/**
* GetRoundingModeOption ( normalizedOptions, fallback )
diff --git a/js/src/builtin/temporal/ZonedDateTime.cpp b/js/src/builtin/temporal/ZonedDateTime.cpp
@@ -2449,8 +2449,8 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) {
Rooted<ZonedDateTime> zonedDateTime(
cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});
- // Steps 3-12.
- auto smallestUnit = TemporalUnit::Auto;
+ // Steps 3-13.
+ auto smallestUnit = TemporalUnit::Unset;
auto roundingMode = TemporalRoundingMode::HalfExpand;
auto roundingIncrement = Increment{1};
if (args.get(0).isString()) {
@@ -2459,12 +2459,17 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) {
// Step 9.
Rooted<JSString*> paramString(cx, args[0].toString());
if (!GetTemporalUnitValuedOption(
- cx, paramString, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::DayTime, &smallestUnit)) {
+ cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) {
return false;
}
- // Steps 6-8 and 10-12. (Implicit)
+ // Step 10.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::DayTime)) {
+ return false;
+ }
+
+ // Steps 6-8 and 11-13. (Implicit)
} else {
// Steps 3 and 5.a
Rooted<JSObject*> roundTo(
@@ -2485,21 +2490,25 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) {
// Step 9.
if (!GetTemporalUnitValuedOption(cx, roundTo, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::DayTime,
&smallestUnit)) {
return false;
}
- if (smallestUnit == TemporalUnit::Auto) {
+ if (smallestUnit == TemporalUnit::Unset) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
return false;
}
+ // Step 10.
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::DayTime)) {
+ return false;
+ }
MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
smallestUnit <= TemporalUnit::Nanosecond);
- // Steps 10-11.
+ // Steps 11-12.
auto maximum = Increment{1};
bool inclusive = true;
if (smallestUnit > TemporalUnit::Day) {
@@ -2507,17 +2516,17 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) {
inclusive = false;
}
- // Step 12.
+ // Step 13.
if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
inclusive)) {
return false;
}
}
- // Step 13.
+ // Step 14.
if (smallestUnit == TemporalUnit::Nanosecond &&
roundingIncrement == Increment{1}) {
- // Step 13.a.
+ // Step 14.a.
auto* result = CreateTemporalZonedDateTime(
cx, zonedDateTime.epochNanoseconds(), zonedDateTime.timeZone(),
zonedDateTime.calendar());
@@ -2529,28 +2538,28 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) {
return true;
}
- // Step 14.
+ // Step 15.
auto thisNs = zonedDateTime.epochNanoseconds();
- // Step 15.
+ // Step 16.
auto timeZone = zonedDateTime.timeZone();
- // Step 16.
+ // Step 17.
auto calendar = zonedDateTime.calendar();
- // Step 17.
+ // Step 18.
ISODateTime isoDateTime;
if (!GetISODateTimeFor(cx, timeZone, thisNs, &isoDateTime)) {
return false;
}
- // Steps 18-19.
+ // Steps 19-20.
EpochNanoseconds epochNanoseconds;
if (smallestUnit == TemporalUnit::Day) {
- // Step 18.a.
+ // Step 19.a.
const auto& dateStart = isoDateTime.date;
- // Step 18.b.
+ // Step 19.b.
auto dateEnd = BalanceISODate(dateStart, 1);
if (!ISODateWithinLimits(dateEnd)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
@@ -2558,30 +2567,30 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 18.c.
+ // Step 19.c.
EpochNanoseconds startNs;
if (!GetStartOfDay(cx, timeZone, dateStart, &startNs)) {
return false;
}
- // Step 18.d.
+ // Step 19.d.
MOZ_ASSERT(thisNs >= startNs);
- // Step 18.e.
+ // Step 19.e.
EpochNanoseconds endNs;
if (!GetStartOfDay(cx, timeZone, dateEnd, &endNs)) {
return false;
}
- // Step 18.f.
+ // Step 19.f.
MOZ_ASSERT(thisNs < endNs);
- // Step 18.g.
+ // Step 19.g.
auto dayLengthNs = endNs - startNs;
MOZ_ASSERT(IsValidEpochDuration(dayLengthNs));
MOZ_ASSERT(dayLengthNs > EpochDuration{}, "dayLengthNs is positive");
- // Step 18.h. (Inlined TimeDurationFromEpochNanosecondsDifference)
+ // Step 19.h. (Inlined TimeDurationFromEpochNanosecondsDifference)
auto dayProgressNs = thisNs - startNs;
MOZ_ASSERT(IsValidEpochDuration(dayProgressNs));
MOZ_ASSERT(dayProgressNs >= EpochDuration{},
@@ -2592,7 +2601,7 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) {
MOZ_ASSERT(dayLengthNs <= EpochDuration::fromDays(2),
"maximum day length for repeated days");
- // Step 18.i. (Inlined RoundTimeDurationToIncrement)
+ // Step 19.i. (Inlined RoundTimeDurationToIncrement)
auto rounded = RoundNumberToIncrement(
static_cast<int64_t>(dayProgressNs.toNanoseconds()),
static_cast<int64_t>(dayLengthNs.toNanoseconds()), roundingMode);
@@ -2601,22 +2610,22 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) {
roundedDaysNs == dayLengthNs);
MOZ_ASSERT(IsValidEpochDuration(roundedDaysNs));
- // Step 18.j. (Inlined AddTimeDurationToEpochNanoseconds)
+ // Step 19.j. (Inlined AddTimeDurationToEpochNanoseconds)
epochNanoseconds = startNs + roundedDaysNs;
MOZ_ASSERT(epochNanoseconds == startNs || epochNanoseconds == endNs);
} else {
- // Step 19.a.
+ // Step 20.a.
auto roundResult = RoundISODateTime(isoDateTime, roundingIncrement,
smallestUnit, roundingMode);
- // Step 19.b.
+ // Step 20.b.
int64_t offsetNanoseconds;
if (!GetOffsetNanosecondsFor(cx, timeZone, thisNs, &offsetNanoseconds)) {
return false;
}
MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
- // Step 19.c.
+ // Step 20.c.
if (!InterpretISODateTimeOffset(
cx, roundResult, OffsetBehaviour::Option, offsetNanoseconds,
timeZone, TemporalDisambiguation::Compatible,
@@ -2627,7 +2636,7 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) {
}
MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));
- // Step 20.
+ // Step 21.
auto* result =
CreateTemporalZonedDateTime(cx, epochNanoseconds, timeZone, calendar);
if (!result) {
@@ -2721,30 +2730,36 @@ static bool ZonedDateTime_toString(JSContext* cx, const CallArgs& args) {
}
// Step 9.
- auto smallestUnit = TemporalUnit::Auto;
+ auto smallestUnit = TemporalUnit::Unset;
if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
- TemporalUnitGroup::Time, &smallestUnit)) {
+ &smallestUnit)) {
return false;
}
// Step 10.
- if (smallestUnit == TemporalUnit::Hour) {
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
- JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
- "smallestUnit");
+ if (!GetTemporalShowTimeZoneNameOption(cx, options, &showTimeZone)) {
return false;
}
// Step 11.
- if (!GetTemporalShowTimeZoneNameOption(cx, options, &showTimeZone)) {
+ if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
+ smallestUnit, TemporalUnitGroup::Time)) {
return false;
}
// Step 12.
+ if (smallestUnit == TemporalUnit::Hour) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
+ "smallestUnit");
+ return false;
+ }
+
+ // Step 13.
precision = ToSecondsStringPrecision(smallestUnit, digits);
}
- // Step 13.
+ // Step 14.
JSString* str = TemporalZonedDateTimeToString(
cx, zonedDateTime, precision.precision, showCalendar, showTimeZone,
showOffset, precision.increment, precision.unit, roundingMode);
diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list
@@ -1099,15 +1099,3 @@ skip script test262/intl402/Temporal/PlainDateTime/from/calendar-not-supporting-
skip script test262/built-ins/Temporal/PlainDateTime/prototype/until/bubble-time-unit.js
skip script test262/built-ins/Temporal/PlainDateTime/prototype/since/bubble-time-unit.js
skip script test262/built-ins/Temporal/Duration/prototype/round/bubble-time-unit.js
-
-# https://bugzilla.mozilla.org/show_bug.cgi?id=1998672
-skip script test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-read-before-algorithmic-validation.js
-skip script test262/built-ins/Temporal/PlainDate/prototype/since/options-read-before-algorithmic-validation.js
-skip script test262/built-ins/Temporal/PlainDate/prototype/until/options-read-before-algorithmic-validation.js
-skip script test262/built-ins/Temporal/PlainTime/prototype/since/options-read-before-algorithmic-validation.js
-skip script test262/built-ins/Temporal/PlainTime/prototype/until/options-read-before-algorithmic-validation.js
-skip script test262/built-ins/Temporal/Instant/prototype/since/options-read-before-algorithmic-validation.js
-skip script test262/built-ins/Temporal/Instant/prototype/until/options-read-before-algorithmic-validation.js
-skip script test262/built-ins/Temporal/Instant/prototype/toString/options-read-before-algorithmic-validation.js
-skip script test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-read-before-algorithmic-validation.js
-skip script test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-read-before-algorithmic-validation.js