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


Quelle  jsdate.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/. */


/*
 * JS date methods.
 *
 * "For example, OS/360 devotes 26 bytes of the permanently
 *  resident date-turnover routine to the proper handling of
 *  December 31 on leap years (when it is Day 366).  That
 *  might have been left to the operator."
 *
 * Frederick Brooks, 'The Second-System Effect'.
 */


#include "jsdate.h"

#include "mozilla/Atomics.h"
#include "mozilla/Casting.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/TextUtils.h"

#include <algorithm>
#include <cstring>
#include <iterator>
#include <math.h>
#include <string.h>

#include "jsapi.h"
#include "jsfriendapi.h"
#include "jsnum.h"
#include "jstypes.h"

#ifdef JS_HAS_TEMPORAL_API
#  include "builtin/temporal/Instant.h"
#endif
#include "jit/InlinableNatives.h"
#include "js/CallAndConstruct.h"  // JS::IsCallable
#include "js/Conversions.h"
#include "js/Date.h"
#include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
#include "js/LocaleSensitive.h"
#include "js/Object.h"  // JS::GetBuiltinClass
#include "js/PropertySpec.h"
#include "js/Wrapper.h"
#include "util/DifferentialTesting.h"
#include "util/StringBuilder.h"
#include "util/Text.h"
#include "vm/DateObject.h"
#include "vm/DateTime.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/JSContext.h"
#include "vm/JSObject.h"
#include "vm/StringType.h"
#include "vm/Time.h"

#include "vm/Compartment-inl.h"  // For js::UnwrapAndTypeCheckThis
#include "vm/GeckoProfiler-inl.h"
#include "vm/JSObject-inl.h"

using namespace js;

using mozilla::Atomic;
using mozilla::BitwiseCast;
using mozilla::IsAsciiAlpha;
using mozilla::IsAsciiDigit;
using mozilla::IsAsciiLowercaseAlpha;
using mozilla::NumbersAreIdentical;
using mozilla::Relaxed;

using JS::AutoCheckCannotGC;
using JS::ClippedTime;
using JS::GenericNaN;
using JS::GetBuiltinClass;
using JS::TimeClip;
using JS::ToInteger;

// When this value is non-zero, we'll round the time by this resolution.
static Atomic<uint32_t, Relaxed> sResolutionUsec;
// This is not implemented yet, but we will use this to know to jitter the time
// in the JS shell
static Atomic<bool, Relaxed> sJitter;
// The callback we will use for the Gecko implementation of Timer
// Clamping/Jittering
static Atomic<JS::ReduceMicrosecondTimePrecisionCallback, Relaxed>
    sReduceMicrosecondTimePrecisionCallback;

namespace {

class DateTimeHelper {
 private:
#if !JS_HAS_INTL_API
  static int equivalentYearForDST(int year);
  static bool isRepresentableAsTime32(int64_t t);
  static int32_t daylightSavingTA(DateTimeInfo::ForceUTC forceUTC, int64_t t);
  static int32_t adjustTime(DateTimeInfo::ForceUTC forceUTC, int64_t date);
  static PRMJTime toPRMJTime(DateTimeInfo::ForceUTC forceUTC, int64_t localTime,
                             int64_t utcTime);
#endif

 public:
  static int32_t getTimeZoneOffset(DateTimeInfo::ForceUTC forceUTC,
                                   int64_t epochMilliseconds,
                                   DateTimeInfo::TimeZoneOffset offset);

  static JSString* timeZoneComment(JSContext* cx,
                                   DateTimeInfo::ForceUTC forceUTC,
                                   const char* locale, int64_t utcTime,
                                   int64_t localTime);
#if !JS_HAS_INTL_API
  static size_t formatTime(DateTimeInfo::ForceUTC forceUTC, char* buf,
                           size_t buflen, const char* fmt, int64_t utcTime,
                           int64_t localTime);
#endif
};

}  // namespace

static DateTimeInfo::ForceUTC ForceUTC(const Realm* realm) {
  return realm->creationOptions().forceUTC() ? DateTimeInfo::ForceUTC::Yes
                                             : DateTimeInfo::ForceUTC::No;
}

/**
 * 5.2.5 Mathematical Operations
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static inline double PositiveModulo(double dividend, double divisor) {
  MOZ_ASSERT(divisor > 0);
  MOZ_ASSERT(std::isfinite(divisor));

  double result = fmod(dividend, divisor);
  if (result < 0) {
    result += divisor;
  }
  return result + (+0.0);
}

template <typename T>
static inline std::enable_if_t<std::is_integral_v<T>, int32_t> PositiveModulo(
    T dividend, int32_t divisor) {
  MOZ_ASSERT(divisor > 0);

  int32_t result = dividend % divisor;
  if (result < 0) {
    result += divisor;
  }
  return result;
}

template <typename T>
static constexpr T FloorDiv(T dividend, int32_t divisor) {
  MOZ_ASSERT(divisor > 0);

  T quotient = dividend / divisor;
  T remainder = dividend % divisor;
  if (remainder < 0) {
    quotient -= 1;
  }
  return quotient;
}

#ifdef DEBUG
/**
 * 21.4.1.1 Time Values and Time Range
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static inline bool IsTimeValue(double t) {
  if (std::isnan(t)) {
    return true;
  }
  return IsInteger(t) && StartOfTime <= t && t <= EndOfTime;
}
#endif

/**
 * Time value with local time zone offset applied.
 */

static inline bool IsLocalTimeValue(double t) {
  if (std::isnan(t)) {
    return true;
  }
  return IsInteger(t) && (StartOfTime - msPerDay) < t &&
         t < (EndOfTime + msPerDay);
}

/**
 * 21.4.1.3 Day ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static inline int32_t Day(int64_t t) {
  MOZ_ASSERT(IsLocalTimeValue(t));
  return int32_t(FloorDiv(t, msPerDay));
}

/**
 * 21.4.1.4 TimeWithinDay ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static int32_t TimeWithinDay(int64_t t) {
  MOZ_ASSERT(IsLocalTimeValue(t));
  return PositiveModulo(t, msPerDay);
}

/**
 * 21.4.1.5 DaysInYear ( y )
 * 21.4.1.10 InLeapYear ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static inline bool IsLeapYear(double year) {
  MOZ_ASSERT(IsInteger(year));
  return fmod(year, 4) == 0 && (fmod(year, 100) != 0 || fmod(year, 400) == 0);
}

static constexpr bool IsLeapYear(int32_t year) {
  MOZ_ASSERT(mozilla::Abs(year) <= 2'000'000);

  // From: https://www.youtube.com/watch?v=0s9F4QWAl-E&t=1790s
  int32_t d = (year % 100 != 0) ? 4 : 16;
  return (year & (d - 1)) == 0;
}

/**
 * 21.4.1.6 DayFromYear ( y )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static inline double DayFromYear(double y) {
  // Steps 1-7.
  return 365 * (y - 1970) + floor((y - 1969) / 4.0) -
         floor((y - 1901) / 100.0) + floor((y - 1601) / 400.0);
}

static constexpr int32_t DayFromYear(int32_t y) {
  MOZ_ASSERT(mozilla::Abs(y) <= 2'000'000);

  // Steps 1-7.
  return 365 * (y - 1970) + FloorDiv((y - 1969), 4) -
         FloorDiv((y - 1901), 100) + FloorDiv((y - 1601), 400);
}

/**
 * 21.4.1.7 TimeFromYear ( y )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static inline double TimeFromYear(double y) {
  return ::DayFromYear(y) * msPerDay;
}

static inline int64_t TimeFromYear(int32_t y) {
  return ::DayFromYear(y) * int64_t(msPerDay);
}

/*
 * This function returns the year, month and day corresponding to a given
 * time value. The implementation closely follows (w.r.t. types and variable
 * names) the algorithm shown in Figure 12 of [1].
 *
 * A key point of the algorithm is that it works on the so called
 * Computational calendar where years run from March to February -- this
 * largely avoids complications with leap years. The algorithm finds the
 * date in the Computational calendar and then maps it to the Gregorian
 * calendar.
 *
 * [1] Neri C, Schneider L., "Euclidean affine functions and their
 * application to calendar algorithms."
 * Softw Pract Exper. 2023;53(4):937-970. doi: 10.1002/spe.3172
 * https://onlinelibrary.wiley.com/doi/full/10.1002/spe.3172
 */

YearMonthDay js::ToYearMonthDay(int64_t time) {
  // Calendar cycles repeat every 400 years in the Gregorian calendar: a
  // leap day is added every 4 years, removed every 100 years and added
  // every 400 years. The number of days in 400 years is cycleInDays.
  constexpr uint32_t cycleInYears = 400;
  constexpr uint32_t cycleInDays = cycleInYears * 365 + (cycleInYears / 4) -
                                   (cycleInYears / 100) + (cycleInYears / 400);
  static_assert(cycleInDays == 146097, "Wrong calculation of cycleInDays.");

  // The natural epoch for the Computational calendar is 0000/Mar/01 and
  // there are rataDie1970Jan1 = 719468 days from this date to 1970/Jan/01,
  // the epoch used by ES2024, 21.4.1.1.
  constexpr uint32_t rataDie1970Jan1 = 719468;

  constexpr uint32_t maxU32 = std::numeric_limits<uint32_t>::max();

  // Let N_U be the number of days since the 1970/Jan/01. This function sets
  // N = N_U + K, where K = rataDie1970Jan1 + s * cycleInDays and s is an
  // integer number (to be chosen). Then, it evaluates 4 * N + 3 on uint32_t
  // operands so that N must be positive and, to prevent overflow,
  //   4 * N + 3 <= maxU32 <=> N <= (maxU32 - 3) / 4.
  // Therefore, we must have  0 <= N_U + K <= (maxU32 - 3) / 4 or, in other
  // words, N_U must be in [minDays, maxDays] = [-K, (maxU32 - 3) / 4 - K].
  // Notice that this interval moves cycleInDays positions to the left when
  // s is incremented. We chose s to get the interval's mid-point as close
  // as possible to 0. For this, we wish to have:
  //   K ~= (maxU32 - 3) / 4 - K <=> 2 * K ~= (maxU32 - 3) / 4 <=>
  //   K ~= (maxU32 - 3) / 8 <=>
  //   rataDie1970Jan1 + s * cycleInDays ~= (maxU32 - 3) / 8 <=>
  //   s ~= ((maxU32 - 3) / 8 - rataDie1970Jan1) / cycleInDays ~= 3669.8.
  // Therefore, we chose s = 3670. The shift and correction constants
  // (see [1]) are then:
  constexpr uint32_t s = 3670;
  constexpr uint32_t K = rataDie1970Jan1 + s * cycleInDays;
  constexpr uint32_t L = s * cycleInYears;

  // [minDays, maxDays] correspond to a date range from -1'468'000/Mar/01 to
  // 1'471'805/Jun/05.
  constexpr int32_t minDays = -int32_t(K);
  constexpr int32_t maxDays = (maxU32 - 3) / 4 - K;
  static_assert(minDays == -536'895'458, "Wrong calculation of minDays or K.");
  static_assert(maxDays == 536'846'365, "Wrong calculation of maxDays or K.");

  // These are hard limits for the algorithm and far greater than the
  // range [-8.64e15, 8.64e15] required by ES2024 21.4.1.1. Callers must
  // ensure this function is not called out of the hard limits and,
  // preferably, not outside the ES2024 limits.
  constexpr int64_t minTime = minDays * int64_t(msPerDay);
  [[maybe_unused]] constexpr int64_t maxTime = maxDays * int64_t(msPerDay);
  MOZ_ASSERT(minTime <= time && time <= maxTime);

  // Since time is the number of milliseconds since the epoch, 1970/Jan/01,
  // one might expect N_U = time / uint64_t(msPerDay) is the number of days
  // since epoch. There's a catch tough. Consider, for instance, half day
  // before the epoch, that is, t = -0.5 * msPerDay. This falls on
  // 1969/Dec/31 and should correspond to N_U = -1 but the above gives
  // N_U = 0. Indeed, t / msPerDay = -0.5 but integer division truncates
  // towards 0 (C++ [expr.mul]/4) and not towards -infinity as needed, so
  // that time / uint64_t(msPerDay) = 0. To workaround this issue we perform
  // the division on positive operands so that truncations towards 0 and
  // -infinity are equivalent. For this, set u = time - minTime, which is
  // positive as asserted above. Then, perform the division u / msPerDay and
  // to the result add minTime / msPerDay = minDays to cancel the
  // subtraction of minTime.
  const uint64_t u = uint64_t(time - minTime);
  const int32_t N_U = int32_t(u / uint64_t(msPerDay)) + minDays;
  MOZ_ASSERT(minDays <= N_U && N_U <= maxDays);

  const uint32_t N = uint32_t(N_U) + K;

  // Some magic numbers have been explained above but, unfortunately,
  // others with no precise interpretation do appear. They mostly come
  // from numerical approximations of Euclidean affine functions (see [1])
  // which are faster for the CPU to calculate. Unfortunately, no compiler
  // can do these optimizations.

  // Century C and year of the century N_C:
  const uint32_t N_1 = 4 * N + 3;
  const uint32_t C = N_1 / 146097;
  const uint32_t N_C = N_1 % 146097 / 4;

  // Year of the century Z and day of the year N_Y:
  const uint32_t N_2 = 4 * N_C + 3;
  const uint64_t P_2 = uint64_t(2939745) * N_2;
  const uint32_t Z = uint32_t(P_2 / 4294967296);
  const uint32_t N_Y = uint32_t(P_2 % 4294967296) / 2939745 / 4;

  // Year Y:
  const uint32_t Y = 100 * C + Z;

  // Month M and day D.
  // The expression for N_3 has been adapted to account for the difference
  // between month numbers in ES5 15.9.1.4 (from 0 to 11) and [1] (from 1
  // to 12). This is done by subtracting 65536 from the original
  // expression so that M decreases by 1 and so does M_G further down.
  const uint32_t N_3 = 2141 * N_Y + 132377;  // 132377 = 197913 - 65536
  const uint32_t M = N_3 / 65536;
  const uint32_t D = N_3 % 65536 / 2141;

  // Map from Computational to Gregorian calendar. Notice also the year
  // correction and the type change and that Jan/01 is day 306 of the
  // Computational calendar, cf. Table 1. [1]
  constexpr uint32_t daysFromMar01ToJan01 = 306;
  const uint32_t J = N_Y >= daysFromMar01ToJan01;
  const int32_t Y_G = int32_t((Y - L) + J);
  const int32_t M_G = int32_t(J ? M - 12 : M);
  const int32_t D_G = int32_t(D + 1);

  return {Y_G, M_G, D_G};
}

/**
 * 21.4.1.8 YearFromTime ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static int32_t YearFromTime(int64_t t) {
  MOZ_ASSERT(IsLocalTimeValue(t));
  return ToYearMonthDay(t).year;
}

/**
 * 21.4.1.9 DayWithinYear ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static double DayWithinYear(int64_t t, double year) {
  MOZ_ASSERT(::YearFromTime(t) == year);
  return Day(t) - ::DayFromYear(year);
}

/**
 * 21.4.1.11 MonthFromTime ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static int32_t MonthFromTime(int64_t t) {
  MOZ_ASSERT(IsLocalTimeValue(t));
  return ToYearMonthDay(t).month;
}

/**
 * 21.4.1.12 DateFromTime ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static int32_t DateFromTime(int64_t t) {
  MOZ_ASSERT(IsLocalTimeValue(t));
  return ToYearMonthDay(t).day;
}

/**
 * 21.4.1.13 WeekDay ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static int32_t WeekDay(int64_t t) {
  MOZ_ASSERT(IsLocalTimeValue(t));

  int32_t result = (Day(t) + 4) % 7;
  if (result < 0) {
    result += 7;
  }
  return result;
}

static inline int DayFromMonth(int month, bool isLeapYear) {
  /*
   * The following array contains the day of year for the first day of
   * each month, where index 0 is January, and day 0 is January 1.
   */

  static const int firstDayOfMonth[2][13] = {
      {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
      {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};

  MOZ_ASSERT(0 <= month && month <= 12);
  return firstDayOfMonth[isLeapYear][month];
}

template <typename T>
static inline int DayFromMonth(T month, bool isLeapYear) = delete;

/**
 * 21.4.1.28 MakeDay ( year, month, date )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static double MakeDay(double year, double month, double date) {
  // Step 1.
  if (!std::isfinite(year) || !std::isfinite(month) || !std::isfinite(date)) {
    return GenericNaN();
  }

  // Steps 2-4.
  double y = ToInteger(year);
  double m = ToInteger(month);
  double dt = ToInteger(date);

  static constexpr int32_t maxYears = 1'000'000;
  static constexpr int32_t maxMonths = 1'000'000 * 12;
  static constexpr int32_t maxDate = 100'000'000;

  // Use integer math if possible, because it avoids some notoriously slow
  // functions like `fmod`.
  if (MOZ_LIKELY(std::abs(y) <= maxYears && std::abs(m) <= maxMonths &&
                 std::abs(dt) <= maxDate)) {
    int32_t year = mozilla::AssertedCast<int32_t>(y);
    int32_t month = mozilla::AssertedCast<int32_t>(m);
    int32_t date = mozilla::AssertedCast<int32_t>(dt);

    static_assert(maxMonths % 12 == 0,
                  "maxYearMonths expects maxMonths is divisible by 12");

    static constexpr int32_t maxYearMonths = maxYears + (maxMonths / 12);
    static constexpr int32_t maxYearDay = DayFromYear(maxYearMonths);
    static constexpr int32_t minYearDay = DayFromYear(-maxYearMonths);
    static constexpr int32_t daysInLeapYear = 366;
    static constexpr int32_t maxDay = maxYearDay + daysInLeapYear + maxDate;
    static constexpr int32_t minDay = minYearDay + daysInLeapYear - maxDate;

    static_assert(maxYearMonths == 2'000'000);
    static_assert(maxYearDay == 729'765'472);
    static_assert(minYearDay == -731'204'528);
    static_assert(maxDay == maxYearDay + daysInLeapYear + maxDate);
    static_assert(minDay == minYearDay + daysInLeapYear - maxDate);

    // Step 5.
    int32_t ym = year + FloorDiv(month, 12);
    MOZ_ASSERT(std::abs(ym) <= maxYearMonths);

    // Step 6. (Implicit)

    // Step 7.
    int32_t mn = PositiveModulo(month, 12);

    // Step 8.
    bool leap = IsLeapYear(ym);
    int32_t yearday = ::DayFromYear(ym);
    int32_t monthday = DayFromMonth(mn, leap);
    MOZ_ASSERT(minYearDay <= yearday && yearday <= maxYearDay);

    // Step 9.
    int32_t day = yearday + monthday + date - 1;
    MOZ_ASSERT(minDay <= day && day <= maxDay);
    return day;
  }

  // Step 5.
  double ym = y + floor(m / 12);

  // Step 6.
  if (!std::isfinite(ym)) {
    return GenericNaN();
  }

  // Step 7.
  int mn = int(PositiveModulo(m, 12));

  // Step 8.
  bool leap = IsLeapYear(ym);
  double yearday = floor(TimeFromYear(ym) / msPerDay);
  double monthday = DayFromMonth(mn, leap);

  // Step 9.
  return yearday + monthday + dt - 1;
}

/**
 * 21.4.1.29 MakeDate ( day, time )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static inline double MakeDate(double day, double time) {
  // Step 1.
  if (!std::isfinite(day) || !std::isfinite(time)) {
    return GenericNaN();
  }

  // Steps 2-4.
  return day * msPerDay + time;
}

JS_PUBLIC_API double JS::MakeDate(double year, unsigned month, unsigned day) {
  MOZ_ASSERT(month <= 11);
  MOZ_ASSERT(day >= 1 && day <= 31);

  return ::MakeDate(MakeDay(year, month, day), 0);
}

JS_PUBLIC_API double JS::MakeDate(double year, unsigned month, unsigned day,
                                  double time) {
  MOZ_ASSERT(month <= 11);
  MOZ_ASSERT(day >= 1 && day <= 31);

  return ::MakeDate(MakeDay(year, month, day), time);
}

JS_PUBLIC_API double JS::YearFromTime(double time) {
  const auto clipped = TimeClip(time);
  if (!clipped.isValid()) {
    return GenericNaN();
  }
  int64_t tv;
  MOZ_ALWAYS_TRUE(mozilla::NumberEqualsInt64(clipped.toDouble(), &tv));
  return ::YearFromTime(tv);
}

JS_PUBLIC_API double JS::MonthFromTime(double time) {
  const auto clipped = TimeClip(time);
  if (!clipped.isValid()) {
    return GenericNaN();
  }
  int64_t tv;
  MOZ_ALWAYS_TRUE(mozilla::NumberEqualsInt64(clipped.toDouble(), &tv));
  return ::MonthFromTime(tv);
}

JS_PUBLIC_API double JS::DayFromTime(double time) {
  const auto clipped = TimeClip(time);
  if (!clipped.isValid()) {
    return GenericNaN();
  }
  int64_t tv;
  MOZ_ALWAYS_TRUE(mozilla::NumberEqualsInt64(clipped.toDouble(), &tv));
  return DateFromTime(tv);
}

JS_PUBLIC_API double JS::DayFromYear(double year) {
  return ::DayFromYear(year);
}

JS_PUBLIC_API double JS::DayWithinYear(double time, double year) {
  const auto clipped = TimeClip(time);
  if (!clipped.isValid()) {
    return GenericNaN();
  }
  int64_t tv;
  MOZ_ALWAYS_TRUE(mozilla::NumberEqualsInt64(clipped.toDouble(), &tv));
  return ::DayWithinYear(tv, year);
}

JS_PUBLIC_API void JS::SetReduceMicrosecondTimePrecisionCallback(
    JS::ReduceMicrosecondTimePrecisionCallback callback) {
  sReduceMicrosecondTimePrecisionCallback = callback;
}

JS_PUBLIC_API JS::ReduceMicrosecondTimePrecisionCallback
JS::GetReduceMicrosecondTimePrecisionCallback() {
  return sReduceMicrosecondTimePrecisionCallback;
}

JS_PUBLIC_API void JS::SetTimeResolutionUsec(uint32_t resolution, bool jitter) {
  sResolutionUsec = resolution;
  sJitter = jitter;
}

#if JS_HAS_INTL_API
int32_t DateTimeHelper::getTimeZoneOffset(DateTimeInfo::ForceUTC forceUTC,
                                          int64_t epochMilliseconds,
                                          DateTimeInfo::TimeZoneOffset offset) {
  MOZ_ASSERT_IF(offset == DateTimeInfo::TimeZoneOffset::UTC,
                IsTimeValue(epochMilliseconds));
  MOZ_ASSERT_IF(offset == DateTimeInfo::TimeZoneOffset::Local,
                IsLocalTimeValue(epochMilliseconds));

  return DateTimeInfo::getOffsetMilliseconds(forceUTC, epochMilliseconds,
                                             offset);
}
#else
/*
 * Find a year for which any given date will fall on the same weekday.
 *
 * This function should be used with caution when used other than
 * for determining DST; it hasn't been proven not to produce an
 * incorrect year for times near year boundaries.
 */

int DateTimeHelper::equivalentYearForDST(int year) {
  /*
   * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
   *
   * yearStartingWith[0][i] is an example non-leap year where
   * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
   *
   * yearStartingWith[1][i] is an example leap year where
   * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
   *
   * Keep two different mappings, one for past years (< 1970), and a
   * different one for future years (> 2037).
   */

  static const int pastYearStartingWith[2][7] = {
      {1978, 1973, 1974, 1975, 1981, 1971, 1977},
      {1984, 1996, 1980, 1992, 1976, 1988, 1972}};
  static const int futureYearStartingWith[2][7] = {
      {2034, 2035, 2030, 2031, 2037, 2027, 2033},
      {2012, 2024, 2036, 2020, 2032, 2016, 2028}};

  int day = int(::DayFromYear(year) + 4) % 7;
  if (day < 0) {
    day += 7;
  }

  const auto& yearStartingWith =
      year < 1970 ? pastYearStartingWith : futureYearStartingWith;
  return yearStartingWith[IsLeapYear(year)][day];
}

// Return true if |t| is representable as a 32-bit time_t variable, that means
// the year is in [1970, 2038).
bool DateTimeHelper::isRepresentableAsTime32(int64_t t) {
  return 0 <= t && t < 2145916800000;
}

/* ES5 15.9.1.8. */
int32_t DateTimeHelper::daylightSavingTA(DateTimeInfo::ForceUTC forceUTC,
                                         int64_t t) {
  /*
   * If earlier than 1970 or after 2038, potentially beyond the ken of
   * many OSes, map it to an equivalent year before asking.
   */

  if (!isRepresentableAsTime32(t)) {
    auto [year, month, day] = ToYearMonthDay(t);

    int equivalentYear = equivalentYearForDST(year);
    double equivalentDay = MakeDay(equivalentYear, month, day);
    double equivalentDate = MakeDate(equivalentDay, TimeWithinDay(t));

    MOZ_ALWAYS_TRUE(mozilla::NumberEqualsInt64(equivalentDate, &t));
  }

  return DateTimeInfo::getDSTOffsetMilliseconds(forceUTC, t);
}

int32_t DateTimeHelper::adjustTime(DateTimeInfo::ForceUTC forceUTC,
                                   int64_t date) {
  int32_t localTZA = DateTimeInfo::localTZA(forceUTC);
  int32_t t = daylightSavingTA(forceUTC, date) + localTZA;
  return (localTZA >= 0) ? (t % msPerDay) : -((msPerDay - t) % msPerDay);
}

int32_t DateTimeHelper::getTimeZoneOffset(DateTimeInfo::ForceUTC forceUTC,
                                          int64_t epochMilliseconds,
                                          DateTimeInfo::TimeZoneOffset offset) {
  MOZ_ASSERT_IF(offset == DateTimeInfo::TimeZoneOffset::UTC,
                IsTimeValue(epochMilliseconds));
  MOZ_ASSERT_IF(offset == DateTimeInfo::TimeZoneOffset::Local,
                IsLocalTimeValue(epochMilliseconds));

  if (offset == DateTimeInfo::TimeZoneOffset::UTC) {
    return adjustTime(forceUTC, epochMilliseconds);
  }

  // Following the ES2017 specification creates undesirable results at DST
  // transitions. For example when transitioning from PST to PDT,
  // |new Date(2016,2,13,2,0,0).toTimeString()| returns the string value
  // "01:00:00 GMT-0800 (PST)" instead of "03:00:00 GMT-0700 (PDT)". Follow
  // V8 and subtract one hour before computing the offset.
  // Spec bug: https://bugs.ecmascript.org/show_bug.cgi?id=4007

  return adjustTime(forceUTC, epochMilliseconds -
                                  int64_t(DateTimeInfo::localTZA(forceUTC)) -
                                  int64_t(msPerHour));
}
#endif /* JS_HAS_INTL_API */

/**
 * 21.4.1.25 LocalTime ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static int64_t LocalTime(DateTimeInfo::ForceUTC forceUTC, double t) {
  MOZ_ASSERT(std::isfinite(t));
  MOZ_ASSERT(IsTimeValue(t));

  // Steps 1-4.
  int32_t offsetMs = DateTimeHelper::getTimeZoneOffset(
      forceUTC, static_cast<int64_t>(t), DateTimeInfo::TimeZoneOffset::UTC);
  MOZ_ASSERT(std::abs(offsetMs) < msPerDay);

  // Step 5.
  return static_cast<int64_t>(t) + offsetMs;
}

/**
 * 21.4.1.26 UTC ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static double UTC(DateTimeInfo::ForceUTC forceUTC, double t) {
  // Step 1.
  if (!std::isfinite(t)) {
    return GenericNaN();
  }

  // Return early when |t| is outside the valid local time value limits.
  if (!IsLocalTimeValue(t)) {
    return GenericNaN();
  }

  // Steps 2-5.
  int32_t offsetMs = DateTimeHelper::getTimeZoneOffset(
      forceUTC, static_cast<int64_t>(t), DateTimeInfo::TimeZoneOffset::Local);
  MOZ_ASSERT(std::abs(offsetMs) < msPerDay);

  // Step 6.
  return static_cast<double>(static_cast<int64_t>(t) - offsetMs);
}

/**
 * 21.4.1.14 HourFromTime ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static int32_t HourFromTime(int64_t t) {
  MOZ_ASSERT(IsLocalTimeValue(t));
  return PositiveModulo(FloorDiv(t, msPerHour), HoursPerDay);
}

/**
 * 21.4.1.15 MinFromTime ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static int32_t MinFromTime(int64_t t) {
  MOZ_ASSERT(IsLocalTimeValue(t));
  return PositiveModulo(FloorDiv(t, msPerMinute), MinutesPerHour);
}

/**
 * 21.4.1.16 SecFromTime ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static int32_t SecFromTime(int64_t t) {
  MOZ_ASSERT(IsLocalTimeValue(t));
  return PositiveModulo(FloorDiv(t, msPerSecond), SecondsPerMinute);
}

/**
 * 21.4.1.17 msFromTime ( t )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static int32_t msFromTime(int64_t t) {
  MOZ_ASSERT(IsLocalTimeValue(t));
  return PositiveModulo(t, msPerSecond);
}

HourMinuteSecond js::ToHourMinuteSecond(int64_t epochMilliseconds) {
  MOZ_ASSERT(IsLocalTimeValue(epochMilliseconds));

  int32_t hour = HourFromTime(epochMilliseconds);
  MOZ_ASSERT(0 <= hour && hour < HoursPerDay);

  int32_t minute = MinFromTime(epochMilliseconds);
  MOZ_ASSERT(0 <= minute && minute < MinutesPerHour);

  int32_t second = SecFromTime(epochMilliseconds);
  MOZ_ASSERT(0 <= minute && minute < SecondsPerMinute);

  return {hour, minute, second};
}

/**
 * 21.4.1.27 MakeTime ( hour, min, sec, ms )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static double MakeTime(double hour, double min, double sec, double ms) {
  // Step 1.
  if (!std::isfinite(hour) || !std::isfinite(min) || !std::isfinite(sec) ||
      !std::isfinite(ms)) {
    return GenericNaN();
  }

  // Step 2.
  double h = ToInteger(hour);

  // Step 3.
  double m = ToInteger(min);

  // Step 4.
  double s = ToInteger(sec);

  // Step 5.
  double milli = ToInteger(ms);

  // Step 6.
  return h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
}

/**
 * 21.4.1.30 MakeFullYear ( year )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static double MakeFullYear(double year) {
  // Step 1.
  if (std::isnan(year)) {
    return year;
  }

  // Step 2.
  double truncated = ToInteger(year);

  // Step 3.
  if (0 <= truncated && truncated <= 99) {
    return 1900 + truncated;
  }

  // Step 4.
  return truncated;
}

/**
 * end of ECMA 'support' functions
 */


/**
 * 21.4.3.4 Date.UTC ( year [ , month [ , date [ , hours [ , minutes [ , seconds
 * [ , ms ] ] ] ] ] ] )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static bool date_UTC(JSContext* cx, unsigned argc, Value* vp) {
  AutoJSMethodProfilerEntry pseudoFrame(cx, "Date""UTC");
  CallArgs args = CallArgsFromVp(argc, vp);

  // Step 1.
  double y;
  if (!ToNumber(cx, args.get(0), &y)) {
    return false;
  }

  // Step 2.
  double m;
  if (args.length() >= 2) {
    if (!ToNumber(cx, args[1], &m)) {
      return false;
    }
  } else {
    m = 0;
  }

  // Step 3.
  double dt;
  if (args.length() >= 3) {
    if (!ToNumber(cx, args[2], &dt)) {
      return false;
    }
  } else {
    dt = 1;
  }

  // Step 4.
  double h;
  if (args.length() >= 4) {
    if (!ToNumber(cx, args[3], &h)) {
      return false;
    }
  } else {
    h = 0;
  }

  // Step 5.
  double min;
  if (args.length() >= 5) {
    if (!ToNumber(cx, args[4], &min)) {
      return false;
    }
  } else {
    min = 0;
  }

  // Step 6.
  double s;
  if (args.length() >= 6) {
    if (!ToNumber(cx, args[5], &s)) {
      return false;
    }
  } else {
    s = 0;
  }

  // Step 7.
  double milli;
  if (args.length() >= 7) {
    if (!ToNumber(cx, args[6], &milli)) {
      return false;
    }
  } else {
    milli = 0;
  }

  // Step 8.
  double yr = MakeFullYear(y);

  // Step 9.
  ClippedTime time =
      TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)));
  args.rval().set(TimeValue(time));
  return true;
}

/*
 * Read and convert decimal digits from s[*i] into *result
 * while *i < limit.
 *
 * Succeed if any digits are converted. Advance *i only
 * as digits are consumed.
 */

template <typename CharT>
static bool ParseDigits(size_t* result, const CharT* s, size_t* i,
                        size_t limit) {
  size_t init = *i;
  *result = 0;
  while (*i < limit && ('0' <= s[*i] && s[*i] <= '9')) {
    *result *= 10;
    *result += (s[*i] - '0');
    ++(*i);
  }
  return *i != init;
}

/*
 * Read and convert decimal digits to the right of a decimal point,
 * representing a fractional integer, from s[*i] into *result
 * while *i < limit, up to 3 digits. Consumes any digits beyond 3
 * without affecting the result.
 *
 * Succeed if any digits are converted. Advance *i only
 * as digits are consumed.
 */

template <typename CharT>
static bool ParseFractional(int* result, const CharT* s, size_t* i,
                            size_t limit) {
  int factor = 100;
  size_t init = *i;
  *result = 0;
  for (; *i < limit && ('0' <= s[*i] && s[*i] <= '9'); ++(*i)) {
    if (*i - init >= 3) {
      // If we're past 3 digits, do nothing with it, but continue to
      // consume the remainder of the digits
      continue;
    }
    *result += (s[*i] - '0') * factor;
    factor /= 10;
  }
  return *i != init;
}

/*
 * Read and convert exactly n decimal digits from s[*i]
 * to s[min(*i+n,limit)] into *result.
 *
 * Succeed if exactly n digits are converted. Advance *i only
 * on success.
 */

template <typename CharT>
static bool ParseDigitsN(size_t n, size_t* result, const CharT* s, size_t* i,
                         size_t limit) {
  size_t init = *i;

  if (ParseDigits(result, s, i, std::min(limit, init + n))) {
    return (*i - init) == n;
  }

  *i = init;
  return false;
}

/*
 * Read and convert n or less decimal digits from s[*i]
 * to s[min(*i+n,limit)] into *result.
 *
 * Succeed only if greater than zero but less than or equal to n digits are
 * converted. Advance *i only on success.
 */

template <typename CharT>
static bool ParseDigitsNOrLess(size_t n, size_t* result, const CharT* s,
                               size_t* i, size_t limit) {
  size_t init = *i;

  if (ParseDigits(result, s, i, std::min(limit, init + n))) {
    return ((*i - init) > 0) && ((*i - init) <= n);
  }

  *i = init;
  return false;
}

/*
 * Parse a string according to the formats specified in the standard:
 *
 * https://tc39.es/ecma262/#sec-date-time-string-format
 * https://tc39.es/ecma262/#sec-expanded-years
 *
 * These formats are based upon a simplification of the ISO 8601 Extended
 * Format. As per the spec omitted month and day values are defaulted to '01',
 * omitted HH:mm:ss values are defaulted to '00' and an omitted sss field is
 * defaulted to '000'.
 *
 * For cross compatibility we allow the following extensions.
 *
 * These are:
 *
 *   One or more decimal digits for milliseconds:
 *     The specification requires exactly three decimal digits for
 *     the fractional part but we allow for one or more digits.
 *
 *   Time zone specifier without ':':
 *     We allow the time zone to be specified without a ':' character.
 *     E.g. "T19:00:00+0700" is equivalent to "T19:00:00+07:00".
 *
 * Date part:
 *
 *  Year:
 *     YYYY (eg 1997)
 *
 *  Year and month:
 *     YYYY-MM (eg 1997-07)
 *
 *  Complete date:
 *     YYYY-MM-DD (eg 1997-07-16)
 *
 * Time part:
 *
 *  Hours and minutes:
 *     Thh:mmTZD (eg T19:20+01:00)
 *
 *  Hours, minutes and seconds:
 *     Thh:mm:ssTZD (eg T19:20:30+01:00)
 *
 *  Hours, minutes, seconds and a decimal fraction of a second:
 *     Thh:mm:ss.sssTZD (eg T19:20:30.45+01:00)
 *
 * where:
 *
 *   YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
 *   MM   = two-digit month (01=January, etc.)
 *   DD   = two-digit day of month (01 through 31)
 *   hh   = two digits of hour (00 through 24) (am/pm NOT allowed)
 *   mm   = two digits of minute (00 through 59)
 *   ss   = two digits of second (00 through 59)
 *   sss  = one or more digits representing a decimal fraction of a second
 *   TZD  = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
 */

template <typename CharT>
static bool ParseISOStyleDate(DateTimeInfo::ForceUTC forceUTC, const CharT* s,
                              size_t length, ClippedTime* result) {
  size_t i = 0;
  int tzMul = 1;
  int dateMul = 1;
  size_t year = 1970;
  size_t month = 1;
  size_t day = 1;
  size_t hour = 0;
  size_t min = 0;
  size_t sec = 0;
  int msec = 0;
  bool isLocalTime = false;
  size_t tzHour = 0;
  size_t tzMin = 0;

#define PEEK(ch) (i < length && s[i] == ch)

#define NEED(ch)                   \
  if (i >= length || s[i] != ch) { \
    return false;                  \
  } else {                         \
    ++i;                           \
  }

#define DONE_DATE_UNLESS(ch)       \
  if (i >= length || s[i] != ch) { \
    goto done_date;                \
  } else {                         \
    ++i;                           \
  }

#define DONE_UNLESS(ch)            \
  if (i >= length || s[i] != ch) { \
    goto done;                     \
  } else {                         \
    ++i;                           \
  }

#define NEED_NDIGITS(n, field)                   \
  if (!ParseDigitsN(n, &field, s, &i, length)) { \
    return false;                                \
  }

  if (PEEK('+') || PEEK('-')) {
    if (PEEK('-')) {
      dateMul = -1;
    }
    ++i;
    NEED_NDIGITS(6, year);

    // https://tc39.es/ecma262/#sec-expanded-years
    // -000000 is not a valid expanded year.
    if (year == 0 && dateMul == -1) {
      return false;
    }
  } else {
    NEED_NDIGITS(4, year);
  }
  DONE_DATE_UNLESS('-');
  NEED_NDIGITS(2, month);
  DONE_DATE_UNLESS('-');
  NEED_NDIGITS(2, day);

done_date:
  if (PEEK('T')) {
    ++i;
  } else {
    goto done;
  }

  NEED_NDIGITS(2, hour);
  NEED(':');
  NEED_NDIGITS(2, min);

  if (PEEK(':')) {
    ++i;
    NEED_NDIGITS(2, sec);
    if (PEEK('.')) {
      ++i;
      if (!ParseFractional(&msec, s, &i, length)) {
        return false;
      }
    }
  }

  if (PEEK('Z')) {
    ++i;
  } else if (PEEK('+') || PEEK('-')) {
    if (PEEK('-')) {
      tzMul = -1;
    }
    ++i;
    NEED_NDIGITS(2, tzHour);
    /*
     * Non-standard extension to the ISO date format (permitted by ES5):
     * allow "-0700" as a time zone offset, not just "-07:00".
     */

    if (PEEK(':')) {
      ++i;
    }
    NEED_NDIGITS(2, tzMin);
  } else {
    isLocalTime = true;
  }

done:
  if (year > 275943  // ceil(1e8/365) + 1970
      || month == 0 || month > 12 || day == 0 || day > 31 || hour > 24 ||
      (hour == 24 && (min > 0 || sec > 0 || msec > 0)) || min > 59 ||
      sec > 59 || tzHour > 23 || tzMin > 59) {
    return false;
  }

  if (i != length) {
    return false;
  }

  month -= 1; /* convert month to 0-based */

  double date = MakeDate(MakeDay(dateMul * int32_t(year), month, day),
                         MakeTime(hour, min, sec, msec));

  if (isLocalTime) {
    date = UTC(forceUTC, date);
  } else {
    date -=
        tzMul * (int32_t(tzHour) * msPerHour + int32_t(tzMin) * msPerMinute);
  }

  *result = TimeClip(date);
  return NumbersAreIdentical(date, result->toDouble());

#undef PEEK
#undef NEED
#undef DONE_UNLESS
#undef NEED_NDIGITS
}

/**
 * Non-ISO years < 100 get fixed up, to allow 2-digit year formats.
 * year < 50 becomes 2000-2049, 50-99 becomes 1950-1999.
 */

static int FixupYear(int year) {
  if (year < 50) {
    year += 2000;
  } else if (year >= 50 && year < 100) {
    year += 1900;
  }
  return year;
}

template <typename CharT>
static bool MatchesKeyword(const CharT* s, size_t len, const char* keyword) {
  while (len > 0) {
    MOZ_ASSERT(IsAsciiAlpha(*s));
    MOZ_ASSERT(IsAsciiLowercaseAlpha(*keyword) || *keyword == '\0');

    if (unicode::ToLowerCase(static_cast<Latin1Char>(*s)) != *keyword) {
      return false;
    }

    ++s, ++keyword;
    --len;
  }

  return *keyword == '\0';
}

static constexpr const charconst month_prefixes[] = {
    "jan""feb""mar""apr""may""jun",
    "jul""aug""sep""oct""nov""dec",
};

/**
 * Given a string s of length >= 3, checks if it begins,
 * case-insensitive, with the given lower case prefix.
 */

template <typename CharT>
static bool StartsWithMonthPrefix(const CharT* s, const char* prefix) {
  MOZ_ASSERT(strlen(prefix) == 3);

  for (size_t i = 0; i < 3; ++i) {
    MOZ_ASSERT(IsAsciiAlpha(*s));
    MOZ_ASSERT(IsAsciiLowercaseAlpha(*prefix));

    if (unicode::ToLowerCase(static_cast<Latin1Char>(*s)) != *prefix) {
      return false;
    }

    ++s, ++prefix;
  }

  return true;
}

template <typename CharT>
static bool IsMonthName(const CharT* s, size_t len, int* mon) {
  // Month abbreviations < 3 chars are not accepted.
  if (len < 3) {
    return false;
  }

  for (size_t m = 0; m < std::size(month_prefixes); ++m) {
    if (StartsWithMonthPrefix(s, month_prefixes[m])) {
      // Use numeric value.
      *mon = m + 1;
      return true;
    }
  }

  return false;
}

/*
 * Try to parse the following date formats:
 *   dd-MMM-yyyy
 *   dd-MMM-yy
 *   MMM-dd-yyyy
 *   MMM-dd-yy
 *   yyyy-MMM-dd
 *   yy-MMM-dd
 *
 * Returns true and fills all out parameters when successfully parsed
 * dashed-date.  Otherwise returns false and leaves out parameters untouched.
 */

template <typename CharT>
static bool TryParseDashedDatePrefix(const CharT* s, size_t length,
                                     size_t* indexOut, int* yearOut,
                                     int* monOut, int* mdayOut) {
  size_t i = *indexOut;

  size_t pre = i;
  size_t mday;
  if (!ParseDigitsNOrLess(6, &mday, s, &i, length)) {
    return false;
  }
  size_t mdayDigits = i - pre;

  if (i >= length || s[i] != '-') {
    return false;
  }
  ++i;

  int mon = 0;
  if (*monOut == -1) {
    // If month wasn't already set by ParseDate, it must be in the middle of
    // this format, let's look for it
    size_t start = i;
    for (; i < length; i++) {
      if (!IsAsciiAlpha(s[i])) {
        break;
      }
    }

    if (!IsMonthName(s + start, i - start, &mon)) {
      return false;
    }

    if (i >= length || s[i] != '-') {
      return false;
    }
    ++i;
  }

  pre = i;
  size_t year;
  if (!ParseDigitsNOrLess(6, &year, s, &i, length)) {
    return false;
  }
  size_t yearDigits = i - pre;

  if (i < length && IsAsciiDigit(s[i])) {
    return false;
  }

  // Swap the mday and year if the year wasn't specified in full.
  if (mday > 31 && year <= 31 && yearDigits < 4) {
    std::swap(mday, year);
    std::swap(mdayDigits, yearDigits);
  }

  if (mday > 31 || mdayDigits > 2) {
    return false;
  }

  year = FixupYear(year);

  *indexOut = i;
  *yearOut = year;
  if (*monOut == -1) {
    *monOut = mon;
  }
  *mdayOut = mday;
  return true;
}

/*
 * Try to parse dates in the style of YYYY-MM-DD which do not conform to
 * the formal standard from ParseISOStyleDate. This includes cases such as
 *
 *  - Year does not have 4 digits
 *  - Month or mday has 1 digit
 *  - Space in between date and time, rather than a 'T'
 *
 * Regarding the last case, this function only parses out the date, returning
 * to ParseDate to finish parsing the time and timezone, if present.
 *
 * Returns true and fills all out parameters when successfully parsed
 * dashed-date.  Otherwise returns false and leaves out parameters untouched.
 */

template <typename CharT>
static bool TryParseDashedNumericDatePrefix(const CharT* s, size_t length,
                                            size_t* indexOut, int* yearOut,
                                            int* monOut, int* mdayOut) {
  size_t i = *indexOut;

  size_t first;
  if (!ParseDigitsNOrLess(6, &first, s, &i, length)) {
    return false;
  }

  if (i >= length || s[i] != '-') {
    return false;
  }
  ++i;

  size_t second;
  if (!ParseDigitsNOrLess(2, &second, s, &i, length)) {
    return false;
  }

  if (i >= length || s[i] != '-') {
    return false;
  }
  ++i;

  size_t third;
  if (!ParseDigitsNOrLess(6, &third, s, &i, length)) {
    return false;
  }

  int year;
  int mon = -1;
  int mday = -1;

  // 1 or 2 digits for the first number is tricky; 1-12 means it's a month, 0 or
  // >31 means it's a year, and 13-31 is invalid due to ambiguity.
  if (first >= 1 && first <= 12) {
    mon = first;
  } else if (first == 0 || first > 31) {
    year = first;
  } else {
    return false;
  }

  if (mon < 0) {
    // If month hasn't been set yet, it's definitely the 2nd number
    mon = second;
  } else {
    // If it has, the next number is the mday
    mday = second;
  }

  if (mday < 0) {
    // The third number is probably the mday...
    mday = third;
  } else {
    // But otherwise, it's the year
    year = third;
  }

  if (mon < 1 || mon > 12 || mday < 1 || mday > 31) {
    return false;
  }

  year = FixupYear(year);

  *indexOut = i;
  *yearOut = year;
  *monOut = mon;
  *mdayOut = mday;
  return true;
}

struct CharsAndAction {
  const char* chars;
  int action;
};

static constexpr CharsAndAction keywords[] = {
    // clang-format off
  // AM/PM
  { "am", -1 },
  { "pm", -2 },
  // Time zone abbreviations.
  { "gmt", 10000 + 0 },
  { "z", 10000 + 0 },
  { "ut", 10000 + 0 },
  { "utc", 10000 + 0 },
  { "est", 10000 + 5 * 60 },
  { "edt", 10000 + 4 * 60 },
  { "cst", 10000 + 6 * 60 },
  { "cdt", 10000 + 5 * 60 },
  { "mst", 10000 + 7 * 60 },
  { "mdt", 10000 + 6 * 60 },
  { "pst", 10000 + 8 * 60 },
  { "pdt", 10000 + 7 * 60 },
    // clang-format on
};

template <size_t N>
static constexpr size_t MinKeywordLength(const CharsAndAction (&keywords)[N]) {
  size_t min = size_t(-1);
  for (const CharsAndAction& keyword : keywords) {
    min = std::min(min, std::char_traits<char>::length(keyword.chars));
  }
  return min;
}

template <typename CharT>
static bool ParseDate(JSContext* cx, DateTimeInfo::ForceUTC forceUTC,
                      const CharT* s, size_t length, ClippedTime* result) {
  if (length == 0) {
    return false;
  }

  if (ParseISOStyleDate(forceUTC, s, length, result)) {
    return true;
  }

  // Collect telemetry on how often Date.parse enters implementation defined
  // code. This can be removed in the future, see Bug 1944630.
  cx->runtime()->setUseCounter(cx->global(), JSUseCounter::DATEPARSE_IMPL_DEF);

  size_t index = 0;
  int mon = -1;
  bool seenMonthName = false;

  // Before we begin, we need to scrub any words from the beginning of the
  // string up to the first number, recording the month if we encounter it
  for (; index < length; index++) {
    int c = s[index];

    if (strchr(" ,.-/", c)) {
      continue;
    }
    if (!IsAsciiAlpha(c)) {
      break;
    }

    size_t start = index;
    index++;
    for (; index < length; index++) {
      if (!IsAsciiAlpha(s[index])) {
        break;
      }
    }

    if (index >= length) {
      return false;
    }

    if (IsMonthName(s + start, index - start, &mon)) {
      seenMonthName = true;
      // If the next digit is a number, we need to break so it
      // gets parsed as mday
      if (IsAsciiDigit(s[index])) {
        break;
      }
    } else if (!strchr(" ,.-/", s[index])) {
      // We're only allowing the above delimiters after the day of
      // week to prevent things such as "foo_1" from being parsed
      // as a date, which may break software which uses this function
      // to determine whether or not something is a date.
      return false;
    }
  }

  int year = -1;
  int mday = -1;
  int hour = -1;
  int min = -1;
  int sec = -1;
  int msec = 0;
  int tzOffset = -1;

  // One of '+', '-', ':', '/', or 0 (the default value).
  int prevc = 0;

  bool seenPlusMinus = false;
  bool seenFullYear = false;
  bool negativeYear = false;
  // Includes "GMT", "UTC", "UT", and "Z" timezone keywords
  bool seenGmtAbbr = false;

  // Try parsing the leading dashed-date.
  //
  // If successfully parsed, index is updated to the end of the date part,
  // and year, mon, mday are set to the date.
  // Continue parsing optional time + tzOffset parts.
  //
  // Otherwise, this is no-op.
  bool isDashedDate =
      TryParseDashedDatePrefix(s, length, &index, &year, &mon, &mday) ||
      TryParseDashedNumericDatePrefix(s, length, &index, &year, &mon, &mday);

  if (isDashedDate && index < length && strchr("T:+", s[index])) {
    return false;
  }

  while (index < length) {
    int c = s[index];
    index++;

    // Normalize U+202F (NARROW NO-BREAK SPACE). This character appears between
    // the AM/PM markers for |date.toLocaleString("en")|. We have to normalize
    // it for backward compatibility reasons.
    if (c == 0x202F) {
      c = ' ';
    }

    if ((c == '+' || c == '-') &&
        // Reject + or - after timezone (still allowing for negative year)
        ((seenPlusMinus && year != -1) ||
         // Reject timezones like "1995-09-26 -04:30" (if the - is right up
         // against the previous number, it will get parsed as a time,
         // see the other comment below)
         (year != -1 && hour == -1 && !seenGmtAbbr &&
          !IsAsciiDigit(s[index - 2])))) {
      return false;
    }

    // Spaces, ASCII control characters, periods, and commas are simply ignored.
    if (c <= ' ' || c == '.' || c == ',') {
      continue;
    }

    // Parse delimiter characters.  Save them to the side for future use.
    if (c == '/' || c == ':' || c == '+') {
      prevc = c;
      continue;
    }

    // Dashes are delimiters if they're immediately followed by a number field.
    // If they're not followed by a number field, they're simply ignored.
    if (c == '-') {
      if (index < length && IsAsciiDigit(s[index])) {
        prevc = c;
      }
      continue;
    }

    // Skip over comments -- text inside matching parentheses.  (Comments
    // themselves may contain comments as long as all the parentheses properly
    // match up.  And apparently comments, including nested ones, may validly be
    // terminated by end of input...)
    if (c == '(') {
      int depth = 1;
      while (index < length) {
        c = s[index];
        index++;
        if (c == '(') {
          depth++;
        } else if (c == ')') {
          if (--depth <= 0) {
            break;
          }
        }
      }
      continue;
    }

    // Parse a number field.
    if (IsAsciiDigit(c)) {
      size_t partStart = index - 1;
      uint32_t u = c - '0';
      while (index < length) {
        c = s[index];
        if (!IsAsciiDigit(c)) {
          break;
        }
        u = u * 10 + (c - '0');
        index++;
      }
      size_t partLength = index - partStart;

      // See above for why we have to normalize U+202F.
      if (c == 0x202F) {
        c = ' ';
      }

      int n = int(u);

      /*
       * Allow TZA before the year, so 'Wed Nov 05 21:49:11 GMT-0800 1997'
       * works.
       *
       * Uses of seenPlusMinus allow ':' in TZA, so Java no-timezone style
       * of GMT+4:30 works.
       */


      if (prevc == '-' && (tzOffset != 0 || seenPlusMinus) && partLength >= 4 &&
          year < 0) {
        // Parse as a negative, possibly zero-padded year if
        // 1. the preceding character is '-',
        // 2. the TZA is not 'GMT' (tested by |tzOffset != 0|),
        // 3. or a TZA was already parsed |seenPlusMinus == true|,
        // 4. the part length is at least 4 (to parse '-08' as a TZA),
        // 5. and we did not already parse a year |year < 0|.
        year = n;
        seenFullYear = true;
        negativeYear = true;
      } else if ((prevc == '+' || prevc == '-') &&
                 // "1995-09-26-04:30" needs to be parsed as a time,
                 // not a time zone
                 (seenGmtAbbr || hour != -1)) {
        /* Make ':' case below change tzOffset. */
        seenPlusMinus = true;

        /* offset */
        if (n < 24 && partLength <= 2) {
          n = n * 60; /* EG. "GMT-3" */
        } else {
          n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
        }

        if (prevc == '+'/* plus means east of GMT */
          n = -n;

        // Reject if not preceded by 'GMT' or if a time zone offset
        // was already parsed.
        if (tzOffset != 0 && tzOffset != -1) {
          return false;
        }

        tzOffset = n;
      } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
        if (c <= ' ' || c == ',' || c == '/' || index >= length) {
          year = n;
        } else {
          return false;
        }
      } else if (c == ':') {
        if (hour < 0) {
          hour = /*byte*/ n;
        } else if (min < 0) {
          min = /*byte*/ n;
        } else {
          return false;
        }
      } else if (c == '/') {
        /*
         * Until it is determined that mon is the actual month, keep
         * it as 1-based rather than 0-based.
         */

        if (mon < 0) {
          mon = /*byte*/ n;
        } else if (mday < 0) {
          mday = /*byte*/ n;
        } else {
          return false;
        }
      } else if (index < length && c != ',' && c > ' ' && c != '-' &&
                 c != '(' &&
                 // Allow '.' as a delimiter until seconds have been parsed
                 // (this allows the decimal for milliseconds)
                 (c != '.' || sec != -1) &&
                 // Allow zulu time e.g. "09/26/1995 16:00Z", or
                 // '+' directly after time e.g. 00:00+0500
                 !(hour != -1 && strchr("Zz+", c)) &&
                 // Allow month or AM/PM directly after a number
                 (!IsAsciiAlpha(c) ||
                  (mon != -1 && !(strchr("AaPp", c) && index < length - 1 &&
                                  strchr("Mm", s[index + 1]))))) {
        return false;
      } else if (seenPlusMinus && n < 60) { /* handle GMT-3:30 */
        if (tzOffset < 0) {
          tzOffset -= n;
        } else {
          tzOffset += n;
        }
      } else if (hour >= 0 && min < 0) {
        min = /*byte*/ n;
      } else if (prevc == ':' && min >= 0 && sec < 0) {
        sec = /*byte*/ n;
        if (c == '.') {
          index++;
          if (!ParseFractional(&msec, s, &index, length)) {
            return false;
          }
        }
      } else if (mon < 0) {
        mon = /*byte*/ n;
      } else if (mon >= 0 && mday < 0) {
        mday = /*byte*/ n;
      } else if (mon >= 0 && mday >= 0 && year < 0) {
        year = n;
        seenFullYear = partLength >= 4;
      } else {
        return false;
      }

      prevc = 0;
      continue;
    }

    // Parse fields that are words: ASCII letters spelling out in English AM/PM,
    // day of week, month, or an extremely limited set of legacy time zone
    // abbreviations.
    if (IsAsciiAlpha(c)) {
      size_t start = index - 1;
      while (index < length) {
        c = s[index];
        if (!IsAsciiAlpha(c)) {
          break;
        }
        index++;
      }

      // There must be at least as many letters as in the shortest keyword.
      constexpr size_t MinLength = MinKeywordLength(keywords);
      if (index - start < MinLength) {
        return false;
      }

      // Record a month if it is a month name. Note that some numbers are
      // initially treated as months; if a numeric field has already been
      // interpreted as a month, store that value to the actually appropriate
      // date component and set the month here.
      int tryMonth;
      if (IsMonthName(s + start, index - start, &tryMonth)) {
        if (seenMonthName) {
          // Overwrite the previous month name
          mon = tryMonth;
          prevc = 0;
          continue;
        }

        seenMonthName = true;

        if (mon < 0) {
          mon = tryMonth;
        } else if (mday < 0) {
          mday = mon;
          mon = tryMonth;
        } else if (year < 0) {
          if (mday > 0) {
            // If the date is of the form f l month, then when month is
            // reached we have f in mon and l in mday. In order to be
            // consistent with the f month l and month f l forms, we need to
            // swap so that f is in mday and l is in year.
            year = mday;
            mday = mon;
          } else {
            year = mon;
          }
          mon = tryMonth;
        } else {
          return false;
        }

        prevc = 0;
        continue;
      }

      size_t k = std::size(keywords);
      while (k-- > 0) {
        const CharsAndAction& keyword = keywords[k];

        // If the field doesn't match the keyword, try the next one.
        if (!MatchesKeyword(s + start, index - start, keyword.chars)) {
          continue;
        }

        int action = keyword.action;

        if (action == 10000) {
          seenGmtAbbr = true;
        }

        // Perform action tests from smallest action values to largest.

        // Adjust a previously-specified hour for AM/PM accordingly (taking care
        // to treat 12:xx AM as 00:xx, 12:xx PM as 12:xx).
        if (action < 0) {
          MOZ_ASSERT(action == -1 || action == -2);
          if (hour > 12 || hour < 0) {
            return false;
          }

          if (action == -1 && hour == 12) {
            hour = 0;
          } else if (action == -2 && hour != 12) {
            hour += 12;
          }

          break;
        }

        // Finally, record a time zone offset.
        MOZ_ASSERT(action >= 10000);
        tzOffset = action - 10000;
        break;
      }

      if (k == size_t(-1)) {
        return false;
      }

      prevc = 0;
      continue;
    }

    // Any other character fails to parse.
    return false;
  }

  // Handle cases where the input is a single number. Single numbers >= 1000
  // are handled by the spec (ParseISOStyleDate), so we don't need to account
  // for that here.
  if (mon != -1 && year < 0 && mday < 0) {
    // Reject 13-31 for Chrome parity
    if (mon >= 13 && mon <= 31) {
      return false;
    }

    mday = 1;
    if (mon >= 1 && mon <= 12) {
      // 1-12 is parsed as a month with the year defaulted to 2001
      // (again, for Chrome parity)
      year = 2001;
    } else {
      year = FixupYear(mon);
      mon = 1;
    }
  }

  if (year < 0 || mon < 0 || mday < 0) {
    return false;
  }

  if (!isDashedDate) {
    // NOTE: TryParseDashedDatePrefix already handles the following fixup.

    /*
     * Case 1. The input string contains an English month name.
     *         The form of the string can be month f l, or f month l, or
     *         f l month which each evaluate to the same date.
     *         If f and l are both greater than or equal to 100 the date
     *         is invalid.
     *
     *         The year is taken to be either l, f if f > 31, or whichever
     *         is set to zero.
     *
     * Case 2. The input string is of the form "f/m/l" where f, m and l are
     *         integers, e.g. 7/16/45. mon, mday and year values are adjusted
     *         to achieve Chrome compatibility.
     *
     *         a. If 0 < f <= 12 and 0 < l <= 31, f/m/l is interpreted as
     *         month/day/year.
     *         b. If 31 < f and 0 < m <= 12 and 0 < l <= 31 f/m/l is
     *         interpreted as year/month/day
     */

    if (seenMonthName) {
      if (mday >= 100 && mon >= 100) {
        return false;
      }

      if (year > 0 && (mday == 0 || mday > 31) && !seenFullYear) {
        int temp = year;
        year = mday;
        mday = temp;
      }

      if (mday <= 0 || mday > 31) {
        return false;
      }

    } else if (0 < mon && mon <= 12 && 0 < mday && mday <= 31) {
      /* (a) month/day/year */
    } else {
      /* (b) year/month/day */
      if (mon > 31 && mday <= 12 && year <= 31 && !seenFullYear) {
        int temp = year;
        year = mon;
        mon = mday;
        mday = temp;
      } else {
        return false;
      }
    }

    year = FixupYear(year);

    if (negativeYear) {
      year = -year;
    }
  }

  mon -= 1; /* convert month to 0-based */
  if (sec < 0) {
    sec = 0;
  }
  if (min < 0) {
    min = 0;
  }
  if (hour < 0) {
    hour = 0;
  }

  double date =
      MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, msec));

  if (tzOffset == -1) { /* no time zone specified, have to use local */
    date = UTC(forceUTC, date);
  } else {
    date += double(tzOffset) * msPerMinute;
  }

  *result = TimeClip(date);
  return true;
}

static bool ParseDate(JSContext* cx, DateTimeInfo::ForceUTC forceUTC,
                      const JSLinearString* s, ClippedTime* result) {
  AutoCheckCannotGC nogc;
  return s->hasLatin1Chars() ? ParseDate(cx, forceUTC, s->latin1Chars(nogc),
                                         s->length(), result)
                             : ParseDate(cx, forceUTC, s->twoByteChars(nogc),
                                         s->length(), result);
}

/**
 * 21.4.3.2 Date.parse ( string )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

static bool date_parse(JSContext* cx, unsigned argc, Value* vp) {
  AutoJSMethodProfilerEntry pseudoFrame(cx, "Date""parse");

  // Collect telemetry on how often Date.parse is being used.
  // This can be removed in the future, see Bug 1944630.
  cx->runtime()->setUseCounter(cx->global(), JSUseCounter::DATEPARSE);
  CallArgs args = CallArgsFromVp(argc, vp);
  if (args.length() == 0) {
    args.rval().setNaN();
    return true;
  }

  JSString* str = ToString<CanGC>(cx, args[0]);
  if (!str) {
    return false;
  }

  JSLinearString* linearStr = str->ensureLinear(cx);
  if (!linearStr) {
    return false;
  }

  ClippedTime result;
  if (!ParseDate(cx, ForceUTC(cx->realm()), linearStr, &result)) {
    args.rval().setNaN();
    return true;
  }

  args.rval().set(TimeValue(result));
  return true;
}

static ClippedTime NowAsMillis(JSContext* cx) {
  if (js::SupportDifferentialTesting()) {
    return TimeClip(0);
  }

  double now = PRMJ_Now();
  bool clampAndJitter = cx->realm()->behaviors().clampAndJitterTime();
  if (clampAndJitter && sReduceMicrosecondTimePrecisionCallback) {
    now = sReduceMicrosecondTimePrecisionCallback(
        now, cx->realm()->behaviors().reduceTimerPrecisionCallerType().value(),
        cx);
  } else if (clampAndJitter && sResolutionUsec) {
    double clamped = floor(now / sResolutionUsec) * sResolutionUsec;

    if (sJitter) {
      // Calculate a random midpoint for jittering. In the browser, we are
      // adversarial: Web Content may try to calculate the midpoint themselves
      // and use that to bypass it's security. In the JS Shell, we are not
      // adversarial, we want to jitter the time to recreate the operating
      // environment, but we do not concern ourselves with trying to prevent an
      // attacker from calculating the midpoint themselves. So we use a very
      // simple, very fast CRC with a hardcoded seed.

      uint64_t midpoint = BitwiseCast<uint64_t>(clamped);
      midpoint ^= 0x0F00DD1E2BAD2DED;  // XOR in a 'secret'
      // MurmurHash3 internal component from
      //   https://searchfox.org/mozilla-central/rev/61d400da1c692453c2dc2c1cf37b616ce13dea5b/dom/canvas/MurmurHash3.cpp#85
      midpoint ^= midpoint >> 33;
      midpoint *= uint64_t{0xFF51AFD7ED558CCD};
      midpoint ^= midpoint >> 33;
      midpoint *= uint64_t{0xC4CEB9FE1A85EC53};
      midpoint ^= midpoint >> 33;
      midpoint %= sResolutionUsec;

      if (now > clamped + midpoint) {  // We're jittering up to the next step
        now = clamped + sResolutionUsec;
      } else {  // We're staying at the clamped value
        now = clamped;
      }
    } else {  // No jitter, only clamping
      now = clamped;
    }
  }

  return TimeClip(now / PRMJ_USEC_PER_MSEC);
}

JS::ClippedTime js::DateNow(JSContext* cx) { return NowAsMillis(cx); }

static bool date_now(JSContext* cx, unsigned argc, Value* vp) {
  AutoJSMethodProfilerEntry pseudoFrame(cx, "Date""now");
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().set(TimeValue(NowAsMillis(cx)));
  return true;
}

DateTimeInfo::ForceUTC DateObject::forceUTC() const {
  return ForceUTC(realm());
}

void DateObject::setUTCTime(ClippedTime t) {
  for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++) {
    setReservedSlot(ind, UndefinedValue());
  }

  setFixedSlot(UTC_TIME_SLOT, TimeValue(t));
}

void DateObject::setUTCTime(ClippedTime t, MutableHandleValue vp) {
  setUTCTime(t);
  vp.set(TimeValue(t));
}

void DateObject::fillLocalTimeSlots() {
  const int32_t utcTZOffset =
      DateTimeInfo::utcToLocalStandardOffsetSeconds(forceUTC());

  /* Check if the cache is already populated. */
  if (!getReservedSlot(LOCAL_TIME_SLOT).isUndefined() &&
      getReservedSlot(UTC_TIME_ZONE_OFFSET_SLOT).toInt32() == utcTZOffset) {
    return;
  }

  /* Remember time zone used to generate the local cache. */
  setReservedSlot(UTC_TIME_ZONE_OFFSET_SLOT, Int32Value(utcTZOffset));

  double utcTime = UTCTime().toDouble();

  if (!std::isfinite(utcTime)) {
    for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++) {
      setReservedSlot(ind, DoubleValue(utcTime));
    }
    return;
  }

  int64_t localTime = LocalTime(forceUTC(), utcTime);

  setReservedSlot(LOCAL_TIME_SLOT, DoubleValue(localTime));

  const auto [year, month, day] = ToYearMonthDay(localTime);

  setReservedSlot(LOCAL_YEAR_SLOT, Int32Value(year));
  setReservedSlot(LOCAL_MONTH_SLOT, Int32Value(month));
  setReservedSlot(LOCAL_DATE_SLOT, Int32Value(day));

  int weekday = WeekDay(localTime);
  setReservedSlot(LOCAL_DAY_SLOT, Int32Value(weekday));

  int64_t yearStartTime = TimeFromYear(year);
  uint64_t yearTime = uint64_t(localTime - yearStartTime);
  int32_t yearSeconds = int32_t(yearTime / msPerSecond);
  setReservedSlot(LOCAL_SECONDS_INTO_YEAR_SLOT, Int32Value(yearSeconds));
}

/**
 * 21.4.4.10 Date.prototype.getTime ( )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

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

  // Steps 1-2.
  auto* unwrapped = UnwrapAndTypeCheckThis<DateObject>(cx, args, "getTime");
  if (!unwrapped) {
    return false;
  }

  // Step 3.
  args.rval().set(unwrapped->UTCTime());
  return true;
}

/**
 * B.2.3.1 Date.prototype.getYear ( )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

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

  // Steps 1-2.
  auto* unwrapped = UnwrapAndTypeCheckThis<DateObject>(cx, args, "getYear");
  if (!unwrapped) {
    return false;
  }

  // Steps 3-5.
  unwrapped->fillLocalTimeSlots();

  Value yearVal = unwrapped->localYear();
  if (yearVal.isInt32()) {
    int year = yearVal.toInt32() - 1900;
    args.rval().setInt32(year);
  } else {
    args.rval().set(yearVal);
  }
  return true;
}

/**
 * 21.4.4.4 Date.prototype.getFullYear ( )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

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

  // Steps 1-2.
  auto* unwrapped = UnwrapAndTypeCheckThis<DateObject>(cx, args, "getFullYear");
  if (!unwrapped) {
    return false;
  }

  // Steps 3-5.
  unwrapped->fillLocalTimeSlots();

  args.rval().set(unwrapped->localYear());
  return true;
}

/**
 * 21.4.4.18 Date.prototype.getUTCMonth ( )
 *
 * ES2025 draft rev 76814cbd5d7842c2a99d28e6e8c7833f1de5bee0
 */

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

  // Steps 1-2.
  auto* unwrapped =
      UnwrapAndTypeCheckThis<DateObject>(cx, args, "getUTCFullYear");
  if (!unwrapped) {
    return false;
  }

  // Step 3.
  double t = unwrapped->UTCTime().toDouble();
  MOZ_ASSERT(IsTimeValue(t));

  // Step 4.
--> --------------------

--> maximum size reached

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

96%


¤ 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.0.41Bemerkung:  (vorverarbeitet)  ¤

*Bot Zugriff






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 ist 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