Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  ZonedDateTime.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "builtin/temporal/ZonedDateTime.h"

#include "mozilla/Assertions.h"
#include "mozilla/EnumSet.h"
#include "mozilla/Maybe.h"

#include <algorithm>
#include <cstdlib>
#include <utility>

#include "jspubtd.h"
#include "NamespaceImports.h"

#include "builtin/intl/DateTimeFormat.h"
#include "builtin/temporal/Calendar.h"
#include "builtin/temporal/CalendarFields.h"
#include "builtin/temporal/Duration.h"
#include "builtin/temporal/Instant.h"
#include "builtin/temporal/Int128.h"
#include "builtin/temporal/PlainDate.h"
#include "builtin/temporal/PlainDateTime.h"
#include "builtin/temporal/PlainMonthDay.h"
#include "builtin/temporal/PlainTime.h"
#include "builtin/temporal/PlainYearMonth.h"
#include "builtin/temporal/Temporal.h"
#include "builtin/temporal/TemporalParser.h"
#include "builtin/temporal/TemporalRoundingMode.h"
#include "builtin/temporal/TemporalTypes.h"
#include "builtin/temporal/TemporalUnit.h"
#include "builtin/temporal/TimeZone.h"
#include "builtin/temporal/ToString.h"
#include "gc/AllocKind.h"
#include "gc/Barrier.h"
#include "gc/GCEnum.h"
#include "js/CallArgs.h"
#include "js/CallNonGenericMethod.h"
#include "js/Class.h"
#include "js/ErrorReport.h"
#include "js/friend/ErrorMessages.h"
#include "js/Printer.h"
#include "js/PropertyDescriptor.h"
#include "js/PropertySpec.h"
#include "js/RootingAPI.h"
#include "js/Value.h"
#include "vm/BigIntType.h"
#include "vm/BytecodeUtil.h"
#include "vm/GlobalObject.h"
#include "vm/JSAtomState.h"
#include "vm/JSContext.h"
#include "vm/JSObject.h"
#include "vm/PlainObject.h"
#include "vm/StringType.h"

#include "vm/JSObject-inl.h"
#include "vm/NativeObject-inl.h"

using namespace js;
using namespace js::temporal;

static inline bool IsZonedDateTime(Handle<Value> v) {
  return v.isObject() && v.toObject().is<ZonedDateTimeObject>();
}

// Returns |RoundNumberToIncrement(offsetNanoseconds, 60 × 10^9, "halfExpand")|.
static int64_t RoundNanosecondsToMinutesIncrement(int64_t offsetNanoseconds) {
  MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));

  constexpr int64_t increment = ToNanoseconds(TemporalUnit::Minute);

  int64_t quotient = offsetNanoseconds / increment;
  int64_t remainder = offsetNanoseconds % increment;
  if (std::abs(remainder * 2) >= increment) {
    quotient += (offsetNanoseconds > 0 ? 1 : -1);
  }
  return quotient * increment;
}

/**
 * InterpretISODateTimeOffset ( isoDate, time, offsetBehaviour,
 * offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour )
 */

bool js::temporal::InterpretISODateTimeOffset(
    JSContext* cx, const ISODateTime& dateTime, OffsetBehaviour offsetBehaviour,
    int64_t offsetNanoseconds, Handle<TimeZoneValue> timeZone,
    TemporalDisambiguation disambiguation, TemporalOffset offsetOption,
    MatchBehaviour matchBehaviour, EpochNanoseconds* result) {
  MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
  MOZ_ASSERT(IsValidISODateTime(dateTime));

  // FIXME: spec issue - avoid calling with date-time outside of limits
  // https://github.com/tc39/proposal-temporal/pull/3014
  if (!ISODateTimeWithinLimits(dateTime)) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
    return false;
  }

  // Steps 1-2. (Not applicable in our implementation.)

  // Step 3.
  if (offsetBehaviour == OffsetBehaviour::Wall ||
      (offsetBehaviour == OffsetBehaviour::Option &&
       offsetOption == TemporalOffset::Ignore)) {
    // Steps 3.a-b.
    return GetEpochNanosecondsFor(cx, timeZone, dateTime, disambiguation,
                                  result);
  }

  // Step 4.
  if (offsetBehaviour == OffsetBehaviour::Exact ||
      (offsetBehaviour == OffsetBehaviour::Option &&
       offsetOption == TemporalOffset::Use)) {
    // Step 4.a.
    auto epochNanoseconds = GetUTCEpochNanoseconds(dateTime) -
                            EpochDuration::fromNanoseconds(offsetNanoseconds);

    // Step 4.b.
    if (!IsValidEpochNanoseconds(epochNanoseconds)) {
      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                JSMSG_TEMPORAL_INSTANT_INVALID);
      return false;
    }

    // Step 4.c.
    *result = epochNanoseconds;
    return true;
  }

  // Step 5.
  MOZ_ASSERT(offsetBehaviour == OffsetBehaviour::Option);

  // Step 6.
  MOZ_ASSERT(offsetOption == TemporalOffset::Prefer ||
             offsetOption == TemporalOffset::Reject);

  // Step 7.
  PossibleEpochNanoseconds possibleEpochNs;
  if (!GetPossibleEpochNanoseconds(cx, timeZone, dateTime, &possibleEpochNs)) {
    return false;
  }

  // Step 8.a.
  for (const auto& candidate : possibleEpochNs) {
    // Step 8.a.i.
    int64_t candidateNanoseconds;
    if (!GetOffsetNanosecondsFor(cx, timeZone, candidate,
                                 &candidateNanoseconds)) {
      return false;
    }
    MOZ_ASSERT(std::abs(candidateNanoseconds) <
               ToNanoseconds(TemporalUnit::Day));

    // Step 8.a.ii.
    if (candidateNanoseconds == offsetNanoseconds) {
      *result = candidate;
      return true;
    }

    // Step 8.a.iii.
    if (matchBehaviour == MatchBehaviour::MatchMinutes) {
      // Step 8.a.iii.1.
      int64_t roundedCandidateNanoseconds =
          RoundNanosecondsToMinutesIncrement(candidateNanoseconds);

      // Step 8.a.iii.2.
      if (roundedCandidateNanoseconds == offsetNanoseconds) {
        // Step 8.a.iii.2.a.
        *result = candidate;
        return true;
      }
    }
  }

  // Step 9.
  if (offsetOption == TemporalOffset::Reject) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_TEMPORAL_ZONED_DATE_TIME_NO_TIME_FOUND);
    return false;
  }

  // Step 10.
  return DisambiguatePossibleEpochNanoseconds(cx, possibleEpochNs, timeZone,
                                              dateTime, disambiguation, result);
}

/**
 * InterpretISODateTimeOffset ( isoDate, time, offsetBehaviour,
 * offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour )
 */

bool js::temporal::InterpretISODateTimeOffset(
    JSContext* cx, const ISODate& isoDate, OffsetBehaviour offsetBehaviour,
    int64_t offsetNanoseconds, Handle<TimeZoneValue> timeZone,
    TemporalDisambiguation disambiguation, TemporalOffset offsetOption,
    MatchBehaviour matchBehaviour, EpochNanoseconds* result) {
  MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));
  MOZ_ASSERT(IsValidISODate(isoDate));

  // Step 1. (Not applicable in our implementation.)

  // Step 2.a.
  MOZ_ASSERT(offsetBehaviour == OffsetBehaviour::Wall);

  // Step 2.b.
  MOZ_ASSERT(offsetNanoseconds == 0);

  // Step 2.c.
  return GetStartOfDay(cx, timeZone, isoDate, result);
}

struct ZonedDateTimeOptions {
  TemporalDisambiguation disambiguation = TemporalDisambiguation::Compatible;
  TemporalOffset offset = TemporalOffset::Reject;
  TemporalOverflow overflow = TemporalOverflow::Constrain;
};

/**
 * ToTemporalZonedDateTime ( item [ , options ] )
 */

static bool ToTemporalZonedDateTimeOptions(JSContext* cx, Handle<Value> options,
                                           ZonedDateTimeOptions* result) {
  if (options.isUndefined()) {
    *result = {};
    return true;
  }

  // NOTE: |options| are only passed from `Temporal.ZonedDateTime.from`.

  Rooted<JSObject*> resolvedOptions(
      cx, RequireObjectArg(cx, "options""from", options));
  if (!resolvedOptions) {
    return false;
  }

  auto disambiguation = TemporalDisambiguation::Compatible;
  if (!GetTemporalDisambiguationOption(cx, resolvedOptions, &disambiguation)) {
    return false;
  }

  auto offset = TemporalOffset::Reject;
  if (!GetTemporalOffsetOption(cx, resolvedOptions, &offset)) {
    return false;
  }

  auto overflow = TemporalOverflow::Constrain;
  if (!GetTemporalOverflowOption(cx, resolvedOptions, &overflow)) {
    return false;
  }

  *result = {disambiguation, offset, overflow};
  return true;
}

/**
 * ToTemporalZonedDateTime ( item [ , options ] )
 */

static bool ToTemporalZonedDateTime(JSContext* cx, Handle<JSObject*> item,
                                    Handle<Value> options,
                                    MutableHandle<ZonedDateTime> result) {
  // Step 1. (Not applicable in our implementation.)

  // Step 2.
  auto offsetBehaviour = OffsetBehaviour::Option;

  // Step 3.
  auto matchBehaviour = MatchBehaviour::MatchExactly;

  // Step 4.a.
  if (auto* zonedDateTime = item->maybeUnwrapIf<ZonedDateTimeObject>()) {
    auto epochNs = zonedDateTime->epochNanoseconds();
    Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
    Rooted<CalendarValue> calendar(cx, zonedDateTime->calendar());

    if (!timeZone.wrap(cx)) {
      return false;
    }
    if (!calendar.wrap(cx)) {
      return false;
    }

    // Steps 4.a.i-v.
    ZonedDateTimeOptions ignoredOptions;
    if (!ToTemporalZonedDateTimeOptions(cx, options, &ignoredOptions)) {
      return false;
    }

    // Step 4.a.vi.
    result.set(ZonedDateTime{epochNs, timeZone, calendar});
    return true;
  }

  // Step 4.b.
  Rooted<CalendarValue> calendar(cx);
  if (!GetTemporalCalendarWithISODefault(cx, item, &calendar)) {
    return false;
  }

  // Step 4.c.
  Rooted<CalendarFields> fields(cx);
  if (!PrepareCalendarFields(cx, calendar, item,
                             {
                                 CalendarField::Year,
                                 CalendarField::Month,
                                 CalendarField::MonthCode,
                                 CalendarField::Day,
                                 CalendarField::Hour,
                                 CalendarField::Minute,
                                 CalendarField::Second,
                                 CalendarField::Millisecond,
                                 CalendarField::Microsecond,
                                 CalendarField::Nanosecond,
                                 CalendarField::Offset,
                                 CalendarField::TimeZone,
                             },
                             {CalendarField::TimeZone}, &fields)) {
    return false;
  }

  // Step 4.d.
  auto timeZone = fields.timeZone();

  // Step 4.e.
  auto offsetString = fields.offset();

  // Step 4.f.
  if (!fields.has(CalendarField::Offset)) {
    offsetBehaviour = OffsetBehaviour::Wall;
  }

  // Steps 4.g-j.
  ZonedDateTimeOptions resolvedOptions;
  if (!ToTemporalZonedDateTimeOptions(cx, options, &resolvedOptions)) {
    return false;
  }
  auto [disambiguation, offsetOption, overflow] = resolvedOptions;

  // Step 4.k.
  ISODateTime dateTime;
  if (!InterpretTemporalDateTimeFields(cx, calendar, fields, overflow,
                                       &dateTime)) {
    return false;
  }

  // Step 6.
  int64_t offsetNanoseconds = 0;

  // Step 7.
  if (offsetBehaviour == OffsetBehaviour::Option) {
    offsetNanoseconds = int64_t(offsetString);
  }

  // Step 8.
  EpochNanoseconds epochNanoseconds;
  if (!InterpretISODateTimeOffset(
          cx, dateTime, offsetBehaviour, offsetNanoseconds, timeZone,
          disambiguation, offsetOption, matchBehaviour, &epochNanoseconds)) {
    return false;
  }
  MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));

  // Step 9.
  result.set(ZonedDateTime{epochNanoseconds, timeZone, calendar});
  return true;
}

/**
 * ToTemporalZonedDateTime ( item [ , options ] )
 */

static bool ToTemporalZonedDateTime(JSContext* cx, Handle<Value> item,
                                    Handle<Value> options,
                                    MutableHandle<ZonedDateTime> result) {
  // Step 1. (Not applicable in our implementation.)

  // Step 2.
  auto offsetBehaviour = OffsetBehaviour::Option;

  // Step 3.
  auto matchBehaviour = MatchBehaviour::MatchExactly;

  // Step 4.
  if (item.isObject()) {
    Rooted<JSObject*> itemObj(cx, &item.toObject());
    return ToTemporalZonedDateTime(cx, itemObj, options, result);
  }

  // Step 5.a.
  if (!item.isString()) {
    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item,
                     nullptr, "not a string");
    return false;
  }
  Rooted<JSString*> string(cx, item.toString());

  // Case 1: 19700101Z[+02:00]
  // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
  //
  // Case 2: 19700101+00:00[+02:00]
  // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "+02:00" }
  //
  // Case 3: 19700101[+02:00]
  // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "+02:00" }
  //
  // Case 4: 19700101Z[Europe/Berlin]
  // { [[Z]]: true, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }
  //
  // Case 5: 19700101+00:00[Europe/Berlin]
  // { [[Z]]: false, [[OffsetString]]: "+00:00", [[Name]]: "Europe/Berlin" }
  //
  // Case 6: 19700101[Europe/Berlin]
  // { [[Z]]: false, [[OffsetString]]: undefined, [[Name]]: "Europe/Berlin" }

  // Steps 5.b-c.
  Rooted<ParsedZonedDateTime> parsed(cx);
  if (!ParseTemporalZonedDateTimeString(cx, string, &parsed)) {
    return false;
  }

  // Step 5.d.
  MOZ_ASSERT(parsed.timeZoneAnnotation());

  // Step 5.e.
  Rooted<TimeZoneValue> timeZone(cx);
  if (!ToTemporalTimeZone(cx, parsed.timeZoneAnnotation(), &timeZone)) {
    return false;
  }

  // Step 5.f. (Not applicable in our implementation.)

  // Step 5.g.
  if (parsed.isUTC()) {
    offsetBehaviour = OffsetBehaviour::Exact;
  }

  // Step 5.h.
  else if (!parsed.hasOffset()) {
    offsetBehaviour = OffsetBehaviour::Wall;
  }

  // Steps 5.i-k.
  Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601));
  if (parsed.calendar()) {
    if (!CanonicalizeCalendar(cx, parsed.calendar(), &calendar)) {
      return false;
    }
  }

  // Step 5.l.
  matchBehaviour = MatchBehaviour::MatchMinutes;

  // Steps 5.m-p.
  ZonedDateTimeOptions resolvedOptions;
  if (!ToTemporalZonedDateTimeOptions(cx, options, &resolvedOptions)) {
    return false;
  }
  auto [disambiguation, offsetOption, overflow] = resolvedOptions;

  // Steps 5.q-r. (Not applicable in our implementation.)

  // Step 6.
  int64_t offsetNanoseconds = 0;

  // Step 7.
  if (offsetBehaviour == OffsetBehaviour::Option) {
    MOZ_ASSERT(parsed.hasOffset());
    offsetNanoseconds = parsed.timeZoneOffset();
  }

  // Step 8.
  EpochNanoseconds epochNanoseconds;
  if (parsed.isStartOfDay()) {
    if (!InterpretISODateTimeOffset(cx, parsed.dateTime().date, offsetBehaviour,
                                    offsetNanoseconds, timeZone, disambiguation,
                                    offsetOption, matchBehaviour,
                                    &epochNanoseconds)) {
      return false;
    }
  } else {
    if (!InterpretISODateTimeOffset(
            cx, parsed.dateTime(), offsetBehaviour, offsetNanoseconds, timeZone,
            disambiguation, offsetOption, matchBehaviour, &epochNanoseconds)) {
      return false;
    }
  }
  MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));

  // Step 9.
  result.set(ZonedDateTime{epochNanoseconds, timeZone, calendar});
  return true;
}

/**
 * ToTemporalZonedDateTime ( item [ , options ] )
 */

static bool ToTemporalZonedDateTime(JSContext* cx, Handle<Value> item,
                                    MutableHandle<ZonedDateTime> result) {
  return ToTemporalZonedDateTime(cx, item, UndefinedHandleValue, result);
}

/**
 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ ,
 * newTarget ] )
 */

static ZonedDateTimeObject* CreateTemporalZonedDateTime(
    JSContext* cx, const CallArgs& args, Handle<BigInt*> epochNanoseconds,
    Handle<TimeZoneValue> timeZone, Handle<CalendarValue> calendar) {
  // Step 1.
  MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));

  // Steps 3-4.
  Rooted<JSObject*> proto(cx);
  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_ZonedDateTime,
                                          &proto)) {
    return nullptr;
  }

  auto* object = NewObjectWithClassProto<ZonedDateTimeObject>(cx, proto);
  if (!object) {
    return nullptr;
  }

  // Step 4.
  auto epochNs = ToEpochNanoseconds(epochNanoseconds);
  object->setFixedSlot(ZonedDateTimeObject::SECONDS_SLOT,
                       NumberValue(epochNs.seconds));
  object->setFixedSlot(ZonedDateTimeObject::NANOSECONDS_SLOT,
                       Int32Value(epochNs.nanoseconds));

  // Step 5.
  object->setFixedSlot(ZonedDateTimeObject::TIMEZONE_SLOT,
                       timeZone.toSlotValue());

  // Step 6.
  object->setFixedSlot(ZonedDateTimeObject::CALENDAR_SLOT,
                       calendar.toSlotValue());

  // Step 7.
  return object;
}

/**
 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ ,
 * newTarget ] )
 */

ZonedDateTimeObject* js::temporal::CreateTemporalZonedDateTime(
    JSContext* cx, const EpochNanoseconds& epochNanoseconds,
    Handle<TimeZoneValue> timeZone, Handle<CalendarValue> calendar) {
  // Step 1.
  MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));

  // Steps 2-3.
  auto* object = NewBuiltinClassInstance<ZonedDateTimeObject>(cx);
  if (!object) {
    return nullptr;
  }

  // Step 4.
  object->setFixedSlot(ZonedDateTimeObject::SECONDS_SLOT,
                       NumberValue(epochNanoseconds.seconds));
  object->setFixedSlot(ZonedDateTimeObject::NANOSECONDS_SLOT,
                       Int32Value(epochNanoseconds.nanoseconds));

  // Step 5.
  object->setFixedSlot(ZonedDateTimeObject::TIMEZONE_SLOT,
                       timeZone.toSlotValue());

  // Step 6.
  object->setFixedSlot(ZonedDateTimeObject::CALENDAR_SLOT,
                       calendar.toSlotValue());

  // Step 7.
  return object;
}

/**
 * CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ ,
 * newTarget ] )
 */

static auto* CreateTemporalZonedDateTime(JSContext* cx,
                                         Handle<ZonedDateTime> zonedDateTime) {
  return CreateTemporalZonedDateTime(cx, zonedDateTime.epochNanoseconds(),
                                     zonedDateTime.timeZone(),
                                     zonedDateTime.calendar());
}

/**
 * AddZonedDateTime ( epochNanoseconds, timeZone, calendar, duration, overflow )
 */

static bool AddZonedDateTime(JSContext* cx, Handle<ZonedDateTime> zonedDateTime,
                             const InternalDuration& duration,
                             TemporalOverflow overflow,
                             EpochNanoseconds* result) {
  MOZ_ASSERT(IsValidDuration(duration));

  // Step 1.
  if (duration.date == DateDuration{}) {
    // Step 1.a.
    return AddInstant(cx, zonedDateTime.epochNanoseconds(), duration.time,
                      result);
  }

  // Step 2.
  ISODateTime isoDateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &isoDateTime)) {
    return false;
  }

  // Step 3.
  ISODate addedDate;
  if (!CalendarDateAdd(cx, zonedDateTime.calendar(), isoDateTime.date,
                       duration.date, overflow, &addedDate)) {
    return false;
  }

  // Step 4.
  auto intermediateDateTime = ISODateTime{addedDate, isoDateTime.time};

  // Step 5.
  if (!ISODateTimeWithinLimits(intermediateDateTime)) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
    return false;
  }

  // Step 6.
  EpochNanoseconds intermediateNs;
  if (!GetEpochNanosecondsFor(
          cx, zonedDateTime.timeZone(), intermediateDateTime,
          TemporalDisambiguation::Compatible, &intermediateNs)) {
    return false;
  }

  // Step 7.
  return AddInstant(cx, intermediateNs, duration.time, result);
}

/**
 * AddZonedDateTime ( epochNanoseconds, timeZone, calendar, duration, overflow )
 */

bool js::temporal::AddZonedDateTime(JSContext* cx,
                                    Handle<ZonedDateTime> zonedDateTime,
                                    const InternalDuration& duration,
                                    EpochNanoseconds* result) {
  return ::AddZonedDateTime(cx, zonedDateTime, duration,
                            TemporalOverflow::Constrain, result);
}

/**
 * DifferenceZonedDateTime ( ns1, ns2, timeZone, calendar, largestUnit )
 */

static bool DifferenceZonedDateTime(JSContext* cx, const EpochNanoseconds& ns1,
                                    const EpochNanoseconds& ns2,
                                    Handle<TimeZoneValue> timeZone,
                                    Handle<CalendarValue> calendar,
                                    TemporalUnit largestUnit,
                                    InternalDuration* result) {
  MOZ_ASSERT(IsValidEpochNanoseconds(ns1));
  MOZ_ASSERT(IsValidEpochNanoseconds(ns2));

  // Steps 1.
  if (ns1 == ns2) {
    *result = InternalDuration{{}, {}};
    return true;
  }

  // Step 2.
  ISODateTime startDateTime;
  if (!GetISODateTimeFor(cx, timeZone, ns1, &startDateTime)) {
    return false;
  }

  // Steps 2-3.
  ISODateTime endDateTime;
  if (!GetISODateTimeFor(cx, timeZone, ns2, &endDateTime)) {
    return false;
  }

  // Step 4.
  int32_t sign = (ns2 - ns1 < EpochDuration{}) ? -1 : 1;

  // Step 5.
  int32_t maxDayCorrection = 1 + (sign > 0);

  // Step 6.
  int32_t dayCorrection = 0;

  // Step 7.
  auto timeDuration = DifferenceTime(startDateTime.time, endDateTime.time);

  // Step 8.
  if (TimeDurationSign(timeDuration) == -sign) {
    dayCorrection += 1;
  }

  // Steps 9-10.
  while (dayCorrection <= maxDayCorrection) {
    // Step 10.a.
    auto intermediateDate =
        BalanceISODate(endDateTime.date, -dayCorrection * sign);

    // Step 10.b.
    auto intermediateDateTime =
        ISODateTime{intermediateDate, startDateTime.time};
    if (!ISODateTimeWithinLimits(intermediateDateTime)) {
      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID);
      return false;
    }

    // Step 10.c.
    EpochNanoseconds intermediateNs;
    if (!GetEpochNanosecondsFor(cx, timeZone, intermediateDateTime,
                                TemporalDisambiguation::Compatible,
                                &intermediateNs)) {
      return false;
    }

    // Step 10.d.
    auto timeDuration =
        TimeDurationFromEpochNanosecondsDifference(ns2, intermediateNs);

    // Step 10.e.
    int32_t timeSign = TimeDurationSign(timeDuration);

    // Step 10.f.
    if (sign != -timeSign) {
      // Step 12.
      auto dateLargestUnit = std::min(largestUnit, TemporalUnit::Day);

      // Step 13.
      DateDuration dateDifference;
      if (!CalendarDateUntil(cx, calendar, startDateTime.date, intermediateDate,
                             dateLargestUnit, &dateDifference)) {
        return false;
      }

      // Step 14.
      MOZ_ASSERT(DateDurationSign(dateDifference) *
                     TimeDurationSign(timeDuration) >=
                 0);
      *result = {dateDifference, timeDuration};
      return true;
    }

    // Step 10.g.
    dayCorrection += 1;
  }

  // Step 11.
  JS_ReportErrorNumberASCII(
      cx, GetErrorMessage, nullptr,
      JSMSG_TEMPORAL_ZONED_DATE_TIME_INCONSISTENT_INSTANT);
  return false;
}

/**
 * DifferenceZonedDateTimeWithRounding ( ns1, ns2, timeZone, calendar,
 * largestUnit, roundingIncrement, smallestUnit, roundingMode )
 */

bool js::temporal::DifferenceZonedDateTimeWithRounding(
    JSContext* cx, JS::Handle<ZonedDateTime> zonedDateTime,
    const EpochNanoseconds& ns2, const DifferenceSettings& settings,
    InternalDuration* result) {
  MOZ_ASSERT(IsValidEpochNanoseconds(ns2));
  MOZ_ASSERT(settings.smallestUnit >= settings.largestUnit);

  const auto& ns1 = zonedDateTime.epochNanoseconds();
  auto timeZone = zonedDateTime.timeZone();
  auto calendar = zonedDateTime.calendar();

  // Step 1.
  if (settings.largestUnit > TemporalUnit::Day) {
    // Step 1.a.
    auto difference =
        DifferenceInstant(ns1, ns2, settings.roundingIncrement,
                          settings.smallestUnit, settings.roundingMode);
    *result = InternalDuration{{}, difference};
    return true;
  }

  // Step 2.
  InternalDuration difference;
  if (!DifferenceZonedDateTime(cx, ns1, ns2, timeZone, calendar,
                               settings.largestUnit, &difference)) {
    return false;
  }

  // Step 3.
  if (settings.smallestUnit == TemporalUnit::Nanosecond &&
      settings.roundingIncrement == Increment{1}) {
    // Step 3.a.
    *result = difference;
    return true;
  }

  // Step 4.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, timeZone, ns1, &dateTime)) {
    return false;
  }

  // Step 5.
  return RoundRelativeDuration(
      cx, difference, ns2, dateTime, timeZone, calendar, settings.largestUnit,
      settings.roundingIncrement, settings.smallestUnit, settings.roundingMode,
      result);
}

/**
 * DifferenceZonedDateTimeWithTotal ( ns1, ns2, timeZone, calendar, unit )
 */

bool js::temporal::DifferenceZonedDateTimeWithTotal(
    JSContext* cx, JS::Handle<ZonedDateTime> zonedDateTime,
    const EpochNanoseconds& ns2, TemporalUnit unit, double* result) {
  MOZ_ASSERT(IsValidEpochNanoseconds(ns2));

  const auto& ns1 = zonedDateTime.epochNanoseconds();
  auto timeZone = zonedDateTime.timeZone();
  auto calendar = zonedDateTime.calendar();

  // Step 1.
  if (unit > TemporalUnit::Day) {
    // Step 1.a.
    auto difference = TimeDurationFromEpochNanosecondsDifference(ns2, ns1);
    MOZ_ASSERT(IsValidEpochDuration(difference.to<EpochDuration>()));

    // Step 1.b.
    *result = TotalTimeDuration(difference, unit);
    return true;
  }

  // Step 2.
  InternalDuration difference;
  if (!DifferenceZonedDateTime(cx, ns1, ns2, timeZone, calendar, unit,
                               &difference)) {
    return false;
  }

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, timeZone, ns1, &dateTime)) {
    return false;
  }

  // Step 5.
  return TotalRelativeDuration(cx, difference, ns2, dateTime, timeZone,
                               calendar, unit, result);
}

/**
 * DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options )
 */

static bool DifferenceTemporalZonedDateTime(JSContext* cx,
                                            TemporalDifference operation,
                                            const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 1.
  Rooted<ZonedDateTime> other(cx);
  if (!ToTemporalZonedDateTime(cx, args.get(0), &other)) {
    return false;
  }

  // Step 2.
  if (!CalendarEquals(zonedDateTime.calendar(), other.calendar())) {
    JS_ReportErrorNumberASCII(
        cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_CALENDAR_INCOMPATIBLE,
        CalendarIdentifier(zonedDateTime.calendar()).data(),
        CalendarIdentifier(other.calendar()).data());
    return false;
  }

  // Steps 3-4.
  DifferenceSettings settings;
  if (args.hasDefined(1)) {
    // Step 3.
    Rooted<JSObject*> options(
        cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
    if (!options) {
      return false;
    }

    // Step 4.
    if (!GetDifferenceSettings(
            cx, operation, options, TemporalUnitGroup::DateTime,
            TemporalUnit::Nanosecond, TemporalUnit::Hour, &settings)) {
      return false;
    }
  } else {
    // Steps 3-4.
    settings = {
        TemporalUnit::Nanosecond,
        TemporalUnit::Hour,
        TemporalRoundingMode::Trunc,
        Increment{1},
    };
  }

  // Step 5.
  if (settings.largestUnit > TemporalUnit::Day) {
    MOZ_ASSERT(settings.smallestUnit >= settings.largestUnit);

    // Step 5.a.
    auto timeDuration =
        DifferenceInstant(zonedDateTime.epochNanoseconds(),
                          other.epochNanoseconds(), settings.roundingIncrement,
                          settings.smallestUnit, settings.roundingMode);

    // Step 5.b.
    Duration result;
    if (!TemporalDurationFromInternal(cx, timeDuration, settings.largestUnit,
                                      &result)) {
      return false;
    }

    // Step 5.c.
    if (operation == TemporalDifference::Since) {
      result = result.negate();
    }

    // Step 5.d.
    auto* obj = CreateTemporalDuration(cx, result);
    if (!obj) {
      return false;
    }

    args.rval().setObject(*obj);
    return true;
  }

  // Steps 6-7.
  if (!TimeZoneEquals(zonedDateTime.timeZone(), other.timeZone())) {
    if (auto one = QuoteString(cx, zonedDateTime.timeZone().identifier())) {
      if (auto two = QuoteString(cx, other.timeZone().identifier())) {
        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                                 JSMSG_TEMPORAL_TIMEZONE_INCOMPATIBLE,
                                 one.get(), two.get());
      }
    }
    return false;
  }

  // Step 8.
  if (zonedDateTime.epochNanoseconds() == other.epochNanoseconds()) {
    auto* obj = CreateTemporalDuration(cx, {});
    if (!obj) {
      return false;
    }

    args.rval().setObject(*obj);
    return true;
  }

  // Step 9.
  InternalDuration internalDuration;
  if (!DifferenceZonedDateTimeWithRounding(cx, zonedDateTime,
                                           other.epochNanoseconds(), settings,
                                           &internalDuration)) {
    return false;
  }
  MOZ_ASSERT(IsValidDuration(internalDuration));

  // Step 10.
  Duration result;
  if (!TemporalDurationFromInternal(cx, internalDuration, TemporalUnit::Hour,
                                    &result)) {
    return false;
  }

  // Step 11.
  if (operation == TemporalDifference::Since) {
    result = result.negate();
  }

  // Step 12.
  auto* obj = CreateTemporalDuration(cx, result);
  if (!obj) {
    return false;
  }

  args.rval().setObject(*obj);
  return true;
}

/**
 * AddDurationToZonedDateTime ( operation, zonedDateTime, temporalDurationLike,
 * options )
 */

static bool AddDurationToZonedDateTime(JSContext* cx,
                                       TemporalAddDuration operation,
                                       const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, &args.thisv().toObject().as<ZonedDateTimeObject>());

  // Step 1.
  Duration duration;
  if (!ToTemporalDuration(cx, args.get(0), &duration)) {
    return false;
  }

  // Step 2.
  if (operation == TemporalAddDuration::Subtract) {
    duration = duration.negate();
  }

  // Steps 3-4.
  auto overflow = TemporalOverflow::Constrain;
  if (args.hasDefined(1)) {
    // Step 3.
    Rooted<JSObject*> options(
        cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
    if (!options) {
      return false;
    }

    // Step 4.
    if (!GetTemporalOverflowOption(cx, options, &overflow)) {
      return false;
    }
  }

  // Step 5.
  auto calendar = zonedDateTime.calendar();

  // Step 6.
  auto timeZone = zonedDateTime.timeZone();

  // Step 7.
  auto internalDuration = ToInternalDurationRecord(duration);

  // Step 8.
  EpochNanoseconds epochNanoseconds;
  if (!::AddZonedDateTime(cx, zonedDateTime, internalDuration, overflow,
                          &epochNanoseconds)) {
    return false;
  }
  MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));

  // Step 9.
  auto* result =
      CreateTemporalZonedDateTime(cx, epochNanoseconds, timeZone, calendar);
  if (!result) {
    return false;
  }

  args.rval().setObject(*result);
  return true;
}

/**
 * FormatUTCOffsetNanoseconds ( offsetNanoseconds )
 */

static JSString* FormatUTCOffsetNanoseconds(JSContext* cx,
                                            int64_t offsetNanoseconds) {
  MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));

  // Step 1.
  char sign = offsetNanoseconds >= 0 ? '+' : '-';

  // Step 2.
  int64_t absoluteNanoseconds = std::abs(offsetNanoseconds);

  // Step 6. (Reordered)
  int32_t subSecondNanoseconds = int32_t(absoluteNanoseconds % 1'000'000'000);

  // Step 5. (Reordered)
  int32_t quotient = int32_t(absoluteNanoseconds / 1'000'000'000);
  int32_t second = quotient % 60;

  // Step 4. (Reordered)
  quotient /= 60;
  int32_t minute = quotient % 60;

  // Step 3.
  int32_t hour = quotient / 60;
  MOZ_ASSERT(hour < 24, "time zone offset mustn't exceed 24-hours");

  // Format: "sign hour{2} : minute{2} : second{2} . fractional{9}"
  constexpr size_t maxLength = 1 + 2 + 1 + 2 + 1 + 2 + 1 + 9;
  char result[maxLength];

  size_t n = 0;

  // Steps 7-8. (Inlined FormatTimeString).
  result[n++] = sign;
  result[n++] = char('0' + (hour / 10));
  result[n++] = char('0' + (hour % 10));
  result[n++] = ':';
  result[n++] = char('0' + (minute / 10));
  result[n++] = char('0' + (minute % 10));

  if (second != 0 || subSecondNanoseconds != 0) {
    result[n++] = ':';
    result[n++] = char('0' + (second / 10));
    result[n++] = char('0' + (second % 10));

    if (uint32_t fractional = subSecondNanoseconds) {
      result[n++] = '.';

      uint32_t k = 100'000'000;
      do {
        result[n++] = char('0' + (fractional / k));
        fractional %= k;
        k /= 10;
      } while (fractional);
    }
  }

  MOZ_ASSERT(n <= maxLength);

  // Step 9.
  return NewStringCopyN<CanGC>(cx, result, n);
}

/**
 * Temporal.ZonedDateTime ( epochNanoseconds, timeZone [ , calendar ] )
 */

static bool ZonedDateTimeConstructor(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // Step 1.
  if (!ThrowIfNotConstructing(cx, args, "Temporal.ZonedDateTime")) {
    return false;
  }

  // Step 2.
  Rooted<BigInt*> epochNanoseconds(cx, js::ToBigInt(cx, args.get(0)));
  if (!epochNanoseconds) {
    return false;
  }

  // Step 3.
  if (!IsValidEpochNanoseconds(epochNanoseconds)) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_TEMPORAL_INSTANT_INVALID);
    return false;
  }

  // Step 4.
  if (!args.get(1).isString()) {
    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, args.get(1),
                     nullptr, "not a string");
    return false;
  }

  // Step 5.
  Rooted<JSString*> timeZoneString(cx, args[1].toString());
  Rooted<ParsedTimeZone> timeZoneParse(cx);
  if (!ParseTimeZoneIdentifier(cx, timeZoneString, &timeZoneParse)) {
    return false;
  }

  // Steps 6-7.
  Rooted<TimeZoneValue> timeZone(cx);
  if (!ToTemporalTimeZone(cx, timeZoneParse, &timeZone)) {
    return false;
  }

  // Steps 8-10.
  Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601));
  if (args.hasDefined(2)) {
    // Step 9.
    if (!args[2].isString()) {
      ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, args[2],
                       nullptr, "not a string");
      return false;
    }

    // Step 10.
    Rooted<JSString*> calendarString(cx, args[2].toString());
    if (!CanonicalizeCalendar(cx, calendarString, &calendar)) {
      return false;
    }
  }

  // Step 11.
  auto* obj = CreateTemporalZonedDateTime(cx, args, epochNanoseconds, timeZone,
                                          calendar);
  if (!obj) {
    return false;
  }

  args.rval().setObject(*obj);
  return true;
}

/**
 * Temporal.ZonedDateTime.from ( item [ , options ] )
 */

static bool ZonedDateTime_from(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // Step 1.
  Rooted<ZonedDateTime> zonedDateTime(cx);
  if (!ToTemporalZonedDateTime(cx, args.get(0), args.get(1), &zonedDateTime)) {
    return false;
  }

  auto* result = CreateTemporalZonedDateTime(cx, zonedDateTime);
  if (!result) {
    return false;
  }

  args.rval().setObject(*result);
  return true;
}

/**
 * Temporal.ZonedDateTime.compare ( one, two )
 */

static bool ZonedDateTime_compare(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // Step 1.
  Rooted<ZonedDateTime> one(cx);
  if (!ToTemporalZonedDateTime(cx, args.get(0), &one)) {
    return false;
  }

  // Step 2.
  Rooted<ZonedDateTime> two(cx);
  if (!ToTemporalZonedDateTime(cx, args.get(1), &two)) {
    return false;
  }

  // Step 3.
  const auto& oneNs = one.epochNanoseconds();
  const auto& twoNs = two.epochNanoseconds();
  args.rval().setInt32(oneNs > twoNs ? 1 : oneNs < twoNs ? -1 : 0);
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.calendarId
 */

static bool ZonedDateTime_calendarId(JSContext* cx, const CallArgs& args) {
  auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();

  // Step 3.
  auto* str =
      NewStringCopy<CanGC>(cx, CalendarIdentifier(zonedDateTime->calendar()));
  if (!str) {
    return false;
  }

  args.rval().setString(str);
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.calendarId
 */

static bool ZonedDateTime_calendarId(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_calendarId>(cx,
                                                                         args);
}

/**
 * get Temporal.ZonedDateTime.prototype.timeZoneId
 */

static bool ZonedDateTime_timeZoneId(JSContext* cx, const CallArgs& args) {
  auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();

  // Step 3.
  args.rval().setString(zonedDateTime->timeZone().identifier());
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.timeZoneId
 */

static bool ZonedDateTime_timeZoneId(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_timeZoneId>(cx,
                                                                         args);
}

/**
 * get Temporal.ZonedDateTime.prototype.era
 */

static bool ZonedDateTime_era(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  return CalendarEra(cx, zonedDateTime.calendar(), dateTime.date, args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.era
 */

static bool ZonedDateTime_era(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_era>(cx, args);
}

/**
 * get Temporal.ZonedDateTime.prototype.eraYear
 */

static bool ZonedDateTime_eraYear(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Steps 4-6.
  return CalendarEraYear(cx, zonedDateTime.calendar(), dateTime.date,
                         args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.eraYear
 */

static bool ZonedDateTime_eraYear(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_eraYear>(cx, args);
}

/**
 * get Temporal.ZonedDateTime.prototype.year
 */

static bool ZonedDateTime_year(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  return CalendarYear(cx, zonedDateTime.calendar(), dateTime.date, args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.year
 */

static bool ZonedDateTime_year(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_year>(cx, args);
}

/**
 * get Temporal.ZonedDateTime.prototype.month
 */

static bool ZonedDateTime_month(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  return CalendarMonth(cx, zonedDateTime.calendar(), dateTime.date,
                       args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.month
 */

static bool ZonedDateTime_month(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_month>(cx, args);
}

/**
 * get Temporal.ZonedDateTime.prototype.monthCode
 */

static bool ZonedDateTime_monthCode(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  return CalendarMonthCode(cx, zonedDateTime.calendar(), dateTime.date,
                           args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.monthCode
 */

static bool ZonedDateTime_monthCode(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_monthCode>(cx,
                                                                        args);
}

/**
 * get Temporal.ZonedDateTime.prototype.day
 */

static bool ZonedDateTime_day(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  return CalendarDay(cx, zonedDateTime.calendar(), dateTime.date, args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.day
 */

static bool ZonedDateTime_day(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_day>(cx, args);
}

/**
 * get Temporal.ZonedDateTime.prototype.hour
 */

static bool ZonedDateTime_hour(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  args.rval().setInt32(dateTime.time.hour);
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.hour
 */

static bool ZonedDateTime_hour(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_hour>(cx, args);
}

/**
 * get Temporal.ZonedDateTime.prototype.minute
 */

static bool ZonedDateTime_minute(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  args.rval().setInt32(dateTime.time.minute);
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.minute
 */

static bool ZonedDateTime_minute(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_minute>(cx, args);
}

/**
 * get Temporal.ZonedDateTime.prototype.second
 */

static bool ZonedDateTime_second(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  args.rval().setInt32(dateTime.time.second);
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.second
 */

static bool ZonedDateTime_second(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_second>(cx, args);
}

/**
 * get Temporal.ZonedDateTime.prototype.millisecond
 */

static bool ZonedDateTime_millisecond(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  args.rval().setInt32(dateTime.time.millisecond);
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.millisecond
 */

static bool ZonedDateTime_millisecond(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_millisecond>(cx,
                                                                          args);
}

/**
 * get Temporal.ZonedDateTime.prototype.microsecond
 */

static bool ZonedDateTime_microsecond(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  args.rval().setInt32(dateTime.time.microsecond);
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.microsecond
 */

static bool ZonedDateTime_microsecond(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_microsecond>(cx,
                                                                          args);
}

/**
 * get Temporal.ZonedDateTime.prototype.nanosecond
 */

static bool ZonedDateTime_nanosecond(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  args.rval().setInt32(dateTime.time.nanosecond);
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.nanosecond
 */

static bool ZonedDateTime_nanosecond(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_nanosecond>(cx,
                                                                         args);
}

/**
 * get Temporal.ZonedDateTime.prototype.epochMilliseconds
 */

static bool ZonedDateTime_epochMilliseconds(JSContext* cx,
                                            const CallArgs& args) {
  auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();

  // Step 3.
  auto epochNs = zonedDateTime->epochNanoseconds();

  // Steps 4-5.
  args.rval().setNumber(epochNs.floorToMilliseconds());
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.epochMilliseconds
 */

static bool ZonedDateTime_epochMilliseconds(JSContext* cx, unsigned argc,
                                            Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_epochMilliseconds>(
      cx, args);
}

/**
 * get Temporal.ZonedDateTime.prototype.epochNanoseconds
 */

static bool ZonedDateTime_epochNanoseconds(JSContext* cx,
                                           const CallArgs& args) {
  auto* zonedDateTime = &args.thisv().toObject().as<ZonedDateTimeObject>();

  // Step 3.
  auto* nanoseconds = ToBigInt(cx, zonedDateTime->epochNanoseconds());
  if (!nanoseconds) {
    return false;
  }

  args.rval().setBigInt(nanoseconds);
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.epochNanoseconds
 */

static bool ZonedDateTime_epochNanoseconds(JSContext* cx, unsigned argc,
                                           Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_epochNanoseconds>(
      cx, args);
}

/**
 * get Temporal.ZonedDateTime.prototype.dayOfWeek
 */

static bool ZonedDateTime_dayOfWeek(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  return CalendarDayOfWeek(cx, zonedDateTime.calendar(), dateTime.date,
                           args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.dayOfWeek
 */

static bool ZonedDateTime_dayOfWeek(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_dayOfWeek>(cx,
                                                                        args);
}

/**
 * get Temporal.ZonedDateTime.prototype.dayOfYear
 */

static bool ZonedDateTime_dayOfYear(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  return CalendarDayOfYear(cx, zonedDateTime.calendar(), dateTime.date,
                           args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.dayOfYear
 */

static bool ZonedDateTime_dayOfYear(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_dayOfYear>(cx,
                                                                        args);
}

/**
 * get Temporal.ZonedDateTime.prototype.weekOfYear
 */

static bool ZonedDateTime_weekOfYear(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Steps 4-6.
  return CalendarWeekOfYear(cx, zonedDateTime.calendar(), dateTime.date,
                            args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.weekOfYear
 */

static bool ZonedDateTime_weekOfYear(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_weekOfYear>(cx,
                                                                         args);
}

/**
 * get Temporal.ZonedDateTime.prototype.yearOfWeek
 */

static bool ZonedDateTime_yearOfWeek(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Steps 4-6.
  return CalendarYearOfWeek(cx, zonedDateTime.calendar(), dateTime.date,
                            args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.yearOfWeek
 */

static bool ZonedDateTime_yearOfWeek(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_yearOfWeek>(cx,
                                                                         args);
}

/**
 * get Temporal.ZonedDateTime.prototype.hoursInDay
 */

static bool ZonedDateTime_hoursInDay(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  auto timeZone = zonedDateTime.timeZone();

  // Step 4.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, timeZone, zonedDateTime.epochNanoseconds(),
                         &dateTime)) {
    return false;
  }

  // Step 5.
  const auto& today = dateTime.date;

  // Step 6.
  auto tomorrow = BalanceISODate(today, 1);
  if (!ISODateWithinLimits(tomorrow)) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_TEMPORAL_PLAIN_DATE_INVALID);
    return false;
  }

  // Step 7.
  EpochNanoseconds todayNs;
  if (!GetStartOfDay(cx, timeZone, today, &todayNs)) {
    return false;
  }

  // Step 8.
  EpochNanoseconds tomorrowNs;
  if (!GetStartOfDay(cx, timeZone, tomorrow, &tomorrowNs)) {
    return false;
  }

  // Step 9.
  auto diff = tomorrowNs - todayNs;
  MOZ_ASSERT(diff.abs() <= EpochDuration::fromDays(2),
             "maximum day length for repeated days doesn't exceed two days");

  static_assert(EpochDuration::fromDays(2).toNanoseconds() < Int128{INT64_MAX},
                "two days in nanoseconds fits into int64_t");

  // Step 10. (Inlined TotalTimeDuration)
  constexpr auto nsPerHour = ToNanoseconds(TemporalUnit::Hour);
  args.rval().setNumber(
      FractionToDouble(int64_t(diff.toNanoseconds()), nsPerHour));
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.hoursInDay
 */

static bool ZonedDateTime_hoursInDay(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_hoursInDay>(cx,
                                                                         args);
}

/**
 * get Temporal.ZonedDateTime.prototype.daysInWeek
 */

static bool ZonedDateTime_daysInWeek(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  return CalendarDaysInWeek(cx, zonedDateTime.calendar(), dateTime.date,
                            args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.daysInWeek
 */

static bool ZonedDateTime_daysInWeek(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInWeek>(cx,
                                                                         args);
}

/**
 * get Temporal.ZonedDateTime.prototype.daysInMonth
 */

static bool ZonedDateTime_daysInMonth(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  return CalendarDaysInMonth(cx, zonedDateTime.calendar(), dateTime.date,
                             args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.daysInMonth
 */

static bool ZonedDateTime_daysInMonth(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInMonth>(cx,
                                                                          args);
}

/**
 * get Temporal.ZonedDateTime.prototype.daysInYear
 */

static bool ZonedDateTime_daysInYear(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  return CalendarDaysInYear(cx, zonedDateTime.calendar(), dateTime.date,
                            args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.daysInYear
 */

static bool ZonedDateTime_daysInYear(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_daysInYear>(cx,
                                                                         args);
}

/**
 * get Temporal.ZonedDateTime.prototype.monthsInYear
 */

static bool ZonedDateTime_monthsInYear(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  return CalendarMonthsInYear(cx, zonedDateTime.calendar(), dateTime.date,
                              args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.monthsInYear
 */

static bool ZonedDateTime_monthsInYear(JSContext* cx, unsigned argc,
                                       Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_monthsInYear>(
      cx, args);
}

/**
 * get Temporal.ZonedDateTime.prototype.inLeapYear
 */

static bool ZonedDateTime_inLeapYear(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  ISODateTime dateTime;
  if (!GetISODateTimeFor(cx, zonedDateTime.timeZone(),
                         zonedDateTime.epochNanoseconds(), &dateTime)) {
    return false;
  }

  // Step 4.
  return CalendarInLeapYear(cx, zonedDateTime.calendar(), dateTime.date,
                            args.rval());
}

/**
 * get Temporal.ZonedDateTime.prototype.inLeapYear
 */

static bool ZonedDateTime_inLeapYear(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_inLeapYear>(cx,
                                                                         args);
}

/**
 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
 */

static bool ZonedDateTime_offsetNanoseconds(JSContext* cx,
                                            const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  int64_t offsetNanoseconds;
  if (!GetOffsetNanosecondsFor(cx, zonedDateTime.timeZone(),
                               zonedDateTime.epochNanoseconds(),
                               &offsetNanoseconds)) {
    return false;
  }
  MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));

  args.rval().setNumber(offsetNanoseconds);
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.offsetNanoseconds
 */

static bool ZonedDateTime_offsetNanoseconds(JSContext* cx, unsigned argc,
                                            Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_offsetNanoseconds>(
      cx, args);
}

/**
 * get Temporal.ZonedDateTime.prototype.offset
 */

static bool ZonedDateTime_offset(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  int64_t offsetNanoseconds;
  if (!GetOffsetNanosecondsFor(cx, zonedDateTime.timeZone(),
                               zonedDateTime.epochNanoseconds(),
                               &offsetNanoseconds)) {
    return false;
  }
  MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day));

  // Step 4.
  JSString* str = FormatUTCOffsetNanoseconds(cx, offsetNanoseconds);
  if (!str) {
    return false;
  }

  args.rval().setString(str);
  return true;
}

/**
 * get Temporal.ZonedDateTime.prototype.offset
 */

static bool ZonedDateTime_offset(JSContext* cx, unsigned argc, Value* vp) {
  // Steps 1-2.
  CallArgs args = CallArgsFromVp(argc, vp);
  return CallNonGenericMethod<IsZonedDateTime, ZonedDateTime_offset>(cx, args);
}

/**
 * Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options
 * ] )
 */

static bool ZonedDateTime_with(JSContext* cx, const CallArgs& args) {
  Rooted<ZonedDateTime> zonedDateTime(
      cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()});

  // Step 3.
  Rooted<JSObject*> temporalZonedDateTimeLike(
      cx,
      RequireObjectArg(cx, "temporalZonedDateTimeLike""with", args.get(0)));
  if (!temporalZonedDateTimeLike) {
    return false;
  }
  if (!ThrowIfTemporalLikeObject(cx, temporalZonedDateTimeLike)) {
    return false;
  }

  // Step 4.
  const auto& epochNs = zonedDateTime.epochNanoseconds();

  // Step 5.
  auto timeZone = zonedDateTime.timeZone();

  // Step 6.
  auto calendar = zonedDateTime.calendar();

  // Step 7.
  int64_t offsetNanoseconds;
  if (!GetOffsetNanosecondsFor(cx, timeZone, epochNs, &offsetNanoseconds)) {
    return false;
  }

  // Step 8.
  auto dateTime = GetISODateTimeFor(epochNs, offsetNanoseconds);
  MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));

  // Step 9.
  Rooted<PlainDate> date(cx, PlainDate{dateTime.date, calendar});
  Rooted<CalendarFields> fields(cx);
  if (!ISODateToFields(cx, date, &fields)) {
    return false;
  }

  // Steps 10-16.
  fields.setHour(dateTime.time.hour);
  fields.setMinute(dateTime.time.minute);
  fields.setSecond(dateTime.time.second);
  fields.setMillisecond(dateTime.time.millisecond);
  fields.setMicrosecond(dateTime.time.microsecond);
  fields.setNanosecond(dateTime.time.nanosecond);
  fields.setOffset(OffsetField{offsetNanoseconds});

  // Step 17.
  Rooted<CalendarFields> partialZonedDateTime(cx);
  if (!PreparePartialCalendarFields(cx, calendar, temporalZonedDateTimeLike,
                                    {
                                        CalendarField::Year,
                                        CalendarField::Month,
                                        CalendarField::MonthCode,
                                        CalendarField::Day,
                                        CalendarField::Hour,
                                        CalendarField::Minute,
                                        CalendarField::Second,
                                        CalendarField::Millisecond,
                                        CalendarField::Microsecond,
                                        CalendarField::Nanosecond,
                                        CalendarField::Offset,
                                    },
                                    &partialZonedDateTime)) {
    return false;
  }
  MOZ_ASSERT(!partialZonedDateTime.keys().isEmpty());

  // Step 18.
  fields = CalendarMergeFields(calendar, fields, partialZonedDateTime);

  // Steps 19-22.
  auto disambiguation = TemporalDisambiguation::Compatible;
  auto offset = TemporalOffset::Prefer;
  auto overflow = TemporalOverflow::Constrain;
  if (args.hasDefined(1)) {
    // Step 19.
    Rooted<JSObject*> options(cx,
                              RequireObjectArg(cx, "options""with", args[1]));
    if (!options) {
      return false;
    }

    // Step 20.
    if (!GetTemporalDisambiguationOption(cx, options, &disambiguation)) {
      return false;
    }

    // Step 21.
    if (!GetTemporalOffsetOption(cx, options, &offset)) {
      return false;
    }

    // Step 22.
    if (!GetTemporalOverflowOption(cx, options, &overflow)) {
      return false;
    }
  }

  // Step 23.
  ISODateTime dateTimeResult;
  if (!InterpretTemporalDateTimeFields(cx, calendar, fields, overflow,
                                       &dateTimeResult)) {
    return false;
  }

  // Step 24.
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=84 H=100 G=92

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge