summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/temporal/PlainDateTime.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin/temporal/PlainDateTime.cpp')
-rw-r--r--js/src/builtin/temporal/PlainDateTime.cpp358
1 files changed, 147 insertions, 211 deletions
diff --git a/js/src/builtin/temporal/PlainDateTime.cpp b/js/src/builtin/temporal/PlainDateTime.cpp
index 8f137cfe43..f4b15422b3 100644
--- a/js/src/builtin/temporal/PlainDateTime.cpp
+++ b/js/src/builtin/temporal/PlainDateTime.cpp
@@ -178,11 +178,6 @@ static bool ISODateTimeWithinLimits(T year, T month, T day) {
// components set to zero. That means the maximum value is exclusive, whereas
// the minimum value is inclusive.
- // FIXME: spec bug - GetUTCEpochNanoseconds when called with large |year| may
- // cause MakeDay to return NaN, which makes MakeDate return NaN, which is
- // unexpected in GetUTCEpochNanoseconds, step 4.
- // https://github.com/tc39/proposal-temporal/issues/2315
-
// Definitely in range.
if (minYear < year && year < maxYear) {
return true;
@@ -229,7 +224,7 @@ bool js::temporal::ISODateTimeWithinLimits(double year, double month,
* millisecond, microsecond, nanosecond )
*/
bool js::temporal::ISODateTimeWithinLimits(const PlainDateTime& dateTime) {
- auto& [date, time] = dateTime;
+ const auto& [date, time] = dateTime;
return ::ISODateTimeWithinLimits(date.year, date.month, date.day, time.hour,
time.minute, time.second, time.millisecond,
time.microsecond, time.nanosecond);
@@ -295,37 +290,39 @@ static PlainDateTimeObject* CreateTemporalDateTime(
// Step 6.
dateTime->setFixedSlot(PlainDateTimeObject::ISO_YEAR_SLOT,
- Int32Value(isoYear));
+ Int32Value(int32_t(isoYear)));
// Step 7.
dateTime->setFixedSlot(PlainDateTimeObject::ISO_MONTH_SLOT,
- Int32Value(isoMonth));
+ Int32Value(int32_t(isoMonth)));
// Step 8.
- dateTime->setFixedSlot(PlainDateTimeObject::ISO_DAY_SLOT, Int32Value(isoDay));
+ dateTime->setFixedSlot(PlainDateTimeObject::ISO_DAY_SLOT,
+ Int32Value(int32_t(isoDay)));
// Step 9.
- dateTime->setFixedSlot(PlainDateTimeObject::ISO_HOUR_SLOT, Int32Value(hour));
+ dateTime->setFixedSlot(PlainDateTimeObject::ISO_HOUR_SLOT,
+ Int32Value(int32_t(hour)));
// Step 10.
dateTime->setFixedSlot(PlainDateTimeObject::ISO_MINUTE_SLOT,
- Int32Value(minute));
+ Int32Value(int32_t(minute)));
// Step 11.
dateTime->setFixedSlot(PlainDateTimeObject::ISO_SECOND_SLOT,
- Int32Value(second));
+ Int32Value(int32_t(second)));
// Step 12.
dateTime->setFixedSlot(PlainDateTimeObject::ISO_MILLISECOND_SLOT,
- Int32Value(millisecond));
+ Int32Value(int32_t(millisecond)));
// Step 13.
dateTime->setFixedSlot(PlainDateTimeObject::ISO_MICROSECOND_SLOT,
- Int32Value(microsecond));
+ Int32Value(int32_t(microsecond)));
// Step 14.
dateTime->setFixedSlot(PlainDateTimeObject::ISO_NANOSECOND_SLOT,
- Int32Value(nanosecond));
+ Int32Value(int32_t(nanosecond)));
// Step 15.
dateTime->setFixedSlot(PlainDateTimeObject::CALENDAR_SLOT,
@@ -342,9 +339,10 @@ static PlainDateTimeObject* CreateTemporalDateTime(
PlainDateTimeObject* js::temporal::CreateTemporalDateTime(
JSContext* cx, const PlainDateTime& dateTime,
Handle<CalendarValue> calendar) {
- auto& [date, time] = dateTime;
- auto& [isoYear, isoMonth, isoDay] = date;
- auto& [hour, minute, second, millisecond, microsecond, nanosecond] = time;
+ const auto& [date, time] = dateTime;
+ const auto& [isoYear, isoMonth, isoDay] = date;
+ const auto& [hour, minute, second, millisecond, microsecond, nanosecond] =
+ time;
// Steps 1-2.
if (!ThrowIfInvalidISODateTime(cx, dateTime)) {
@@ -441,7 +439,7 @@ bool js::temporal::InterpretTemporalDateTimeFields(
CalendarMethod::DateFromFields));
// Step 3.
- TimeRecord timeResult;
+ TemporalTimeLike timeResult;
if (!ToTemporalTimeRecord(cx, fields, &timeResult)) {
return false;
}
@@ -698,7 +696,7 @@ bool js::temporal::ToTemporalDateTime(JSContext* cx, Handle<Value> item,
static bool ToTemporalDateTime(
JSContext* cx, Handle<Value> item,
MutableHandle<PlainDateTimeWithCalendar> result) {
- HandleObject options = nullptr;
+ Handle<JSObject*> options = nullptr;
auto* obj = ::ToTemporalDateTime(cx, item, options).unwrapOrNull();
if (!obj) {
@@ -734,42 +732,46 @@ static int32_t CompareISODateTime(const PlainDateTime& one,
/**
* AddDateTime ( year, month, day, hour, minute, second, millisecond,
- * microsecond, nanosecond, calendarRec, years, months, weeks, days, hours,
- * minutes, seconds, milliseconds, microseconds, nanoseconds, options )
+ * microsecond, nanosecond, calendarRec, years, months, weeks, days, norm,
+ * options )
*/
static bool AddDateTime(JSContext* cx, const PlainDateTime& dateTime,
Handle<CalendarRecord> calendar,
- const Duration& duration, Handle<JSObject*> options,
- PlainDateTime* result) {
+ const NormalizedDuration& duration,
+ Handle<JSObject*> options, PlainDateTime* result) {
MOZ_ASSERT(IsValidDuration(duration));
// Step 1.
MOZ_ASSERT(IsValidISODateTime(dateTime));
- MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
// Step 2.
- PlainTime timeResult;
- double daysResult;
- if (!AddTime(cx, dateTime.time, duration, &timeResult, &daysResult)) {
- return false;
- }
+ MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
// Step 3.
- const auto& datePart = dateTime.date;
+ auto timeResult = AddTime(dateTime.time, duration.time);
// Step 4.
- Duration dateDuration = {duration.years, duration.months, duration.weeks,
- daysResult};
- MOZ_ASSERT(IsValidDuration(duration));
+ const auto& datePart = dateTime.date;
// Step 5.
+ auto dateDuration = DateDuration{
+ duration.date.years,
+ duration.date.months,
+ duration.date.weeks,
+ duration.date.days + timeResult.days,
+ };
+ if (!ThrowIfInvalidDuration(cx, dateDuration)) {
+ return false;
+ }
+
+ // Step 6.
PlainDate addedDate;
if (!AddDate(cx, calendar, datePart, dateDuration, options, &addedDate)) {
return false;
}
- // Step 6.
- *result = {addedDate, timeResult};
+ // Step 7.
+ *result = {addedDate, timeResult.time};
return true;
}
@@ -782,7 +784,7 @@ static bool DifferenceISODateTime(JSContext* cx, const PlainDateTime& one,
Handle<CalendarRecord> calendar,
TemporalUnit largestUnit,
Handle<PlainObject*> maybeOptions,
- Duration* result) {
+ NormalizedDuration* result) {
// Steps 1-2.
MOZ_ASSERT(IsValidISODateTime(one));
MOZ_ASSERT(IsValidISODateTime(two));
@@ -795,10 +797,10 @@ static bool DifferenceISODateTime(JSContext* cx, const PlainDateTime& one,
CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateUntil));
// Step 4.
- auto timeDifference = DifferenceTime(one.time, two.time);
+ auto timeDuration = DifferenceTime(one.time, two.time);
// Step 5.
- int32_t timeSign = DurationSign(timeDifference.toDuration());
+ int32_t timeSign = NormalizedTimeDurationSign(timeDuration);
// Step 6.
int32_t dateSign = CompareISODate(two.date, one.date);
@@ -813,20 +815,8 @@ static bool DifferenceISODateTime(JSContext* cx, const PlainDateTime& one,
adjustedDate.day - timeSign);
// Step 8.b.
- if (!BalanceTimeDuration(cx,
- {
- 0,
- 0,
- 0,
- double(-timeSign),
- timeDifference.hours,
- timeDifference.minutes,
- timeDifference.seconds,
- timeDifference.milliseconds,
- timeDifference.microseconds,
- timeDifference.nanoseconds,
- },
- largestUnit, &timeDifference)) {
+ if (!Add24HourDaysToNormalizedTimeDuration(cx, timeDuration, -timeSign,
+ &timeDuration)) {
return false;
}
}
@@ -834,49 +824,26 @@ static bool DifferenceISODateTime(JSContext* cx, const PlainDateTime& one,
MOZ_ASSERT(IsValidISODate(adjustedDate));
MOZ_ASSERT(ISODateTimeWithinLimits(adjustedDate));
- // TODO: Avoid allocating CreateTemporalDate.
-
// Step 9.
- Rooted<PlainDateObject*> date1(
- cx, CreateTemporalDate(cx, adjustedDate, calendar.receiver()));
- if (!date1) {
- return false;
- }
+ const auto& date1 = adjustedDate;
// Step 10.
- Rooted<PlainDateObject*> date2(
- cx, CreateTemporalDate(cx, two.date, calendar.receiver()));
- if (!date2) {
- return false;
- }
+ const auto& date2 = two.date;
// Step 11.
auto dateLargestUnit = std::min(TemporalUnit::Day, largestUnit);
- Duration dateDifference;
+ DateDuration dateDifference;
if (maybeOptions) {
- // FIXME: spec issue - this copy is no longer needed, all callers have
- // already copied the user input object.
- // https://github.com/tc39/proposal-temporal/issues/2525
-
// Step 12.
- Rooted<PlainObject*> untilOptions(cx,
- SnapshotOwnProperties(cx, maybeOptions));
- if (!untilOptions) {
- return false;
- }
+ //
+ // The spec performs an unnecessary copy operation. As an optimization, we
+ // omit this copy.
+ auto untilOptions = maybeOptions;
- // Step 13.
- Rooted<Value> largestUnitValue(
- cx, StringValue(TemporalUnitToString(cx, dateLargestUnit)));
- if (!DefineDataProperty(cx, untilOptions, cx->names().largestUnit,
- largestUnitValue)) {
- return false;
- }
-
- // Step 14.
- if (!DifferenceDate(cx, calendar, date1, date2, untilOptions,
- &dateDifference)) {
+ // Steps 13-14.
+ if (!DifferenceDate(cx, calendar, date1, date2, dateLargestUnit,
+ untilOptions, &dateDifference)) {
return false;
}
} else {
@@ -888,82 +855,32 @@ static bool DifferenceISODateTime(JSContext* cx, const PlainDateTime& one,
}
// Step 15.
- TimeDuration balanceResult;
- if (!BalanceTimeDuration(cx,
- {
- 0,
- 0,
- 0,
- dateDifference.days,
- timeDifference.hours,
- timeDifference.minutes,
- timeDifference.seconds,
- timeDifference.milliseconds,
- timeDifference.microseconds,
- timeDifference.nanoseconds,
- },
- largestUnit, &balanceResult)) {
- return false;
- }
-
- // Step 16.
- *result = {dateDifference.years, dateDifference.months,
- dateDifference.weeks, balanceResult.days,
- balanceResult.hours, balanceResult.minutes,
- balanceResult.seconds, balanceResult.milliseconds,
- balanceResult.microseconds, balanceResult.nanoseconds};
- MOZ_ASSERT(IsValidDuration(*result));
- return true;
-}
-
-/**
- * DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
- * d2, h2, min2, s2, ms2, mus2, ns2, calendarRec, largestUnit, options )
- */
-bool js::temporal::DifferenceISODateTime(JSContext* cx,
- const PlainDateTime& one,
- const PlainDateTime& two,
- Handle<CalendarRecord> calendar,
- TemporalUnit largestUnit,
- Duration* result) {
- return ::DifferenceISODateTime(cx, one, two, calendar, largestUnit, nullptr,
- result);
-}
-
-/**
- * DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2,
- * d2, h2, min2, s2, ms2, mus2, ns2, calendarRec, largestUnit, options )
- */
-bool js::temporal::DifferenceISODateTime(
- JSContext* cx, const PlainDateTime& one, const PlainDateTime& two,
- Handle<CalendarRecord> calendar, TemporalUnit largestUnit,
- Handle<PlainObject*> options, Duration* result) {
- return ::DifferenceISODateTime(cx, one, two, calendar, largestUnit, options,
- result);
+ return CreateNormalizedDurationRecord(cx, dateDifference, timeDuration,
+ result);
}
/**
* RoundISODateTime ( year, month, day, hour, minute, second, millisecond,
- * microsecond, nanosecond, increment, unit, roundingMode [ , dayLength ] )
+ * microsecond, nanosecond, increment, unit, roundingMode )
*/
-static PlainDateTime RoundISODateTime(const PlainDateTime& dateTime,
- Increment increment, TemporalUnit unit,
- TemporalRoundingMode roundingMode) {
+PlainDateTime js::temporal::RoundISODateTime(
+ const PlainDateTime& dateTime, Increment increment, TemporalUnit unit,
+ TemporalRoundingMode roundingMode) {
const auto& [date, time] = dateTime;
// Step 1.
MOZ_ASSERT(IsValidISODateTime(dateTime));
- MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
- // Step 2. (Not applicable in our implementation.)
+ // Step 2.
+ MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
// Step 3.
auto roundedTime = RoundTime(time, increment, unit, roundingMode);
MOZ_ASSERT(0 <= roundedTime.days && roundedTime.days <= 1);
// Step 4.
- auto balanceResult =
- BalanceISODate(date.year, date.month, date.day + roundedTime.days);
+ auto balanceResult = BalanceISODate(date.year, date.month,
+ date.day + int32_t(roundedTime.days));
// Step 5.
return {balanceResult, roundedTime.time};
@@ -1049,7 +966,7 @@ static bool DifferenceTemporalPlainDateTime(JSContext* cx,
}
// Step 10.
- Duration diff;
+ NormalizedDuration diff;
if (!::DifferenceISODateTime(cx, dateTime, other, calendar,
settings.largestUnit, resolvedOptions, &diff)) {
return false;
@@ -1060,62 +977,81 @@ static bool DifferenceTemporalPlainDateTime(JSContext* cx,
settings.smallestUnit == TemporalUnit::Nanosecond &&
settings.roundingIncrement == Increment{1};
- // Step 12.
- if (roundingGranularityIsNoop) {
- if (operation == TemporalDifference::Since) {
- diff = diff.negate();
+ // Steps 12-13.
+ DateDuration balancedDate;
+ TimeDuration balancedTime;
+ if (!roundingGranularityIsNoop) {
+ // Step 12.a.
+ Rooted<PlainDateObject*> relativeTo(
+ cx, CreateTemporalDate(cx, dateTime.date(), dateTime.calendar()));
+ if (!relativeTo) {
+ return false;
}
- auto* obj = CreateTemporalDuration(cx, diff);
- if (!obj) {
+ // Steps 12.b-c.
+ NormalizedDuration roundResult;
+ if (!temporal::RoundDuration(cx, diff, settings.roundingIncrement,
+ settings.smallestUnit, settings.roundingMode,
+ relativeTo, calendar, &roundResult)) {
return false;
}
- args.rval().setObject(*obj);
- return true;
- }
+ // Step 12.d.
+ NormalizedTimeDuration withDays;
+ if (!Add24HourDaysToNormalizedTimeDuration(
+ cx, roundResult.time, roundResult.date.days, &withDays)) {
+ return false;
+ }
- // Step 13.
- Rooted<PlainDateObject*> relativeTo(
- cx, CreateTemporalDate(cx, dateTime.date(), dateTime.calendar()));
- if (!relativeTo) {
- return false;
- }
+ // Step 12.e.
+ if (!BalanceTimeDuration(cx, withDays, settings.largestUnit,
+ &balancedTime)) {
+ return false;
+ }
- // Steps 14-15.
- Duration roundResult;
- if (!temporal::RoundDuration(cx, diff, settings.roundingIncrement,
- settings.smallestUnit, settings.roundingMode,
- relativeTo, calendar, &roundResult)) {
- return false;
- }
+ // Step 12.f.
+ auto toBalance = DateDuration{
+ roundResult.date.years,
+ roundResult.date.months,
+ roundResult.date.weeks,
+ balancedTime.days,
+ };
+ if (!temporal::BalanceDateDurationRelative(
+ cx, toBalance, settings.largestUnit, settings.smallestUnit,
+ relativeTo, calendar, &balancedDate)) {
+ return false;
+ }
+ } else {
+ // Step 13.a.
+ NormalizedTimeDuration withDays;
+ if (!Add24HourDaysToNormalizedTimeDuration(cx, diff.time, diff.date.days,
+ &withDays)) {
+ return false;
+ }
- // Step 16.
- TimeDuration result;
- if (!BalanceTimeDuration(cx, roundResult, settings.largestUnit, &result)) {
- return false;
- }
+ // Step 13.b.
+ if (!BalanceTimeDuration(cx, withDays, settings.largestUnit,
+ &balancedTime)) {
+ return false;
+ }
- // Step 17.
- auto toBalance = Duration{
- roundResult.years,
- roundResult.months,
- roundResult.weeks,
- result.days,
- };
- DateDuration balanceResult;
- if (!temporal::BalanceDateDurationRelative(
- cx, toBalance, settings.largestUnit, settings.smallestUnit,
- relativeTo, calendar, &balanceResult)) {
- return false;
+ // Step 13.c.
+ balancedDate = {
+ diff.date.years,
+ diff.date.months,
+ diff.date.weeks,
+ balancedTime.days,
+ };
}
+ MOZ_ASSERT(IsValidDuration(balancedDate));
- // Step 18.
+ // Step 14.
Duration duration = {
- balanceResult.years, balanceResult.months, balanceResult.weeks,
- balanceResult.days, result.hours, result.minutes,
- result.seconds, result.milliseconds, result.microseconds,
- result.nanoseconds,
+ double(balancedDate.years), double(balancedDate.months),
+ double(balancedDate.weeks), double(balancedDate.days),
+ double(balancedTime.hours), double(balancedTime.minutes),
+ double(balancedTime.seconds), double(balancedTime.milliseconds),
+ balancedTime.microseconds, balancedTime.nanoseconds,
};
if (operation == TemporalDifference::Since) {
duration = duration.negate();
@@ -1176,16 +1112,18 @@ static bool AddDurationToOrSubtractDurationFromPlainDateTime(
if (operation == PlainDateTimeDuration::Subtract) {
duration = duration.negate();
}
+ auto normalized = CreateNormalizedDurationRecord(duration);
+ // Step 6
PlainDateTime result;
- if (!AddDateTime(cx, dateTime, calendar, duration, options, &result)) {
+ if (!AddDateTime(cx, dateTime, calendar, normalized, options, &result)) {
return false;
}
- // Steps 6-7.
+ // Steps 7-8.
MOZ_ASSERT(IsValidISODateTime(result));
- // Step 8.
+ // Step 9.
auto* obj = CreateTemporalDateTime(cx, result, dateTime.calendar());
if (!obj) {
return false;
@@ -1824,13 +1762,11 @@ static bool PlainDateTime_with(JSContext* cx, const CallArgs& args) {
if (!temporalDateTimeLike) {
return false;
}
-
- // Step 4.
- if (!RejectTemporalLikeObject(cx, temporalDateTimeLike)) {
+ if (!ThrowIfTemporalLikeObject(cx, temporalDateTimeLike)) {
return false;
}
- // Step 5.
+ // Step 4.
Rooted<PlainObject*> resolvedOptions(cx);
if (args.hasDefined(1)) {
Rooted<JSObject*> options(cx,
@@ -1846,7 +1782,7 @@ static bool PlainDateTime_with(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 6.
+ // Step 5.
Rooted<CalendarValue> calendarValue(cx, dateTime->calendar());
Rooted<CalendarRecord> calendar(cx);
if (!CreateCalendarMethodsRecord(cx, calendarValue,
@@ -1859,7 +1795,7 @@ static bool PlainDateTime_with(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 7.
+ // Step 6.
JS::RootedVector<PropertyKey> fieldNames(cx);
if (!CalendarFields(cx, calendar,
{CalendarField::Day, CalendarField::Month,
@@ -1868,14 +1804,14 @@ static bool PlainDateTime_with(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 8.
+ // Step 7.
Rooted<PlainObject*> fields(cx,
PrepareTemporalFields(cx, dateTime, fieldNames));
if (!fields) {
return false;
}
- // Steps 9-14.
+ // Steps 8-13.
struct TimeField {
using FieldName = ImmutableTenuredPtr<PropertyName*> JSAtomState::*;
@@ -1900,7 +1836,7 @@ static bool PlainDateTime_with(JSContext* cx, const CallArgs& args) {
}
}
- // Step 15.
+ // Step 14.
if (!AppendSorted(cx, fieldNames.get(),
{
TemporalField::Hour,
@@ -1913,37 +1849,37 @@ static bool PlainDateTime_with(JSContext* cx, const CallArgs& args) {
return false;
}
- // Step 16.
+ // Step 15.
Rooted<PlainObject*> partialDateTime(
cx, PreparePartialTemporalFields(cx, temporalDateTimeLike, fieldNames));
if (!partialDateTime) {
return false;
}
- // Step 17.
+ // Step 16.
Rooted<JSObject*> mergedFields(
cx, CalendarMergeFields(cx, calendar, fields, partialDateTime));
if (!mergedFields) {
return false;
}
- // Step 18.
+ // Step 17.
fields = PrepareTemporalFields(cx, mergedFields, fieldNames);
if (!fields) {
return false;
}
- // Step 19.
+ // Step 18.
PlainDateTime result;
if (!InterpretTemporalDateTimeFields(cx, calendar, fields, resolvedOptions,
&result)) {
return false;
}
- // Steps 20-21.
+ // Steps 19-20.
MOZ_ASSERT(IsValidISODateTime(result));
- // Step 22.
+ // Step 21.
auto* obj = CreateTemporalDateTime(cx, result, calendar.receiver());
if (!obj) {
return false;
@@ -1970,7 +1906,7 @@ static bool PlainDateTime_withPlainTime(JSContext* cx, const CallArgs& args) {
auto date = ToPlainDate(temporalDateTime);
Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
- // Step 4.
+ // Step 3. (Inlined ToTemporalTimeOrMidnight)
PlainTime time = {};
if (args.hasDefined(0)) {
if (!ToTemporalTime(cx, args[0], &time)) {
@@ -1978,7 +1914,7 @@ static bool PlainDateTime_withPlainTime(JSContext* cx, const CallArgs& args) {
}
}
- // Steps 3 and 5.
+ // Step 4.
auto* obj = CreateTemporalDateTime(cx, {date, time}, calendar);
if (!obj) {
return false;