tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PlainDateTime.cpp (66045B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "builtin/temporal/PlainDateTime.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "mozilla/Casting.h"
     11 
     12 #include <algorithm>
     13 
     14 #include "jspubtd.h"
     15 #include "NamespaceImports.h"
     16 
     17 #include "builtin/intl/DateTimeFormat.h"
     18 #include "builtin/temporal/Calendar.h"
     19 #include "builtin/temporal/CalendarFields.h"
     20 #include "builtin/temporal/Duration.h"
     21 #include "builtin/temporal/Instant.h"
     22 #include "builtin/temporal/PlainDate.h"
     23 #include "builtin/temporal/PlainMonthDay.h"
     24 #include "builtin/temporal/PlainTime.h"
     25 #include "builtin/temporal/PlainYearMonth.h"
     26 #include "builtin/temporal/Temporal.h"
     27 #include "builtin/temporal/TemporalParser.h"
     28 #include "builtin/temporal/TemporalRoundingMode.h"
     29 #include "builtin/temporal/TemporalTypes.h"
     30 #include "builtin/temporal/TemporalUnit.h"
     31 #include "builtin/temporal/TimeZone.h"
     32 #include "builtin/temporal/ToString.h"
     33 #include "builtin/temporal/ZonedDateTime.h"
     34 #include "gc/AllocKind.h"
     35 #include "gc/Barrier.h"
     36 #include "gc/GCEnum.h"
     37 #include "js/CallArgs.h"
     38 #include "js/CallNonGenericMethod.h"
     39 #include "js/Class.h"
     40 #include "js/ErrorReport.h"
     41 #include "js/friend/ErrorMessages.h"
     42 #include "js/PropertyDescriptor.h"
     43 #include "js/PropertySpec.h"
     44 #include "js/RootingAPI.h"
     45 #include "js/TypeDecls.h"
     46 #include "js/Value.h"
     47 #include "vm/BytecodeUtil.h"
     48 #include "vm/GlobalObject.h"
     49 #include "vm/JSAtomState.h"
     50 #include "vm/JSContext.h"
     51 #include "vm/JSObject.h"
     52 #include "vm/PlainObject.h"
     53 #include "vm/StringType.h"
     54 
     55 #include "vm/JSObject-inl.h"
     56 #include "vm/NativeObject-inl.h"
     57 
     58 using namespace js;
     59 using namespace js::temporal;
     60 
     61 static inline bool IsPlainDateTime(Handle<Value> v) {
     62  return v.isObject() && v.toObject().is<PlainDateTimeObject>();
     63 }
     64 
     65 #ifdef DEBUG
     66 /**
     67 * IsValidISODate ( year, month, day )
     68 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond )
     69 */
     70 bool js::temporal::IsValidISODateTime(const ISODateTime& isoDateTime) {
     71  return IsValidISODate(isoDateTime.date) && IsValidTime(isoDateTime.time);
     72 }
     73 #endif
     74 
     75 /**
     76 * ISODateTimeWithinLimits ( isoDateTime )
     77 */
     78 bool js::temporal::ISODateTimeWithinLimits(const ISODateTime& isoDateTime) {
     79  MOZ_ASSERT(IsValidISODateTime(isoDateTime));
     80 
     81  constexpr auto min = ISODate::min();
     82  constexpr auto max = ISODate::max();
     83 
     84  const auto& year = isoDateTime.date.year;
     85 
     86  // Fast-path when the input is definitely in range.
     87  if (min.year < year && year < max.year) {
     88    return true;
     89  }
     90 
     91  // Check |isoDateTime| is within the valid limits.
     92  if (year < 0) {
     93    if (isoDateTime.date != min) {
     94      return isoDateTime.date > min;
     95    }
     96 
     97    // Needs to be past midnight.
     98    return isoDateTime.time != Time{};
     99  }
    100  return isoDateTime.date <= max;
    101 }
    102 
    103 /**
    104 * CreateTemporalDateTime ( isoDateTime, calendar [ , newTarget ] )
    105 */
    106 static PlainDateTimeObject* CreateTemporalDateTime(
    107    JSContext* cx, const CallArgs& args, const ISODateTime& isoDateTime,
    108    Handle<CalendarValue> calendar) {
    109  MOZ_ASSERT(IsValidISODateTime(isoDateTime));
    110 
    111  // Step 1.
    112  if (!ISODateTimeWithinLimits(isoDateTime)) {
    113    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    114                              JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
    115    return nullptr;
    116  }
    117 
    118  // Steps 2-3.
    119  Rooted<JSObject*> proto(cx);
    120  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_PlainDateTime,
    121                                          &proto)) {
    122    return nullptr;
    123  }
    124 
    125  auto* object = NewObjectWithClassProto<PlainDateTimeObject>(cx, proto);
    126  if (!object) {
    127    return nullptr;
    128  }
    129 
    130  // Step 4.
    131  auto packedDate = PackedDate::pack(isoDateTime.date);
    132  auto packedTime = PackedTime::pack(isoDateTime.time);
    133  object->initFixedSlot(PlainDateTimeObject::PACKED_DATE_SLOT,
    134                        PrivateUint32Value(packedDate.value));
    135  object->initFixedSlot(
    136      PlainDateTimeObject::PACKED_TIME_SLOT,
    137      DoubleValue(mozilla::BitwiseCast<double>(packedTime.value)));
    138 
    139  // Step 5.
    140  object->initFixedSlot(PlainDateTimeObject::CALENDAR_SLOT,
    141                        calendar.toSlotValue());
    142 
    143  // Step 6.
    144  return object;
    145 }
    146 
    147 /**
    148 * CreateTemporalDateTime ( isoDateTime, calendar [ , newTarget ] )
    149 */
    150 PlainDateTimeObject* js::temporal::CreateTemporalDateTime(
    151    JSContext* cx, const ISODateTime& isoDateTime,
    152    Handle<CalendarValue> calendar) {
    153  MOZ_ASSERT(IsValidISODateTime(isoDateTime));
    154 
    155  // Step 1.
    156  if (!ISODateTimeWithinLimits(isoDateTime)) {
    157    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    158                              JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
    159    return nullptr;
    160  }
    161 
    162  // Steps 2-3.
    163  auto* object = NewBuiltinClassInstance<PlainDateTimeObject>(cx);
    164  if (!object) {
    165    return nullptr;
    166  }
    167 
    168  // Step 4.
    169  auto packedDate = PackedDate::pack(isoDateTime.date);
    170  auto packedTime = PackedTime::pack(isoDateTime.time);
    171  object->initFixedSlot(PlainDateTimeObject::PACKED_DATE_SLOT,
    172                        PrivateUint32Value(packedDate.value));
    173  object->initFixedSlot(
    174      PlainDateTimeObject::PACKED_TIME_SLOT,
    175      DoubleValue(mozilla::BitwiseCast<double>(packedTime.value)));
    176 
    177  // Step 5.
    178  object->initFixedSlot(PlainDateTimeObject::CALENDAR_SLOT,
    179                        calendar.toSlotValue());
    180 
    181  // Step 6.
    182  return object;
    183 }
    184 
    185 /**
    186 * CreateTemporalDateTime ( isoDateTime, calendar [ , newTarget ] )
    187 */
    188 static PlainDateTimeObject* CreateTemporalDateTime(
    189    JSContext* cx, Handle<PlainDateTime> dateTime) {
    190  MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
    191  return CreateTemporalDateTime(cx, dateTime, dateTime.calendar());
    192 }
    193 
    194 /**
    195 * CreateTemporalDateTime ( isoDateTime, calendar [ , newTarget ] )
    196 */
    197 static bool CreateTemporalDateTime(JSContext* cx, const ISODateTime& dateTime,
    198                                   Handle<CalendarValue> calendar,
    199                                   MutableHandle<PlainDateTime> result) {
    200  MOZ_ASSERT(IsValidISODateTime(dateTime));
    201 
    202  // Step 1.
    203  if (!ISODateTimeWithinLimits(dateTime)) {
    204    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    205                              JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
    206    return false;
    207  }
    208 
    209  // Step 2-6.
    210  result.set(PlainDateTime{dateTime, calendar});
    211  return true;
    212 }
    213 
    214 /**
    215 * InterpretTemporalDateTimeFields ( calendar, fields, overflow )
    216 */
    217 bool js::temporal::InterpretTemporalDateTimeFields(
    218    JSContext* cx, Handle<CalendarValue> calendar,
    219    Handle<CalendarFields> fields, TemporalOverflow overflow,
    220    ISODateTime* result) {
    221  // Step 1.
    222  Rooted<PlainDate> temporalDate(cx);
    223  if (!CalendarDateFromFields(cx, calendar, fields, overflow, &temporalDate)) {
    224    return false;
    225  }
    226 
    227  // Step 2.
    228  auto timeLike = TemporalTimeLike{
    229      fields.hour(),        fields.minute(),      fields.second(),
    230      fields.millisecond(), fields.microsecond(), fields.nanosecond(),
    231  };
    232  Time time;
    233  if (!RegulateTime(cx, timeLike, overflow, &time)) {
    234    return false;
    235  }
    236 
    237  // Step 3.
    238  *result = {temporalDate.date(), time};
    239  return true;
    240 }
    241 
    242 struct DateTimeOptions {
    243  TemporalOverflow overflow = TemporalOverflow::Constrain;
    244 };
    245 
    246 /**
    247 * ToTemporalDateTime ( item [ , options ] )
    248 */
    249 static bool ToTemporalDateTimeOptions(JSContext* cx, Handle<Value> options,
    250                                      DateTimeOptions* result) {
    251  if (options.isUndefined()) {
    252    *result = {};
    253    return true;
    254  }
    255 
    256  // NOTE: |options| are only passed from `Temporal.PlainDateTime.from`.
    257 
    258  Rooted<JSObject*> resolvedOptions(
    259      cx, RequireObjectArg(cx, "options", "from", options));
    260  if (!resolvedOptions) {
    261    return false;
    262  }
    263 
    264  auto overflow = TemporalOverflow::Constrain;
    265  if (!GetTemporalOverflowOption(cx, resolvedOptions, &overflow)) {
    266    return false;
    267  }
    268 
    269  *result = {overflow};
    270  return true;
    271 }
    272 
    273 /**
    274 * ToTemporalDateTime ( item [ , options ] )
    275 */
    276 static bool ToTemporalDateTime(JSContext* cx, Handle<JSObject*> item,
    277                               Handle<Value> options,
    278                               MutableHandle<PlainDateTime> result) {
    279  // Step 1. (Not applicable in our implementation.)
    280 
    281  // Step 2.a.
    282  if (auto* plainDateTime = item->maybeUnwrapIf<PlainDateTimeObject>()) {
    283    auto dateTime = plainDateTime->dateTime();
    284    Rooted<CalendarValue> calendar(cx, plainDateTime->calendar());
    285    if (!calendar.wrap(cx)) {
    286      return false;
    287    }
    288 
    289    // Steps 2.a.i-ii.
    290    DateTimeOptions ignoredOptions;
    291    if (!ToTemporalDateTimeOptions(cx, options, &ignoredOptions)) {
    292      return false;
    293    }
    294 
    295    // Step 2.a.iii.
    296    result.set(PlainDateTime{dateTime, calendar});
    297    return true;
    298  }
    299 
    300  // Step 2.b.
    301  if (auto* zonedDateTime = item->maybeUnwrapIf<ZonedDateTimeObject>()) {
    302    auto epochNs = zonedDateTime->epochNanoseconds();
    303    Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
    304    Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());
    305 
    306    if (!timeZone.wrap(cx)) {
    307      return false;
    308    }
    309    if (!calendar.wrap(cx)) {
    310      return false;
    311    }
    312 
    313    // Step 2.b.i.
    314    ISODateTime dateTime;
    315    if (!GetISODateTimeFor(cx, timeZone, epochNs, &dateTime)) {
    316      return false;
    317    }
    318 
    319    // Steps 2.b.ii-iii.
    320    DateTimeOptions ignoredOptions;
    321    if (!ToTemporalDateTimeOptions(cx, options, &ignoredOptions)) {
    322      return false;
    323    }
    324 
    325    // Step 2.b.iv.
    326    result.set(PlainDateTime{dateTime, calendar});
    327    return true;
    328  }
    329 
    330  // Step 2.c.
    331  if (auto* plainDate = item->maybeUnwrapIf<PlainDateObject>()) {
    332    auto date = plainDate->date();
    333    Rooted<CalendarValue> calendar(cx, plainDate->calendar());
    334    if (!calendar.wrap(cx)) {
    335      return false;
    336    }
    337 
    338    // Steps 2.c.i-ii.
    339    DateTimeOptions ignoredOptions;
    340    if (!ToTemporalDateTimeOptions(cx, options, &ignoredOptions)) {
    341      return false;
    342    }
    343 
    344    // Steps 2.c.iii-iv.
    345    return CreateTemporalDateTime(cx, ISODateTime{date}, calendar, result);
    346  }
    347 
    348  // Step 2.d.
    349  Rooted<CalendarValue> calendar(cx);
    350  if (!GetTemporalCalendarWithISODefault(cx, item, &calendar)) {
    351    return false;
    352  }
    353 
    354  // Step 2.e.
    355  Rooted<CalendarFields> fields(cx);
    356  if (!PrepareCalendarFields(cx, calendar, item,
    357                             {
    358                                 CalendarField::Year,
    359                                 CalendarField::Month,
    360                                 CalendarField::MonthCode,
    361                                 CalendarField::Day,
    362                                 CalendarField::Hour,
    363                                 CalendarField::Minute,
    364                                 CalendarField::Second,
    365                                 CalendarField::Millisecond,
    366                                 CalendarField::Microsecond,
    367                                 CalendarField::Nanosecond,
    368                             },
    369                             &fields)) {
    370    return false;
    371  }
    372 
    373  // Steps 2.f-g.
    374  DateTimeOptions resolvedOptions;
    375  if (!ToTemporalDateTimeOptions(cx, options, &resolvedOptions)) {
    376    return false;
    377  }
    378  auto [overflow] = resolvedOptions;
    379 
    380  // Step 2.h.
    381  ISODateTime dateTime;
    382  if (!InterpretTemporalDateTimeFields(cx, calendar, fields, overflow,
    383                                       &dateTime)) {
    384    return false;
    385  }
    386 
    387  // Step 2.i.
    388  return CreateTemporalDateTime(cx, dateTime, calendar, result);
    389 }
    390 
    391 /**
    392 * ToTemporalDateTime ( item [ , options ] )
    393 */
    394 static bool ToTemporalDateTime(JSContext* cx, Handle<Value> item,
    395                               Handle<Value> options,
    396                               MutableHandle<PlainDateTime> result) {
    397  // Step 1. (Not applicable)
    398 
    399  // Step 2.
    400  if (item.isObject()) {
    401    Rooted<JSObject*> itemObj(cx, &item.toObject());
    402    return ToTemporalDateTime(cx, itemObj, options, result);
    403  }
    404 
    405  // Step 3.
    406  if (!item.isString()) {
    407    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item,
    408                     nullptr, "not a string");
    409    return false;
    410  }
    411  Rooted<JSString*> string(cx, item.toString());
    412 
    413  // Steps 4-5.
    414  ISODateTime dateTime;
    415  Rooted<JSString*> calendarString(cx);
    416  if (!ParseTemporalDateTimeString(cx, string, &dateTime, &calendarString)) {
    417    return false;
    418  }
    419  MOZ_ASSERT(IsValidISODateTime(dateTime));
    420 
    421  // Steps 6-8.
    422  Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601));
    423  if (calendarString) {
    424    if (!CanonicalizeCalendar(cx, calendarString, &calendar)) {
    425      return false;
    426    }
    427  }
    428 
    429  // Steps 9-10.
    430  DateTimeOptions ignoredOptions;
    431  if (!ToTemporalDateTimeOptions(cx, options, &ignoredOptions)) {
    432    return false;
    433  }
    434 
    435  // Steps 11-13.
    436  return CreateTemporalDateTime(cx, dateTime, calendar, result);
    437 }
    438 
    439 /**
    440 * ToTemporalDateTime ( item [ , options ] )
    441 */
    442 static bool ToTemporalDateTime(JSContext* cx, Handle<Value> item,
    443                               MutableHandle<PlainDateTime> result) {
    444  return ToTemporalDateTime(cx, item, UndefinedHandleValue, result);
    445 }
    446 
    447 /**
    448 * CompareISODateTime ( isoDateTime1, isoDateTime2 )
    449 */
    450 static int32_t CompareISODateTime(const ISODateTime& isoDateTime1,
    451                                  const ISODateTime& isoDateTime2) {
    452  // Steps 1-2.
    453  if (int32_t dateResult =
    454          CompareISODate(isoDateTime1.date, isoDateTime2.date)) {
    455    return dateResult;
    456  }
    457 
    458  // Steps 3.
    459  return CompareTimeRecord(isoDateTime1.time, isoDateTime2.time);
    460 }
    461 
    462 /**
    463 * Add24HourDaysToTimeDuration ( d, days )
    464 */
    465 static TimeDuration Add24HourDaysToTimeDuration(const TimeDuration& d,
    466                                                int32_t days) {
    467  // Step 1.
    468  auto result = d + TimeDuration::fromDays(days);
    469 
    470  // Step 2.
    471  MOZ_ASSERT(result.abs() <= TimeDuration::max());
    472 
    473  // Step 3.
    474  return result;
    475 }
    476 
    477 /**
    478 * DifferenceISODateTime ( isoDateTime1, isoDateTime2, calendar, largestUnit )
    479 */
    480 static bool DifferenceISODateTime(JSContext* cx,
    481                                  const ISODateTime& isoDateTime1,
    482                                  const ISODateTime& isoDateTime2,
    483                                  Handle<CalendarValue> calendar,
    484                                  TemporalUnit largestUnit,
    485                                  InternalDuration* result) {
    486  MOZ_ASSERT(isoDateTime1 != isoDateTime2,
    487             "fast-path for same date-time case handled in caller");
    488 
    489  // Steps 1-2.
    490  MOZ_ASSERT(IsValidISODateTime(isoDateTime1));
    491  MOZ_ASSERT(IsValidISODateTime(isoDateTime2));
    492  MOZ_ASSERT(ISODateTimeWithinLimits(isoDateTime1));
    493  MOZ_ASSERT(ISODateTimeWithinLimits(isoDateTime2));
    494 
    495  // Step 3.
    496  auto timeDuration = DifferenceTime(isoDateTime1.time, isoDateTime2.time);
    497 
    498  // Step 4.
    499  int32_t timeSign = TimeDurationSign(timeDuration);
    500 
    501  // Step 5.
    502  int32_t dateSign = CompareISODate(isoDateTime1.date, isoDateTime2.date);
    503 
    504  // Step 6.
    505  auto adjustedDate = isoDateTime2.date;
    506 
    507  // Step 7.
    508  if (timeSign == dateSign) {
    509    // Step 7.a.
    510    adjustedDate = BalanceISODate(adjustedDate, timeSign);
    511 
    512    // Step 7.b.
    513    timeDuration = Add24HourDaysToTimeDuration(timeDuration, -timeSign);
    514  }
    515 
    516  MOZ_ASSERT(IsValidISODate(adjustedDate));
    517  MOZ_ASSERT(ISODateWithinLimits(adjustedDate));
    518 
    519  // Step 8.
    520  auto dateLargestUnit = std::min(TemporalUnit::Day, largestUnit);
    521 
    522  // Step 9.
    523  DateDuration dateDifference;
    524  if (!CalendarDateUntil(cx, calendar, isoDateTime1.date, adjustedDate,
    525                         dateLargestUnit, &dateDifference)) {
    526    return false;
    527  }
    528 
    529  // Steps 10.
    530  if (largestUnit > TemporalUnit::Day) {
    531    // Step 10.a.
    532    auto days = mozilla::AssertedCast<int32_t>(dateDifference.days);
    533    timeDuration = Add24HourDaysToTimeDuration(timeDuration, days);
    534 
    535    // Step 10.b.
    536    dateDifference.days = 0;
    537  }
    538 
    539  // Step 11.
    540  MOZ_ASSERT(
    541      DateDurationSign(dateDifference) * TimeDurationSign(timeDuration) >= 0);
    542  *result = {dateDifference, timeDuration};
    543  return true;
    544 }
    545 
    546 /**
    547 * RoundISODateTime ( isoDateTime, increment, unit, roundingMode )
    548 */
    549 ISODateTime js::temporal::RoundISODateTime(const ISODateTime& isoDateTime,
    550                                           Increment increment,
    551                                           TemporalUnit unit,
    552                                           TemporalRoundingMode roundingMode) {
    553  MOZ_ASSERT(IsValidISODateTime(isoDateTime));
    554 
    555  // Step 1.
    556  MOZ_ASSERT(ISODateTimeWithinLimits(isoDateTime));
    557 
    558  // Step 2.
    559  auto roundedTime = RoundTime(isoDateTime.time, increment, unit, roundingMode);
    560  MOZ_ASSERT(0 <= roundedTime.days && roundedTime.days <= 1);
    561 
    562  // Step 3.
    563  auto balanceResult = BalanceISODate(isoDateTime.date, roundedTime.days);
    564 
    565  // Step 4.
    566  return {balanceResult, roundedTime.time};
    567 }
    568 
    569 /**
    570 * DifferencePlainDateTimeWithRounding ( isoDateTime1, isoDateTime2, calendar,
    571 * largestUnit, roundingIncrement, smallestUnit, roundingMode )
    572 */
    573 bool js::temporal::DifferencePlainDateTimeWithRounding(
    574    JSContext* cx, const ISODateTime& isoDateTime1,
    575    const ISODateTime& isoDateTime2, Handle<CalendarValue> calendar,
    576    const DifferenceSettings& settings, InternalDuration* result) {
    577  // Step 1.
    578  if (isoDateTime1 == isoDateTime2) {
    579    // Step 1.a.
    580    *result = {};
    581    return true;
    582  }
    583 
    584  // Step 2.
    585  if (!ISODateTimeWithinLimits(isoDateTime1) ||
    586      !ISODateTimeWithinLimits(isoDateTime2)) {
    587    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    588                              JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
    589    return false;
    590  }
    591 
    592  // Step 3.
    593  InternalDuration diff;
    594  if (!DifferenceISODateTime(cx, isoDateTime1, isoDateTime2, calendar,
    595                             settings.largestUnit, &diff)) {
    596    return false;
    597  }
    598 
    599  // Step 4.
    600  if (settings.smallestUnit == TemporalUnit::Nanosecond &&
    601      settings.roundingIncrement == Increment{1}) {
    602    *result = diff;
    603    return true;
    604  }
    605 
    606  // Step 5.
    607  auto originEpochNs = GetUTCEpochNanoseconds(isoDateTime1);
    608 
    609  // Step 6.
    610  auto destEpochNs = GetUTCEpochNanoseconds(isoDateTime2);
    611 
    612  // Step 7.
    613  Rooted<TimeZoneValue> timeZone(cx, TimeZoneValue{});
    614  return RoundRelativeDuration(
    615      cx, diff, originEpochNs, destEpochNs, isoDateTime1, timeZone, calendar,
    616      settings.largestUnit, settings.roundingIncrement, settings.smallestUnit,
    617      settings.roundingMode, result);
    618 }
    619 
    620 /**
    621 * DifferencePlainDateTimeWithTotal ( isoDateTime1, isoDateTime2, calendar, unit
    622 * )
    623 */
    624 bool js::temporal::DifferencePlainDateTimeWithTotal(
    625    JSContext* cx, const ISODateTime& isoDateTime1,
    626    const ISODateTime& isoDateTime2, Handle<CalendarValue> calendar,
    627    TemporalUnit unit, double* result) {
    628  // Step 1.
    629  if (isoDateTime1 == isoDateTime2) {
    630    // Step 1.a.
    631    *result = 0;
    632    return true;
    633  }
    634 
    635  // Step 2.
    636  if (!ISODateTimeWithinLimits(isoDateTime1) ||
    637      !ISODateTimeWithinLimits(isoDateTime2)) {
    638    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    639                              JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
    640    return false;
    641  }
    642 
    643  // Step 3.
    644  InternalDuration diff;
    645  if (!DifferenceISODateTime(cx, isoDateTime1, isoDateTime2, calendar, unit,
    646                             &diff)) {
    647    return false;
    648  }
    649 
    650  // Step 4. (Optimized to avoid GetUTCEpochNanoseconds for non-calendar units.)
    651  if (unit > TemporalUnit::Day) {
    652    MOZ_ASSERT(diff.date == DateDuration{});
    653 
    654    // TotalRelativeDuration, steps 1-2. (Not applicable)
    655 
    656    // TotalRelativeDuration, step 3.
    657    *result = TotalTimeDuration(diff.time, unit);
    658    return true;
    659  } else if (unit == TemporalUnit::Day) {
    660    // TotalRelativeDuration, step 1. (Not applicable)
    661 
    662    // TotalRelativeDuration, step 2.
    663    auto days = mozilla::AssertedCast<int32_t>(diff.date.days);
    664    auto timeDuration = Add24HourDaysToTimeDuration(diff.time, days);
    665 
    666    // TotalRelativeDuration, step 3.
    667    *result = TotalTimeDuration(timeDuration, unit);
    668    return true;
    669  }
    670 
    671  // Step 5.
    672  auto originEpochNs = GetUTCEpochNanoseconds(isoDateTime1);
    673 
    674  // Step 6.
    675  auto destEpochNs = GetUTCEpochNanoseconds(isoDateTime2);
    676 
    677  // Step 7.
    678  Rooted<TimeZoneValue> timeZone(cx, TimeZoneValue{});
    679  return TotalRelativeDuration(cx, diff, originEpochNs, destEpochNs,
    680                               isoDateTime1, timeZone, calendar, unit, result);
    681 }
    682 
    683 /**
    684 * DifferenceTemporalPlainDateTime ( operation, dateTime, other, options )
    685 */
    686 static bool DifferenceTemporalPlainDateTime(JSContext* cx,
    687                                            TemporalDifference operation,
    688                                            const CallArgs& args) {
    689  Rooted<PlainDateTime> dateTime(
    690      cx, &args.thisv().toObject().as<PlainDateTimeObject>());
    691 
    692  // Step 1.
    693  Rooted<PlainDateTime> other(cx);
    694  if (!ToTemporalDateTime(cx, args.get(0), &other)) {
    695    return false;
    696  }
    697 
    698  // Step 2.
    699  if (!CalendarEquals(dateTime.calendar(), other.calendar())) {
    700    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    701                              JSMSG_TEMPORAL_CALENDAR_INCOMPATIBLE,
    702                              CalendarIdentifier(dateTime.calendar()).data(),
    703                              CalendarIdentifier(other.calendar()).data());
    704    return false;
    705  }
    706 
    707  // Steps 3-4.
    708  DifferenceSettings settings;
    709  if (args.hasDefined(1)) {
    710    // Step 3.
    711    Rooted<JSObject*> options(
    712        cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
    713    if (!options) {
    714      return false;
    715    }
    716 
    717    // Step 4.
    718    if (!GetDifferenceSettings(
    719            cx, operation, options, TemporalUnitGroup::DateTime,
    720            TemporalUnit::Nanosecond, TemporalUnit::Day, &settings)) {
    721      return false;
    722    }
    723  } else {
    724    // Steps 3-4.
    725    settings = {
    726        TemporalUnit::Nanosecond,
    727        TemporalUnit::Day,
    728        TemporalRoundingMode::Trunc,
    729        Increment{1},
    730    };
    731  }
    732 
    733  // Step 5.
    734  if (dateTime.dateTime() == other.dateTime()) {
    735    auto* obj = CreateTemporalDuration(cx, {});
    736    if (!obj) {
    737      return false;
    738    }
    739 
    740    args.rval().setObject(*obj);
    741    return true;
    742  }
    743 
    744  // Step 6.
    745  InternalDuration internalDuration;
    746  if (!DifferencePlainDateTimeWithRounding(cx, dateTime, other,
    747                                           dateTime.calendar(), settings,
    748                                           &internalDuration)) {
    749    return false;
    750  }
    751  MOZ_ASSERT(IsValidDuration(internalDuration));
    752 
    753  // Step 7.
    754  Duration result;
    755  if (!TemporalDurationFromInternal(cx, internalDuration, settings.largestUnit,
    756                                    &result)) {
    757    return false;
    758  }
    759  MOZ_ASSERT(IsValidDuration(result));
    760 
    761  // Step 8.
    762  if (operation == TemporalDifference::Since) {
    763    result = result.negate();
    764  }
    765 
    766  // Step 9.
    767  auto* obj = CreateTemporalDuration(cx, result);
    768  if (!obj) {
    769    return false;
    770  }
    771 
    772  args.rval().setObject(*obj);
    773  return true;
    774 }
    775 
    776 /**
    777 * AddDurationToDateTime ( operation, dateTime, temporalDurationLike, options )
    778 */
    779 static bool AddDurationToDateTime(JSContext* cx, TemporalAddDuration operation,
    780                                  const CallArgs& args) {
    781  Rooted<PlainDateTime> dateTime(
    782      cx, &args.thisv().toObject().as<PlainDateTimeObject>());
    783 
    784  // Step 1.
    785  Duration duration;
    786  if (!ToTemporalDuration(cx, args.get(0), &duration)) {
    787    return false;
    788  }
    789 
    790  // Step 2.
    791  if (operation == TemporalAddDuration::Subtract) {
    792    duration = duration.negate();
    793  }
    794 
    795  // Steps 3-4.
    796  auto overflow = TemporalOverflow::Constrain;
    797  if (args.hasDefined(1)) {
    798    // Step 3.
    799    Rooted<JSObject*> options(
    800        cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
    801    if (!options) {
    802      return false;
    803    }
    804 
    805    // Step 4.
    806    if (!GetTemporalOverflowOption(cx, options, &overflow)) {
    807      return false;
    808    }
    809  }
    810 
    811  // Step 5.
    812  auto internalDuration = ToInternalDurationRecordWith24HourDays(duration);
    813  MOZ_ASSERT(IsValidDuration(internalDuration));
    814 
    815  // Step 6.
    816  auto timeResult = AddTime(dateTime.time(), internalDuration.time);
    817 
    818  // Step 7. (Inlined AdjustDateDurationRecord)
    819  if (std::abs(timeResult.days) > TimeDuration::max().toDays()) {
    820    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    821                              JSMSG_TEMPORAL_DURATION_INVALID_NORMALIZED_TIME);
    822    return false;
    823  }
    824  auto dateDuration = DateDuration{
    825      internalDuration.date.years,
    826      internalDuration.date.months,
    827      internalDuration.date.weeks,
    828      timeResult.days,
    829  };
    830  MOZ_ASSERT(IsValidDuration(dateDuration));
    831 
    832  // Step 8.
    833  ISODate addedDate;
    834  if (!CalendarDateAdd(cx, dateTime.calendar(), dateTime.date(), dateDuration,
    835                       overflow, &addedDate)) {
    836    return false;
    837  }
    838 
    839  // Step 9.
    840  auto result = ISODateTime{addedDate, timeResult.time};
    841  MOZ_ASSERT(IsValidISODateTime(result));
    842 
    843  // Step 10.
    844  auto* obj = CreateTemporalDateTime(cx, result, dateTime.calendar());
    845  if (!obj) {
    846    return false;
    847  }
    848 
    849  args.rval().setObject(*obj);
    850  return true;
    851 }
    852 
    853 /**
    854 * Temporal.PlainDateTime ( isoYear, isoMonth, isoDay [ , hour [ , minute [ ,
    855 * second [ , millisecond [ , microsecond [ , nanosecond [ , calendar ] ] ] ] ]
    856 * ] ] )
    857 */
    858 static bool PlainDateTimeConstructor(JSContext* cx, unsigned argc, Value* vp) {
    859  CallArgs args = CallArgsFromVp(argc, vp);
    860 
    861  // Step 1.
    862  if (!ThrowIfNotConstructing(cx, args, "Temporal.PlainDateTime")) {
    863    return false;
    864  }
    865 
    866  // Step 2.
    867  double isoYear;
    868  if (!ToIntegerWithTruncation(cx, args.get(0), "year", &isoYear)) {
    869    return false;
    870  }
    871 
    872  // Step 3.
    873  double isoMonth;
    874  if (!ToIntegerWithTruncation(cx, args.get(1), "month", &isoMonth)) {
    875    return false;
    876  }
    877 
    878  // Step 4.
    879  double isoDay;
    880  if (!ToIntegerWithTruncation(cx, args.get(2), "day", &isoDay)) {
    881    return false;
    882  }
    883 
    884  // Step 5.
    885  double hour = 0;
    886  if (args.hasDefined(3)) {
    887    if (!ToIntegerWithTruncation(cx, args[3], "hour", &hour)) {
    888      return false;
    889    }
    890  }
    891 
    892  // Step 6.
    893  double minute = 0;
    894  if (args.hasDefined(4)) {
    895    if (!ToIntegerWithTruncation(cx, args[4], "minute", &minute)) {
    896      return false;
    897    }
    898  }
    899 
    900  // Step 7.
    901  double second = 0;
    902  if (args.hasDefined(5)) {
    903    if (!ToIntegerWithTruncation(cx, args[5], "second", &second)) {
    904      return false;
    905    }
    906  }
    907 
    908  // Step 8.
    909  double millisecond = 0;
    910  if (args.hasDefined(6)) {
    911    if (!ToIntegerWithTruncation(cx, args[6], "millisecond", &millisecond)) {
    912      return false;
    913    }
    914  }
    915 
    916  // Step 9.
    917  double microsecond = 0;
    918  if (args.hasDefined(7)) {
    919    if (!ToIntegerWithTruncation(cx, args[7], "microsecond", &microsecond)) {
    920      return false;
    921    }
    922  }
    923 
    924  // Step 10.
    925  double nanosecond = 0;
    926  if (args.hasDefined(8)) {
    927    if (!ToIntegerWithTruncation(cx, args[8], "nanosecond", &nanosecond)) {
    928      return false;
    929    }
    930  }
    931 
    932  // Steps 11-13.
    933  Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601));
    934  if (args.hasDefined(9)) {
    935    // Step 12.
    936    if (!args[9].isString()) {
    937      ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, args[9],
    938                       nullptr, "not a string");
    939      return false;
    940    }
    941 
    942    // Step 13.
    943    Rooted<JSString*> calendarString(cx, args[9].toString());
    944    if (!CanonicalizeCalendar(cx, calendarString, &calendar)) {
    945      return false;
    946    }
    947  }
    948 
    949  // Step 14.
    950  if (!ThrowIfInvalidISODate(cx, isoYear, isoMonth, isoDay)) {
    951    return false;
    952  }
    953 
    954  // Step 15.
    955  auto isoDate = ISODate{int32_t(isoYear), int32_t(isoMonth), int32_t(isoDay)};
    956 
    957  // Step 16.
    958  if (!ThrowIfInvalidTime(cx, hour, minute, second, millisecond, microsecond,
    959                          nanosecond)) {
    960    return false;
    961  }
    962 
    963  // Step 17.
    964  auto time =
    965      Time{int32_t(hour),        int32_t(minute),      int32_t(second),
    966           int32_t(millisecond), int32_t(microsecond), int32_t(nanosecond)};
    967 
    968  // Step 18.
    969  auto isoDateTime = ISODateTime{isoDate, time};
    970 
    971  // Step 19.
    972  auto* temporalDateTime =
    973      CreateTemporalDateTime(cx, args, isoDateTime, calendar);
    974  if (!temporalDateTime) {
    975    return false;
    976  }
    977 
    978  args.rval().setObject(*temporalDateTime);
    979  return true;
    980 }
    981 
    982 /**
    983 * Temporal.PlainDateTime.from ( item [ , options ] )
    984 */
    985 static bool PlainDateTime_from(JSContext* cx, unsigned argc, Value* vp) {
    986  CallArgs args = CallArgsFromVp(argc, vp);
    987 
    988  // Step 1.
    989  Rooted<PlainDateTime> dateTime(cx);
    990  if (!ToTemporalDateTime(cx, args.get(0), args.get(1), &dateTime)) {
    991    return false;
    992  }
    993 
    994  auto* result = CreateTemporalDateTime(cx, dateTime);
    995  if (!result) {
    996    return false;
    997  }
    998 
    999  args.rval().setObject(*result);
   1000  return true;
   1001 }
   1002 
   1003 /**
   1004 * Temporal.PlainDateTime.compare ( one, two )
   1005 */
   1006 static bool PlainDateTime_compare(JSContext* cx, unsigned argc, Value* vp) {
   1007  CallArgs args = CallArgsFromVp(argc, vp);
   1008 
   1009  // Step 1.
   1010  Rooted<PlainDateTime> one(cx);
   1011  if (!ToTemporalDateTime(cx, args.get(0), &one)) {
   1012    return false;
   1013  }
   1014 
   1015  // Step 2.
   1016  Rooted<PlainDateTime> two(cx);
   1017  if (!ToTemporalDateTime(cx, args.get(1), &two)) {
   1018    return false;
   1019  }
   1020 
   1021  // Step 3.
   1022  args.rval().setInt32(CompareISODateTime(one, two));
   1023  return true;
   1024 }
   1025 
   1026 /**
   1027 * get Temporal.PlainDateTime.prototype.calendarId
   1028 */
   1029 static bool PlainDateTime_calendarId(JSContext* cx, const CallArgs& args) {
   1030  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1031 
   1032  // Step 3.
   1033  auto* str =
   1034      NewStringCopy<CanGC>(cx, CalendarIdentifier(dateTime->calendar()));
   1035  if (!str) {
   1036    return false;
   1037  }
   1038 
   1039  args.rval().setString(str);
   1040  return true;
   1041 }
   1042 
   1043 /**
   1044 * get Temporal.PlainDateTime.prototype.calendarId
   1045 */
   1046 static bool PlainDateTime_calendarId(JSContext* cx, unsigned argc, Value* vp) {
   1047  // Steps 1-2.
   1048  CallArgs args = CallArgsFromVp(argc, vp);
   1049  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_calendarId>(cx,
   1050                                                                         args);
   1051 }
   1052 
   1053 /**
   1054 * get Temporal.PlainDateTime.prototype.era
   1055 */
   1056 static bool PlainDateTime_era(JSContext* cx, const CallArgs& args) {
   1057  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1058  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1059 
   1060  // Step 3.
   1061  return CalendarEra(cx, calendar, dateTime->date(), args.rval());
   1062 }
   1063 
   1064 /**
   1065 * get Temporal.PlainDateTime.prototype.era
   1066 */
   1067 static bool PlainDateTime_era(JSContext* cx, unsigned argc, Value* vp) {
   1068  // Steps 1-2.
   1069  CallArgs args = CallArgsFromVp(argc, vp);
   1070  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_era>(cx, args);
   1071 }
   1072 
   1073 /**
   1074 * get Temporal.PlainDateTime.prototype.eraYear
   1075 */
   1076 static bool PlainDateTime_eraYear(JSContext* cx, const CallArgs& args) {
   1077  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1078  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1079 
   1080  // Steps 3-5.
   1081  return CalendarEraYear(cx, calendar, dateTime->date(), args.rval());
   1082 }
   1083 
   1084 /**
   1085 * get Temporal.PlainDateTime.prototype.eraYear
   1086 */
   1087 static bool PlainDateTime_eraYear(JSContext* cx, unsigned argc, Value* vp) {
   1088  // Steps 1-2.
   1089  CallArgs args = CallArgsFromVp(argc, vp);
   1090  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_eraYear>(cx, args);
   1091 }
   1092 
   1093 /**
   1094 * get Temporal.PlainDateTime.prototype.year
   1095 */
   1096 static bool PlainDateTime_year(JSContext* cx, const CallArgs& args) {
   1097  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1098  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1099 
   1100  // Step 3.
   1101  return CalendarYear(cx, calendar, dateTime->date(), args.rval());
   1102 }
   1103 
   1104 /**
   1105 * get Temporal.PlainDateTime.prototype.year
   1106 */
   1107 static bool PlainDateTime_year(JSContext* cx, unsigned argc, Value* vp) {
   1108  // Steps 1-2.
   1109  CallArgs args = CallArgsFromVp(argc, vp);
   1110  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_year>(cx, args);
   1111 }
   1112 
   1113 /**
   1114 * get Temporal.PlainDateTime.prototype.month
   1115 */
   1116 static bool PlainDateTime_month(JSContext* cx, const CallArgs& args) {
   1117  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1118  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1119 
   1120  // Step 3.
   1121  return CalendarMonth(cx, calendar, dateTime->date(), args.rval());
   1122 }
   1123 
   1124 /**
   1125 * get Temporal.PlainDateTime.prototype.month
   1126 */
   1127 static bool PlainDateTime_month(JSContext* cx, unsigned argc, Value* vp) {
   1128  // Steps 1-2.
   1129  CallArgs args = CallArgsFromVp(argc, vp);
   1130  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_month>(cx, args);
   1131 }
   1132 
   1133 /**
   1134 * get Temporal.PlainDateTime.prototype.monthCode
   1135 */
   1136 static bool PlainDateTime_monthCode(JSContext* cx, const CallArgs& args) {
   1137  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1138  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1139 
   1140  // Step 3.
   1141  return CalendarMonthCode(cx, calendar, dateTime->date(), args.rval());
   1142 }
   1143 
   1144 /**
   1145 * get Temporal.PlainDateTime.prototype.monthCode
   1146 */
   1147 static bool PlainDateTime_monthCode(JSContext* cx, unsigned argc, Value* vp) {
   1148  // Steps 1-2.
   1149  CallArgs args = CallArgsFromVp(argc, vp);
   1150  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_monthCode>(cx,
   1151                                                                        args);
   1152 }
   1153 
   1154 /**
   1155 * get Temporal.PlainDateTime.prototype.day
   1156 */
   1157 static bool PlainDateTime_day(JSContext* cx, const CallArgs& args) {
   1158  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1159  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1160 
   1161  // Step 3.
   1162  return CalendarDay(cx, calendar, dateTime->date(), args.rval());
   1163 }
   1164 
   1165 /**
   1166 * get Temporal.PlainDateTime.prototype.day
   1167 */
   1168 static bool PlainDateTime_day(JSContext* cx, unsigned argc, Value* vp) {
   1169  // Steps 1-2.
   1170  CallArgs args = CallArgsFromVp(argc, vp);
   1171  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_day>(cx, args);
   1172 }
   1173 
   1174 /**
   1175 * get Temporal.PlainDateTime.prototype.hour
   1176 */
   1177 static bool PlainDateTime_hour(JSContext* cx, const CallArgs& args) {
   1178  // Step 3.
   1179  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1180  args.rval().setInt32(dateTime->time().hour);
   1181  return true;
   1182 }
   1183 
   1184 /**
   1185 * get Temporal.PlainDateTime.prototype.hour
   1186 */
   1187 static bool PlainDateTime_hour(JSContext* cx, unsigned argc, Value* vp) {
   1188  // Steps 1-2.
   1189  CallArgs args = CallArgsFromVp(argc, vp);
   1190  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_hour>(cx, args);
   1191 }
   1192 
   1193 /**
   1194 * get Temporal.PlainDateTime.prototype.minute
   1195 */
   1196 static bool PlainDateTime_minute(JSContext* cx, const CallArgs& args) {
   1197  // Step 3.
   1198  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1199  args.rval().setInt32(dateTime->time().minute);
   1200  return true;
   1201 }
   1202 
   1203 /**
   1204 * get Temporal.PlainDateTime.prototype.minute
   1205 */
   1206 static bool PlainDateTime_minute(JSContext* cx, unsigned argc, Value* vp) {
   1207  // Steps 1-2.
   1208  CallArgs args = CallArgsFromVp(argc, vp);
   1209  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_minute>(cx, args);
   1210 }
   1211 
   1212 /**
   1213 * get Temporal.PlainDateTime.prototype.second
   1214 */
   1215 static bool PlainDateTime_second(JSContext* cx, const CallArgs& args) {
   1216  // Step 3.
   1217  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1218  args.rval().setInt32(dateTime->time().second);
   1219  return true;
   1220 }
   1221 
   1222 /**
   1223 * get Temporal.PlainDateTime.prototype.second
   1224 */
   1225 static bool PlainDateTime_second(JSContext* cx, unsigned argc, Value* vp) {
   1226  // Steps 1-2.
   1227  CallArgs args = CallArgsFromVp(argc, vp);
   1228  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_second>(cx, args);
   1229 }
   1230 
   1231 /**
   1232 * get Temporal.PlainDateTime.prototype.millisecond
   1233 */
   1234 static bool PlainDateTime_millisecond(JSContext* cx, const CallArgs& args) {
   1235  // Step 3.
   1236  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1237  args.rval().setInt32(dateTime->time().millisecond);
   1238  return true;
   1239 }
   1240 
   1241 /**
   1242 * get Temporal.PlainDateTime.prototype.millisecond
   1243 */
   1244 static bool PlainDateTime_millisecond(JSContext* cx, unsigned argc, Value* vp) {
   1245  // Steps 1-2.
   1246  CallArgs args = CallArgsFromVp(argc, vp);
   1247  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_millisecond>(cx,
   1248                                                                          args);
   1249 }
   1250 
   1251 /**
   1252 * get Temporal.PlainDateTime.prototype.microsecond
   1253 */
   1254 static bool PlainDateTime_microsecond(JSContext* cx, const CallArgs& args) {
   1255  // Step 3.
   1256  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1257  args.rval().setInt32(dateTime->time().microsecond);
   1258  return true;
   1259 }
   1260 
   1261 /**
   1262 * get Temporal.PlainDateTime.prototype.microsecond
   1263 */
   1264 static bool PlainDateTime_microsecond(JSContext* cx, unsigned argc, Value* vp) {
   1265  // Steps 1-2.
   1266  CallArgs args = CallArgsFromVp(argc, vp);
   1267  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_microsecond>(cx,
   1268                                                                          args);
   1269 }
   1270 
   1271 /**
   1272 * get Temporal.PlainDateTime.prototype.nanosecond
   1273 */
   1274 static bool PlainDateTime_nanosecond(JSContext* cx, const CallArgs& args) {
   1275  // Step 3.
   1276  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1277  args.rval().setInt32(dateTime->time().nanosecond);
   1278  return true;
   1279 }
   1280 
   1281 /**
   1282 * get Temporal.PlainDateTime.prototype.nanosecond
   1283 */
   1284 static bool PlainDateTime_nanosecond(JSContext* cx, unsigned argc, Value* vp) {
   1285  // Steps 1-2.
   1286  CallArgs args = CallArgsFromVp(argc, vp);
   1287  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_nanosecond>(cx,
   1288                                                                         args);
   1289 }
   1290 
   1291 /**
   1292 * get Temporal.PlainDateTime.prototype.dayOfWeek
   1293 */
   1294 static bool PlainDateTime_dayOfWeek(JSContext* cx, const CallArgs& args) {
   1295  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1296  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1297 
   1298  // Step 3.
   1299  return CalendarDayOfWeek(cx, calendar, dateTime->date(), args.rval());
   1300 }
   1301 
   1302 /**
   1303 * get Temporal.PlainDateTime.prototype.dayOfWeek
   1304 */
   1305 static bool PlainDateTime_dayOfWeek(JSContext* cx, unsigned argc, Value* vp) {
   1306  // Steps 1-2.
   1307  CallArgs args = CallArgsFromVp(argc, vp);
   1308  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_dayOfWeek>(cx,
   1309                                                                        args);
   1310 }
   1311 
   1312 /**
   1313 * get Temporal.PlainDateTime.prototype.dayOfYear
   1314 */
   1315 static bool PlainDateTime_dayOfYear(JSContext* cx, const CallArgs& args) {
   1316  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1317  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1318 
   1319  // Step 3.
   1320  return CalendarDayOfYear(cx, calendar, dateTime->date(), args.rval());
   1321 }
   1322 
   1323 /**
   1324 * get Temporal.PlainDateTime.prototype.dayOfYear
   1325 */
   1326 static bool PlainDateTime_dayOfYear(JSContext* cx, unsigned argc, Value* vp) {
   1327  // Steps 1-2.
   1328  CallArgs args = CallArgsFromVp(argc, vp);
   1329  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_dayOfYear>(cx,
   1330                                                                        args);
   1331 }
   1332 
   1333 /**
   1334 * get Temporal.PlainDateTime.prototype.weekOfYear
   1335 */
   1336 static bool PlainDateTime_weekOfYear(JSContext* cx, const CallArgs& args) {
   1337  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1338  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1339 
   1340  // Steps 3-5.
   1341  return CalendarWeekOfYear(cx, calendar, dateTime->date(), args.rval());
   1342 }
   1343 
   1344 /**
   1345 * get Temporal.PlainDateTime.prototype.weekOfYear
   1346 */
   1347 static bool PlainDateTime_weekOfYear(JSContext* cx, unsigned argc, Value* vp) {
   1348  // Steps 1-2.
   1349  CallArgs args = CallArgsFromVp(argc, vp);
   1350  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_weekOfYear>(cx,
   1351                                                                         args);
   1352 }
   1353 
   1354 /**
   1355 * get Temporal.PlainDateTime.prototype.yearOfWeek
   1356 */
   1357 static bool PlainDateTime_yearOfWeek(JSContext* cx, const CallArgs& args) {
   1358  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1359  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1360 
   1361  // Steps 3-5.
   1362  return CalendarYearOfWeek(cx, calendar, dateTime->date(), args.rval());
   1363 }
   1364 
   1365 /**
   1366 * get Temporal.PlainDateTime.prototype.yearOfWeek
   1367 */
   1368 static bool PlainDateTime_yearOfWeek(JSContext* cx, unsigned argc, Value* vp) {
   1369  // Steps 1-2.
   1370  CallArgs args = CallArgsFromVp(argc, vp);
   1371  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_yearOfWeek>(cx,
   1372                                                                         args);
   1373 }
   1374 
   1375 /**
   1376 * get Temporal.PlainDateTime.prototype.daysInWeek
   1377 */
   1378 static bool PlainDateTime_daysInWeek(JSContext* cx, const CallArgs& args) {
   1379  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1380  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1381 
   1382  // Step 3.
   1383  return CalendarDaysInWeek(cx, calendar, dateTime->date(), args.rval());
   1384 }
   1385 
   1386 /**
   1387 * get Temporal.PlainDateTime.prototype.daysInWeek
   1388 */
   1389 static bool PlainDateTime_daysInWeek(JSContext* cx, unsigned argc, Value* vp) {
   1390  // Steps 1-2.
   1391  CallArgs args = CallArgsFromVp(argc, vp);
   1392  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInWeek>(cx,
   1393                                                                         args);
   1394 }
   1395 
   1396 /**
   1397 * get Temporal.PlainDateTime.prototype.daysInMonth
   1398 */
   1399 static bool PlainDateTime_daysInMonth(JSContext* cx, const CallArgs& args) {
   1400  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1401  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1402 
   1403  // Step 3.
   1404  return CalendarDaysInMonth(cx, calendar, dateTime->date(), args.rval());
   1405 }
   1406 
   1407 /**
   1408 * get Temporal.PlainDateTime.prototype.daysInMonth
   1409 */
   1410 static bool PlainDateTime_daysInMonth(JSContext* cx, unsigned argc, Value* vp) {
   1411  // Steps 1-2.
   1412  CallArgs args = CallArgsFromVp(argc, vp);
   1413  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInMonth>(cx,
   1414                                                                          args);
   1415 }
   1416 
   1417 /**
   1418 * get Temporal.PlainDateTime.prototype.daysInYear
   1419 */
   1420 static bool PlainDateTime_daysInYear(JSContext* cx, const CallArgs& args) {
   1421  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1422  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1423 
   1424  // Step 3.
   1425  return CalendarDaysInYear(cx, calendar, dateTime->date(), args.rval());
   1426 }
   1427 
   1428 /**
   1429 * get Temporal.PlainDateTime.prototype.daysInYear
   1430 */
   1431 static bool PlainDateTime_daysInYear(JSContext* cx, unsigned argc, Value* vp) {
   1432  // Steps 1-2.
   1433  CallArgs args = CallArgsFromVp(argc, vp);
   1434  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_daysInYear>(cx,
   1435                                                                         args);
   1436 }
   1437 
   1438 /**
   1439 * get Temporal.PlainDateTime.prototype.monthsInYear
   1440 */
   1441 static bool PlainDateTime_monthsInYear(JSContext* cx, const CallArgs& args) {
   1442  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1443  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1444 
   1445  // Step 3.
   1446  return CalendarMonthsInYear(cx, calendar, dateTime->date(), args.rval());
   1447 }
   1448 
   1449 /**
   1450 * get Temporal.PlainDateTime.prototype.monthsInYear
   1451 */
   1452 static bool PlainDateTime_monthsInYear(JSContext* cx, unsigned argc,
   1453                                       Value* vp) {
   1454  // Steps 1-2.
   1455  CallArgs args = CallArgsFromVp(argc, vp);
   1456  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_monthsInYear>(
   1457      cx, args);
   1458 }
   1459 
   1460 /**
   1461 * get Temporal.PlainDateTime.prototype.inLeapYear
   1462 */
   1463 static bool PlainDateTime_inLeapYear(JSContext* cx, const CallArgs& args) {
   1464  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1465  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1466 
   1467  // Step 3.
   1468  return CalendarInLeapYear(cx, calendar, dateTime->date(), args.rval());
   1469 }
   1470 
   1471 /**
   1472 * get Temporal.PlainDateTime.prototype.inLeapYear
   1473 */
   1474 static bool PlainDateTime_inLeapYear(JSContext* cx, unsigned argc, Value* vp) {
   1475  // Steps 1-2.
   1476  CallArgs args = CallArgsFromVp(argc, vp);
   1477  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_inLeapYear>(cx,
   1478                                                                         args);
   1479 }
   1480 
   1481 /**
   1482 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )
   1483 */
   1484 static bool PlainDateTime_with(JSContext* cx, const CallArgs& args) {
   1485  Rooted<PlainDateTime> dateTime(
   1486      cx, &args.thisv().toObject().as<PlainDateTimeObject>());
   1487 
   1488  // Step 3.
   1489  Rooted<JSObject*> temporalDateTimeLike(
   1490      cx, RequireObjectArg(cx, "temporalDateTimeLike", "with", args.get(0)));
   1491  if (!temporalDateTimeLike) {
   1492    return false;
   1493  }
   1494  if (!ThrowIfTemporalLikeObject(cx, temporalDateTimeLike)) {
   1495    return false;
   1496  }
   1497 
   1498  // Step 4.
   1499  auto calendar = dateTime.calendar();
   1500 
   1501  // Step 5.
   1502  Rooted<CalendarFields> fields(cx);
   1503  if (!ISODateToFields(cx, dateTime, &fields)) {
   1504    return false;
   1505  }
   1506 
   1507  // Steps 6-11.
   1508  fields.setHour(dateTime.time().hour);
   1509  fields.setMinute(dateTime.time().minute);
   1510  fields.setSecond(dateTime.time().second);
   1511  fields.setMillisecond(dateTime.time().millisecond);
   1512  fields.setMicrosecond(dateTime.time().microsecond);
   1513  fields.setNanosecond(dateTime.time().nanosecond);
   1514 
   1515  // Step 12.
   1516  Rooted<CalendarFields> partialDateTime(cx);
   1517  if (!PreparePartialCalendarFields(cx, calendar, temporalDateTimeLike,
   1518                                    {
   1519                                        CalendarField::Year,
   1520                                        CalendarField::Month,
   1521                                        CalendarField::MonthCode,
   1522                                        CalendarField::Day,
   1523                                        CalendarField::Hour,
   1524                                        CalendarField::Minute,
   1525                                        CalendarField::Second,
   1526                                        CalendarField::Millisecond,
   1527                                        CalendarField::Microsecond,
   1528                                        CalendarField::Nanosecond,
   1529                                    },
   1530                                    &partialDateTime)) {
   1531    return false;
   1532  }
   1533  MOZ_ASSERT(!partialDateTime.keys().isEmpty());
   1534 
   1535  // Step 13.
   1536  fields = CalendarMergeFields(calendar, fields, partialDateTime);
   1537 
   1538  // Steps 14-15.
   1539  auto overflow = TemporalOverflow::Constrain;
   1540  if (args.hasDefined(1)) {
   1541    // Step 14.
   1542    Rooted<JSObject*> options(cx,
   1543                              RequireObjectArg(cx, "options", "with", args[1]));
   1544    if (!options) {
   1545      return false;
   1546    }
   1547 
   1548    // Step 15.
   1549    if (!GetTemporalOverflowOption(cx, options, &overflow)) {
   1550      return false;
   1551    }
   1552  }
   1553 
   1554  // Step 16.
   1555  ISODateTime result;
   1556  if (!InterpretTemporalDateTimeFields(cx, calendar, fields, overflow,
   1557                                       &result)) {
   1558    return false;
   1559  }
   1560  MOZ_ASSERT(IsValidISODateTime(result));
   1561 
   1562  // Step 21.
   1563  auto* obj = CreateTemporalDateTime(cx, result, calendar);
   1564  if (!obj) {
   1565    return false;
   1566  }
   1567 
   1568  args.rval().setObject(*obj);
   1569  return true;
   1570 }
   1571 
   1572 /**
   1573 * Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )
   1574 */
   1575 static bool PlainDateTime_with(JSContext* cx, unsigned argc, Value* vp) {
   1576  // Steps 1-2.
   1577  CallArgs args = CallArgsFromVp(argc, vp);
   1578  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_with>(cx, args);
   1579 }
   1580 
   1581 /**
   1582 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
   1583 */
   1584 static bool PlainDateTime_withPlainTime(JSContext* cx, const CallArgs& args) {
   1585  auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1586  auto date = temporalDateTime->date();
   1587  Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
   1588 
   1589  // Step 3. (Inlined ToTemporalTimeOrMidnight)
   1590  Time time = {};
   1591  if (args.hasDefined(0)) {
   1592    if (!ToTemporalTime(cx, args[0], &time)) {
   1593      return false;
   1594    }
   1595  }
   1596 
   1597  // Step 4.
   1598  auto isoDateTime = ISODateTime{date, time};
   1599 
   1600  // Step 5.
   1601  auto* obj = CreateTemporalDateTime(cx, isoDateTime, calendar);
   1602  if (!obj) {
   1603    return false;
   1604  }
   1605 
   1606  args.rval().setObject(*obj);
   1607  return true;
   1608 }
   1609 
   1610 /**
   1611 * Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] )
   1612 */
   1613 static bool PlainDateTime_withPlainTime(JSContext* cx, unsigned argc,
   1614                                        Value* vp) {
   1615  // Steps 1-2.
   1616  CallArgs args = CallArgsFromVp(argc, vp);
   1617  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withPlainTime>(
   1618      cx, args);
   1619 }
   1620 
   1621 /**
   1622 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
   1623 */
   1624 static bool PlainDateTime_withCalendar(JSContext* cx, const CallArgs& args) {
   1625  auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1626  auto dateTime = temporalDateTime->dateTime();
   1627 
   1628  // Step 3.
   1629  Rooted<CalendarValue> calendar(cx);
   1630  if (!ToTemporalCalendar(cx, args.get(0), &calendar)) {
   1631    return false;
   1632  }
   1633 
   1634  // Step 4.
   1635  auto* result = CreateTemporalDateTime(cx, dateTime, calendar);
   1636  if (!result) {
   1637    return false;
   1638  }
   1639 
   1640  args.rval().setObject(*result);
   1641  return true;
   1642 }
   1643 
   1644 /**
   1645 * Temporal.PlainDateTime.prototype.withCalendar ( calendar )
   1646 */
   1647 static bool PlainDateTime_withCalendar(JSContext* cx, unsigned argc,
   1648                                       Value* vp) {
   1649  // Steps 1-2.
   1650  CallArgs args = CallArgsFromVp(argc, vp);
   1651  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_withCalendar>(
   1652      cx, args);
   1653 }
   1654 
   1655 /**
   1656 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
   1657 */
   1658 static bool PlainDateTime_add(JSContext* cx, const CallArgs& args) {
   1659  // Step 3.
   1660  return AddDurationToDateTime(cx, TemporalAddDuration::Add, args);
   1661 }
   1662 
   1663 /**
   1664 * Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )
   1665 */
   1666 static bool PlainDateTime_add(JSContext* cx, unsigned argc, Value* vp) {
   1667  // Steps 1-2.
   1668  CallArgs args = CallArgsFromVp(argc, vp);
   1669  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_add>(cx, args);
   1670 }
   1671 
   1672 /**
   1673 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
   1674 * ] )
   1675 */
   1676 static bool PlainDateTime_subtract(JSContext* cx, const CallArgs& args) {
   1677  // Step 3.
   1678  return AddDurationToDateTime(cx, TemporalAddDuration::Subtract, args);
   1679 }
   1680 
   1681 /**
   1682 * Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options
   1683 * ] )
   1684 */
   1685 static bool PlainDateTime_subtract(JSContext* cx, unsigned argc, Value* vp) {
   1686  // Steps 1-2.
   1687  CallArgs args = CallArgsFromVp(argc, vp);
   1688  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_subtract>(cx,
   1689                                                                       args);
   1690 }
   1691 
   1692 /**
   1693 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
   1694 */
   1695 static bool PlainDateTime_until(JSContext* cx, const CallArgs& args) {
   1696  // Step 3.
   1697  return DifferenceTemporalPlainDateTime(cx, TemporalDifference::Until, args);
   1698 }
   1699 
   1700 /**
   1701 * Temporal.PlainDateTime.prototype.until ( other [ , options ] )
   1702 */
   1703 static bool PlainDateTime_until(JSContext* cx, unsigned argc, Value* vp) {
   1704  // Steps 1-2.
   1705  CallArgs args = CallArgsFromVp(argc, vp);
   1706  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_until>(cx, args);
   1707 }
   1708 
   1709 /**
   1710 * Temporal.PlainDateTime.prototype.since ( other [ , options ] )
   1711 */
   1712 static bool PlainDateTime_since(JSContext* cx, const CallArgs& args) {
   1713  // Step 3.
   1714  return DifferenceTemporalPlainDateTime(cx, TemporalDifference::Since, args);
   1715 }
   1716 
   1717 /**
   1718 * Temporal.PlainDateTime.prototype.since ( other [ , options ] )
   1719 */
   1720 static bool PlainDateTime_since(JSContext* cx, unsigned argc, Value* vp) {
   1721  // Steps 1-2.
   1722  CallArgs args = CallArgsFromVp(argc, vp);
   1723  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_since>(cx, args);
   1724 }
   1725 
   1726 /**
   1727 * Temporal.PlainDateTime.prototype.round ( roundTo )
   1728 */
   1729 static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) {
   1730  auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1731  auto dateTime = temporalDateTime->dateTime();
   1732  Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
   1733 
   1734  // Steps 3-13.
   1735  auto smallestUnit = TemporalUnit::Unset;
   1736  auto roundingMode = TemporalRoundingMode::HalfExpand;
   1737  auto roundingIncrement = Increment{1};
   1738  if (args.get(0).isString()) {
   1739    // Step 4. (Not applicable in our implementation.)
   1740 
   1741    // Step 9.
   1742    Rooted<JSString*> paramString(cx, args[0].toString());
   1743    if (!GetTemporalUnitValuedOption(
   1744            cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) {
   1745      return false;
   1746    }
   1747 
   1748    // Step 10.
   1749    if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
   1750                                   smallestUnit, TemporalUnitGroup::DayTime)) {
   1751      return false;
   1752    }
   1753    MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
   1754               smallestUnit <= TemporalUnit::Nanosecond);
   1755 
   1756    // Steps 6-8 and 11-13. (Implicit)
   1757  } else {
   1758    // Steps 3 and 5.
   1759    Rooted<JSObject*> roundTo(
   1760        cx, RequireObjectArg(cx, "roundTo", "round", args.get(0)));
   1761    if (!roundTo) {
   1762      return false;
   1763    }
   1764 
   1765    // Steps 6-7.
   1766    if (!GetRoundingIncrementOption(cx, roundTo, &roundingIncrement)) {
   1767      return false;
   1768    }
   1769 
   1770    // Step 8.
   1771    if (!GetRoundingModeOption(cx, roundTo, &roundingMode)) {
   1772      return false;
   1773    }
   1774 
   1775    // Step 9.
   1776    if (!GetTemporalUnitValuedOption(cx, roundTo, TemporalUnitKey::SmallestUnit,
   1777                                     &smallestUnit)) {
   1778      return false;
   1779    }
   1780 
   1781    if (smallestUnit == TemporalUnit::Unset) {
   1782      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1783                                JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
   1784      return false;
   1785    }
   1786 
   1787    // Step 10.
   1788    if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
   1789                                   smallestUnit, TemporalUnitGroup::DayTime)) {
   1790      return false;
   1791    }
   1792    MOZ_ASSERT(TemporalUnit::Day <= smallestUnit &&
   1793               smallestUnit <= TemporalUnit::Nanosecond);
   1794 
   1795    // Steps 11-12.
   1796    auto maximum = Increment{1};
   1797    bool inclusive = true;
   1798    if (smallestUnit > TemporalUnit::Day) {
   1799      maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit);
   1800      inclusive = false;
   1801    }
   1802 
   1803    // Step 13.
   1804    if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
   1805                                           inclusive)) {
   1806      return false;
   1807    }
   1808  }
   1809 
   1810  // Step 14.
   1811  if (smallestUnit == TemporalUnit::Nanosecond &&
   1812      roundingIncrement == Increment{1}) {
   1813    auto* obj = CreateTemporalDateTime(cx, dateTime, calendar);
   1814    if (!obj) {
   1815      return false;
   1816    }
   1817 
   1818    args.rval().setObject(*obj);
   1819    return true;
   1820  }
   1821 
   1822  // Step 15.
   1823  auto result =
   1824      RoundISODateTime(dateTime, roundingIncrement, smallestUnit, roundingMode);
   1825 
   1826  // Step 16.
   1827  auto* obj = CreateTemporalDateTime(cx, result, calendar);
   1828  if (!obj) {
   1829    return false;
   1830  }
   1831 
   1832  args.rval().setObject(*obj);
   1833  return true;
   1834 }
   1835 
   1836 /**
   1837 * Temporal.PlainDateTime.prototype.round ( roundTo )
   1838 */
   1839 static bool PlainDateTime_round(JSContext* cx, unsigned argc, Value* vp) {
   1840  // Steps 1-2.
   1841  CallArgs args = CallArgsFromVp(argc, vp);
   1842  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_round>(cx, args);
   1843 }
   1844 
   1845 /**
   1846 * Temporal.PlainDateTime.prototype.equals ( other )
   1847 */
   1848 static bool PlainDateTime_equals(JSContext* cx, const CallArgs& args) {
   1849  auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1850  auto dateTime = temporalDateTime->dateTime();
   1851  Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
   1852 
   1853  // Step 3.
   1854  Rooted<PlainDateTime> other(cx);
   1855  if (!ToTemporalDateTime(cx, args.get(0), &other)) {
   1856    return false;
   1857  }
   1858 
   1859  // Steps 4-5.
   1860  bool equals = dateTime == other.dateTime() &&
   1861                CalendarEquals(calendar, other.calendar());
   1862 
   1863  args.rval().setBoolean(equals);
   1864  return true;
   1865 }
   1866 
   1867 /**
   1868 * Temporal.PlainDateTime.prototype.equals ( other )
   1869 */
   1870 static bool PlainDateTime_equals(JSContext* cx, unsigned argc, Value* vp) {
   1871  // Steps 1-2.
   1872  CallArgs args = CallArgsFromVp(argc, vp);
   1873  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_equals>(cx, args);
   1874 }
   1875 
   1876 /**
   1877 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
   1878 */
   1879 static bool PlainDateTime_toString(JSContext* cx, const CallArgs& args) {
   1880  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1881  auto dt = dateTime->dateTime();
   1882  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1883 
   1884  SecondsStringPrecision precision = {Precision::Auto(),
   1885                                      TemporalUnit::Nanosecond, Increment{1}};
   1886  auto roundingMode = TemporalRoundingMode::Trunc;
   1887  auto showCalendar = ShowCalendar::Auto;
   1888  if (args.hasDefined(0)) {
   1889    // Step 3.
   1890    Rooted<JSObject*> options(
   1891        cx, RequireObjectArg(cx, "options", "toString", args[0]));
   1892    if (!options) {
   1893      return false;
   1894    }
   1895 
   1896    // Steps 4-5.
   1897    if (!GetTemporalShowCalendarNameOption(cx, options, &showCalendar)) {
   1898      return false;
   1899    }
   1900 
   1901    // Step 6.
   1902    auto digits = Precision::Auto();
   1903    if (!GetTemporalFractionalSecondDigitsOption(cx, options, &digits)) {
   1904      return false;
   1905    }
   1906 
   1907    // Step 7.
   1908    if (!GetRoundingModeOption(cx, options, &roundingMode)) {
   1909      return false;
   1910    }
   1911 
   1912    // Step 8.
   1913    auto smallestUnit = TemporalUnit::Unset;
   1914    if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
   1915                                     &smallestUnit)) {
   1916      return false;
   1917    }
   1918 
   1919    // Step 9.
   1920    if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
   1921                                   smallestUnit, TemporalUnitGroup::Time)) {
   1922      return false;
   1923    }
   1924 
   1925    // Step 10.
   1926    if (smallestUnit == TemporalUnit::Hour) {
   1927      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1928                                JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
   1929                                "smallestUnit");
   1930      return false;
   1931    }
   1932 
   1933    // Step 11.
   1934    precision = ToSecondsStringPrecision(smallestUnit, digits);
   1935  }
   1936 
   1937  // Step 12.
   1938  auto result =
   1939      RoundISODateTime(dt, precision.increment, precision.unit, roundingMode);
   1940 
   1941  // Step 13.
   1942  if (!ISODateTimeWithinLimits(result)) {
   1943    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1944                              JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
   1945    return false;
   1946  }
   1947 
   1948  // Step 14.
   1949  JSString* str = ISODateTimeToString(cx, result, calendar, precision.precision,
   1950                                      showCalendar);
   1951  if (!str) {
   1952    return false;
   1953  }
   1954 
   1955  args.rval().setString(str);
   1956  return true;
   1957 }
   1958 
   1959 /**
   1960 * Temporal.PlainDateTime.prototype.toString ( [ options ] )
   1961 */
   1962 static bool PlainDateTime_toString(JSContext* cx, unsigned argc, Value* vp) {
   1963  // Steps 1-2.
   1964  CallArgs args = CallArgsFromVp(argc, vp);
   1965  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toString>(cx,
   1966                                                                       args);
   1967 }
   1968 
   1969 /**
   1970 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
   1971 */
   1972 static bool PlainDateTime_toLocaleString(JSContext* cx, const CallArgs& args) {
   1973  // Steps 3-4.
   1974  return intl::TemporalObjectToLocaleString(cx, args,
   1975                                            intl::DateTimeFormatKind::All);
   1976 }
   1977 
   1978 /**
   1979 * Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )
   1980 */
   1981 static bool PlainDateTime_toLocaleString(JSContext* cx, unsigned argc,
   1982                                         Value* vp) {
   1983  // Steps 1-2.
   1984  CallArgs args = CallArgsFromVp(argc, vp);
   1985  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toLocaleString>(
   1986      cx, args);
   1987 }
   1988 
   1989 /**
   1990 * Temporal.PlainDateTime.prototype.toJSON ( )
   1991 */
   1992 static bool PlainDateTime_toJSON(JSContext* cx, const CallArgs& args) {
   1993  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   1994  auto dt = dateTime->dateTime();
   1995  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   1996 
   1997  // Step 3.
   1998  JSString* str = ISODateTimeToString(cx, dt, calendar, Precision::Auto(),
   1999                                      ShowCalendar::Auto);
   2000  if (!str) {
   2001    return false;
   2002  }
   2003 
   2004  args.rval().setString(str);
   2005  return true;
   2006 }
   2007 
   2008 /**
   2009 * Temporal.PlainDateTime.prototype.toJSON ( )
   2010 */
   2011 static bool PlainDateTime_toJSON(JSContext* cx, unsigned argc, Value* vp) {
   2012  // Steps 1-2.
   2013  CallArgs args = CallArgsFromVp(argc, vp);
   2014  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toJSON>(cx, args);
   2015 }
   2016 
   2017 /**
   2018 * Temporal.PlainDateTime.prototype.valueOf ( )
   2019 */
   2020 static bool PlainDateTime_valueOf(JSContext* cx, unsigned argc, Value* vp) {
   2021  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
   2022                            "PlainDateTime", "primitive type");
   2023  return false;
   2024 }
   2025 
   2026 /**
   2027 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
   2028 * options ] )
   2029 */
   2030 static bool PlainDateTime_toZonedDateTime(JSContext* cx, const CallArgs& args) {
   2031  auto* temporalDateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   2032  auto isoDateTime = temporalDateTime->dateTime();
   2033  Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar());
   2034 
   2035  // Step 3.
   2036  Rooted<TimeZoneValue> timeZone(cx);
   2037  if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) {
   2038    return false;
   2039  }
   2040 
   2041  auto disambiguation = TemporalDisambiguation::Compatible;
   2042  if (args.hasDefined(1)) {
   2043    // Step 4.
   2044    Rooted<JSObject*> options(
   2045        cx, RequireObjectArg(cx, "options", "toZonedDateTime", args[1]));
   2046    if (!options) {
   2047      return false;
   2048    }
   2049 
   2050    // Step 5.
   2051    if (!GetTemporalDisambiguationOption(cx, options, &disambiguation)) {
   2052      return false;
   2053    }
   2054  }
   2055 
   2056  // Step 6.
   2057  EpochNanoseconds epochNs;
   2058  if (!GetEpochNanosecondsFor(cx, timeZone, isoDateTime, disambiguation,
   2059                              &epochNs)) {
   2060    return false;
   2061  }
   2062 
   2063  // Step 7.
   2064  auto* result = CreateTemporalZonedDateTime(cx, epochNs, timeZone, calendar);
   2065  if (!result) {
   2066    return false;
   2067  }
   2068 
   2069  args.rval().setObject(*result);
   2070  return true;
   2071 }
   2072 
   2073 /**
   2074 * Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ ,
   2075 * options ] )
   2076 */
   2077 static bool PlainDateTime_toZonedDateTime(JSContext* cx, unsigned argc,
   2078                                          Value* vp) {
   2079  // Steps 1-2.
   2080  CallArgs args = CallArgsFromVp(argc, vp);
   2081  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toZonedDateTime>(
   2082      cx, args);
   2083 }
   2084 
   2085 /**
   2086 * Temporal.PlainDateTime.prototype.toPlainDate ( )
   2087 */
   2088 static bool PlainDateTime_toPlainDate(JSContext* cx, const CallArgs& args) {
   2089  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   2090  Rooted<CalendarValue> calendar(cx, dateTime->calendar());
   2091 
   2092  // Step 3.
   2093  auto* obj = CreateTemporalDate(cx, dateTime->date(), calendar);
   2094  if (!obj) {
   2095    return false;
   2096  }
   2097 
   2098  args.rval().setObject(*obj);
   2099  return true;
   2100 }
   2101 
   2102 /**
   2103 * Temporal.PlainDateTime.prototype.toPlainDate ( )
   2104 */
   2105 static bool PlainDateTime_toPlainDate(JSContext* cx, unsigned argc, Value* vp) {
   2106  // Steps 1-2.
   2107  CallArgs args = CallArgsFromVp(argc, vp);
   2108  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainDate>(cx,
   2109                                                                          args);
   2110 }
   2111 
   2112 /**
   2113 * Temporal.PlainDateTime.prototype.toPlainTime ( )
   2114 */
   2115 static bool PlainDateTime_toPlainTime(JSContext* cx, const CallArgs& args) {
   2116  auto* dateTime = &args.thisv().toObject().as<PlainDateTimeObject>();
   2117 
   2118  // Step 3.
   2119  auto* obj = CreateTemporalTime(cx, dateTime->time());
   2120  if (!obj) {
   2121    return false;
   2122  }
   2123 
   2124  args.rval().setObject(*obj);
   2125  return true;
   2126 }
   2127 
   2128 /**
   2129 * Temporal.PlainDateTime.prototype.toPlainTime ( )
   2130 */
   2131 static bool PlainDateTime_toPlainTime(JSContext* cx, unsigned argc, Value* vp) {
   2132  // Steps 1-2.
   2133  CallArgs args = CallArgsFromVp(argc, vp);
   2134  return CallNonGenericMethod<IsPlainDateTime, PlainDateTime_toPlainTime>(cx,
   2135                                                                          args);
   2136 }
   2137 
   2138 const JSClass PlainDateTimeObject::class_ = {
   2139    "Temporal.PlainDateTime",
   2140    JSCLASS_HAS_RESERVED_SLOTS(PlainDateTimeObject::SLOT_COUNT) |
   2141        JSCLASS_HAS_CACHED_PROTO(JSProto_PlainDateTime),
   2142    JS_NULL_CLASS_OPS,
   2143    &PlainDateTimeObject::classSpec_,
   2144 };
   2145 
   2146 const JSClass& PlainDateTimeObject::protoClass_ = PlainObject::class_;
   2147 
   2148 static const JSFunctionSpec PlainDateTime_methods[] = {
   2149    JS_FN("from", PlainDateTime_from, 1, 0),
   2150    JS_FN("compare", PlainDateTime_compare, 2, 0),
   2151    JS_FS_END,
   2152 };
   2153 
   2154 static const JSFunctionSpec PlainDateTime_prototype_methods[] = {
   2155    JS_FN("with", PlainDateTime_with, 1, 0),
   2156    JS_FN("withPlainTime", PlainDateTime_withPlainTime, 0, 0),
   2157    JS_FN("withCalendar", PlainDateTime_withCalendar, 1, 0),
   2158    JS_FN("add", PlainDateTime_add, 1, 0),
   2159    JS_FN("subtract", PlainDateTime_subtract, 1, 0),
   2160    JS_FN("until", PlainDateTime_until, 1, 0),
   2161    JS_FN("since", PlainDateTime_since, 1, 0),
   2162    JS_FN("round", PlainDateTime_round, 1, 0),
   2163    JS_FN("equals", PlainDateTime_equals, 1, 0),
   2164    JS_FN("toString", PlainDateTime_toString, 0, 0),
   2165    JS_FN("toLocaleString", PlainDateTime_toLocaleString, 0, 0),
   2166    JS_FN("toJSON", PlainDateTime_toJSON, 0, 0),
   2167    JS_FN("valueOf", PlainDateTime_valueOf, 0, 0),
   2168    JS_FN("toZonedDateTime", PlainDateTime_toZonedDateTime, 1, 0),
   2169    JS_FN("toPlainDate", PlainDateTime_toPlainDate, 0, 0),
   2170    JS_FN("toPlainTime", PlainDateTime_toPlainTime, 0, 0),
   2171    JS_FS_END,
   2172 };
   2173 
   2174 static const JSPropertySpec PlainDateTime_prototype_properties[] = {
   2175    JS_PSG("calendarId", PlainDateTime_calendarId, 0),
   2176    JS_PSG("era", PlainDateTime_era, 0),
   2177    JS_PSG("eraYear", PlainDateTime_eraYear, 0),
   2178    JS_PSG("year", PlainDateTime_year, 0),
   2179    JS_PSG("month", PlainDateTime_month, 0),
   2180    JS_PSG("monthCode", PlainDateTime_monthCode, 0),
   2181    JS_PSG("day", PlainDateTime_day, 0),
   2182    JS_PSG("hour", PlainDateTime_hour, 0),
   2183    JS_PSG("minute", PlainDateTime_minute, 0),
   2184    JS_PSG("second", PlainDateTime_second, 0),
   2185    JS_PSG("millisecond", PlainDateTime_millisecond, 0),
   2186    JS_PSG("microsecond", PlainDateTime_microsecond, 0),
   2187    JS_PSG("nanosecond", PlainDateTime_nanosecond, 0),
   2188    JS_PSG("dayOfWeek", PlainDateTime_dayOfWeek, 0),
   2189    JS_PSG("dayOfYear", PlainDateTime_dayOfYear, 0),
   2190    JS_PSG("weekOfYear", PlainDateTime_weekOfYear, 0),
   2191    JS_PSG("yearOfWeek", PlainDateTime_yearOfWeek, 0),
   2192    JS_PSG("daysInWeek", PlainDateTime_daysInWeek, 0),
   2193    JS_PSG("daysInMonth", PlainDateTime_daysInMonth, 0),
   2194    JS_PSG("daysInYear", PlainDateTime_daysInYear, 0),
   2195    JS_PSG("monthsInYear", PlainDateTime_monthsInYear, 0),
   2196    JS_PSG("inLeapYear", PlainDateTime_inLeapYear, 0),
   2197    JS_STRING_SYM_PS(toStringTag, "Temporal.PlainDateTime", JSPROP_READONLY),
   2198    JS_PS_END,
   2199 };
   2200 
   2201 const ClassSpec PlainDateTimeObject::classSpec_ = {
   2202    GenericCreateConstructor<PlainDateTimeConstructor, 3,
   2203                             gc::AllocKind::FUNCTION>,
   2204    GenericCreatePrototype<PlainDateTimeObject>,
   2205    PlainDateTime_methods,
   2206    nullptr,
   2207    PlainDateTime_prototype_methods,
   2208    PlainDateTime_prototype_properties,
   2209    nullptr,
   2210    ClassSpec::DontDefineConstructor,
   2211 };