| // Copyright 2021 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/objects/js-temporal-objects.h" |
| |
| #include <algorithm> |
| #include <optional> |
| #include <set> |
| |
| #include "include/v8config.h" |
| #include "src/api/api-inl.h" |
| #include "src/base/numerics/safe_conversions.h" |
| #include "src/common/globals.h" |
| #include "src/date/date.h" |
| #include "src/execution/isolate.h" |
| #include "src/heap/factory.h" |
| #include "src/numbers/conversions-inl.h" |
| #include "src/objects/js-objects-inl.h" |
| #include "src/objects/js-objects.h" |
| #include "src/objects/js-temporal-helpers.h" |
| #include "src/objects/js-temporal-objects-inl.h" |
| #include "src/objects/js-temporal-zoneinfo64.h" |
| #include "src/objects/managed-inl.h" |
| #include "src/objects/object-conversions-inl.h" |
| #include "src/objects/objects-inl.h" |
| #include "src/objects/option-utils.h" |
| #include "src/objects/property-descriptor.h" |
| #include "src/objects/string-set.h" |
| #include "src/strings/string-builder-inl.h" |
| #include "temporal_rs/I128Nanoseconds.hpp" |
| #include "temporal_rs/OwnedRelativeTo.hpp" |
| #include "temporal_rs/Unit.hpp" |
| #ifdef V8_INTL_SUPPORT |
| #include "src/objects/intl-objects.h" |
| #include "src/objects/js-date-time-format.h" |
| #include "src/objects/js-duration-format.h" |
| #include "unicode/calendar.h" |
| #include "unicode/unistr.h" |
| #endif // V8_INTL_SUPPORT |
| |
| namespace v8::internal { |
| |
| namespace { |
| |
| // Shorten enums with `using` |
| using temporal_rs::RoundingMode; |
| using temporal_rs::Unit; |
| |
| /** |
| * This header declare the Abstract Operations defined in the |
| * Temporal spec with the enum and struct for them. |
| */ |
| |
| template <typename T> |
| using TemporalResult = |
| temporal_rs::diplomat::result<T, temporal_rs::TemporalError>; |
| template <typename T> |
| using TemporalAllocatedResult = TemporalResult<std::unique_ptr<T>>; |
| |
| using temporal::DurationRecord; |
| using temporal::TimeDurationRecord; |
| |
| // Options |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporaldisambiguation |
| enum class Disambiguation { kCompatible, kEarlier, kLater, kReject }; |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-toshowcalendaroption |
| enum class ShowCalendar { kAuto, kAlways, kNever }; |
| |
| // https://tc39.es/proposal-temporal/#table-temporal-unsigned-rounding-modes |
| enum class UnsignedRoundingMode { |
| kInfinity, |
| kZero, |
| kHalfInfinity, |
| kHalfZero, |
| kHalfEven |
| }; |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-GetTemporalUnit |
| enum class UnitGroup { |
| kDate, |
| kTime, |
| kDateTime, |
| }; |
| |
| // Default value as used by GetTemporalUnitValuedOption |
| enum class DefaultValue { |
| kUnset, |
| kRequired, |
| }; |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimerecord |
| enum Completeness { |
| kComplete, |
| kPartial, |
| }; |
| |
| // Convenience method for getting the timezone provider |
| temporal_rs::Provider& TimeZoneProvider() { |
| return ZoneInfo64Provider::Singleton().Provider(); |
| } |
| |
| // Common error strings |
| static constexpr char kInvalidIsoDate[] = "Invalid ISO date."; |
| static constexpr char kInvalidTime[] = "Invalid time"; |
| static constexpr char kFiniteInteger[] = "Expected finite integer."; |
| static constexpr char kIntegerOutOfRange[] = "Integer out of range."; |
| static constexpr char kOptionMustBeObject[] = "Option must be object:"; |
| static constexpr char kCalendarMustBeString[] = "Calendar must be string."; |
| static constexpr char kRoundToMissing[] = "Must specify a roundTo parameter."; |
| static constexpr char kRoundToMustBeObject[] = "roundTo must be an object."; |
| static constexpr char kYearMustBeObject[] = "year argument must be an object."; |
| static constexpr char kTimeZoneMissing[] = "Must specify time zone."; |
| static constexpr char kWithNoPartial[] = |
| "Argument to with() must contain some date/time fields."; |
| |
| #define ORDINARY_CREATE_FROM_CONSTRUCTOR(obj, target, new_target, T) \ |
| DirectHandle<JSReceiver> new_target_receiver = Cast<JSReceiver>(new_target); \ |
| DirectHandle<Map> map; \ |
| ASSIGN_RETURN_ON_EXCEPTION( \ |
| isolate, map, \ |
| JSFunction::GetDerivedMap(isolate, target, new_target_receiver)); \ |
| DirectHandle<T> object = \ |
| Cast<T>(isolate->factory()->NewFastOrSlowJSObjectFromMap(map)); |
| |
| // Helper function to split the string into its two constituent encodings |
| // and call two different functions depending on the encoding |
| template <typename RetVal> |
| RetVal HandleStringEncodings( |
| Isolate* isolate, DirectHandle<String> string, |
| std::function<RetVal(std::string_view)> utf8_fn, |
| std::function<RetVal(std::u16string_view)> utf16_fn) { |
| string = String::Flatten(isolate, string); |
| DisallowGarbageCollection no_gc; |
| auto flat = string->GetFlatContent(no_gc); |
| if (flat.IsOneByte()) { |
| auto content = flat.ToOneByteVector(); |
| // reinterpret_cast since std string types accept signed chars |
| std::string_view view(reinterpret_cast<const char*>(content.data()), |
| content.size()); |
| return utf8_fn(view); |
| |
| } else { |
| auto content = flat.ToUC16Vector(); |
| std::u16string_view view(reinterpret_cast<const char16_t*>(content.data()), |
| content.size()); |
| return utf16_fn(view); |
| } |
| } |
| |
| // Take a Rust Result and turn it into a Maybe, suitable for use |
| // with error handling macros. |
| // |
| // Note that a lot of the types returned by Rust code prefer |
| // move semantics, try using MOVE_RETURN_ON_EXCEPTION |
| template <typename ContainedValue> |
| Maybe<ContainedValue> ExtractRustResult( |
| Isolate* isolate, TemporalResult<ContainedValue>&& rust_result) { |
| if (rust_result.is_err()) { |
| auto err = std::move(rust_result).err().value(); |
| Handle<String> msg; |
| if (err.msg.has_value()) { |
| bool success = |
| isolate->factory()->NewStringFromUtf8(err.msg.value()).ToHandle(&msg); |
| if (!success) { |
| msg = isolate->factory()->NewStringFromStaticChars("(utf8 error)"); |
| } |
| } else { |
| // We need to have *some* message here even if Rust doesn't have one |
| msg = isolate->factory()->NewStringFromStaticChars("Unspecified error."); |
| } |
| switch (err.kind) { |
| case temporal_rs::ErrorKind::Type: |
| THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kTemporal, msg)); |
| break; |
| case temporal_rs::ErrorKind::Range: |
| THROW_NEW_ERROR(isolate, |
| NewRangeError(MessageTemplate::kTemporal, msg)); |
| break; |
| case temporal_rs::ErrorKind::Syntax: |
| THROW_NEW_ERROR(isolate, |
| NewSyntaxError(MessageTemplate::kTemporal, msg)); |
| case temporal_rs::ErrorKind::Assert: |
| case temporal_rs::ErrorKind::Generic: |
| default: |
| // These cases shouldn't happen; the spec doesn't currently trigger |
| // these errors |
| THROW_NEW_ERROR(isolate, |
| NewError(MessageTemplate::kTemporalWithArg, |
| isolate->factory()->NewStringFromStaticChars( |
| "Internal error:"), |
| msg)); |
| } |
| return Nothing<ContainedValue>(); |
| } |
| return Just(std::move(rust_result).ok().value()); |
| } |
| |
| // Helper function to construct a JSType that wraps a RustType (infallible) |
| template <typename JSType> |
| MaybeDirectHandle<JSType> ConstructRustWrappingType( |
| Isolate* isolate, DirectHandle<JSFunction> target, |
| DirectHandle<HeapObject> new_target, |
| std::unique_ptr<typename JSType::RustType>&& rust_value) { |
| // CppGCManaged requires shared ownership |
| std::shared_ptr<typename JSType::RustType> rust_shared = |
| std::move(rust_value); |
| |
| DirectHandle<CppGCManaged<typename JSType::RustType>> managed = |
| CppGCManaged<typename JSType::RustType>::Create(isolate, 0, rust_shared); |
| |
| ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, JSType) |
| object->initialize_with_wrapped_rust_value(*managed); |
| return object; |
| } |
| |
| // Helper function to construct a JSType that wraps a RustType (fallible) |
| template <typename JSType> |
| MaybeDirectHandle<JSType> ConstructRustWrappingType( |
| Isolate* isolate, DirectHandle<JSFunction> target, |
| DirectHandle<HeapObject> new_target, |
| TemporalResult<std::unique_ptr<typename JSType::RustType>>&& rust_result) { |
| std::unique_ptr<typename JSType::RustType> rust_value = nullptr; |
| |
| MOVE_RETURN_ON_EXCEPTION(isolate, rust_value, |
| ExtractRustResult(isolate, std::move(rust_result))); |
| |
| return ConstructRustWrappingType<JSType>(isolate, target, new_target, |
| std::move(rust_value)); |
| } |
| |
| // Most of the time you just need JSType's constructor as the target, |
| // these helpers let you skip passing the argument |
| template <typename JSType> |
| MaybeDirectHandle<JSType> ConstructRustWrappingType( |
| Isolate* isolate, std::unique_ptr<typename JSType::RustType>&& rust_value) { |
| auto ctor = JSType::GetConstructorTarget(isolate); |
| return ConstructRustWrappingType<JSType>(isolate, ctor, ctor, |
| std::move(rust_value)); |
| } |
| |
| template <typename JSType> |
| MaybeDirectHandle<JSType> ConstructRustWrappingType( |
| Isolate* isolate, |
| TemporalResult<std::unique_ptr<typename JSType::RustType>>&& rust_result) { |
| auto ctor = JSType::GetConstructorTarget(isolate); |
| return ConstructRustWrappingType<JSType>(isolate, ctor, ctor, |
| std::move(rust_result)); |
| } |
| |
| } // namespace |
| |
| namespace temporal { |
| |
| // ====== Numeric conversions ====== |
| |
| // Note: All of these IntegralDouble functions MUST |
| // be given an integral number, typically obtained via |
| // ToIntegerIfIntegral or ToIntegerWithTruncation. |
| template <typename IntegerType> |
| IntegerType CastIntegralDouble(double d) { |
| DCHECK((base::IsValueInRangeForNumericType<IntegerType, double>(d))); |
| DCHECK_EQ(nearbyint(d), d); |
| return static_cast<IntegerType>(d); |
| } |
| |
| template <typename IntegerType> |
| IntegerType ClampIntegralDouble(double d, IntegerType min, IntegerType max) { |
| DCHECK_EQ(nearbyint(d), d); |
| double clamped = |
| std::clamp(d, static_cast<double>(min), static_cast<double>(max)); |
| return CastIntegralDouble<IntegerType>(clamped); |
| } |
| |
| template <typename IntegerType> |
| IntegerType ClampIntegralDoubleToRange(double d) { |
| return ClampIntegralDouble<IntegerType>( |
| d, std::numeric_limits<IntegerType>::min(), |
| std::numeric_limits<IntegerType>::max()); |
| } |
| |
| template <typename IntegerType> |
| Maybe<IntegerType> CheckDoubleInRange(Isolate* isolate, double d) { |
| if (!base::IsValueInRangeForNumericType<IntegerType, double>(d)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kIntegerOutOfRange)); |
| } |
| return Just(CastIntegralDouble<IntegerType>(d)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-tointegerifintegral |
| Maybe<double> ToIntegerIfIntegral(Isolate* isolate, |
| DirectHandle<Object> argument) { |
| // 1. Let number be ? ToNumber(argument). |
| DirectHandle<Number> number; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, number, |
| Object::ToNumber(isolate, argument)); |
| double number_double = Object::NumberValue(*number); |
| // 2. If number is not an integral Number, throw a RangeError exception. |
| if (!std::isfinite(number_double) || |
| nearbyint(number_double) != number_double) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kFiniteInteger)); |
| } |
| // 3. Return ℝ(number). |
| return Just(number_double); |
| } |
| |
| // temporal_rs currently accepts integer types in cases where |
| // the spec uses a double (and bounds-checks later). This helper |
| // allows safely converting objects to some known integer type. |
| // |
| // TODO(manishearth) This helper should be removed when it is unnecessary. |
| // Tracked in https://github.com/boa-dev/temporal/issues/334 |
| template <typename IntegerType> |
| Maybe<IntegerType> ToIntegerTypeIfIntegral(Isolate* isolate, |
| DirectHandle<Object> argument) { |
| double d; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, d, |
| ToIntegerIfIntegral(isolate, argument)); |
| if (!base::IsValueInRangeForNumericType<IntegerType, double>(d)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kIntegerOutOfRange)); |
| } |
| |
| return Just(static_cast<IntegerType>(d)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-tointegerwithtruncation |
| Maybe<double> ToIntegerWithTruncation(Isolate* isolate, |
| DirectHandle<Object> argument) { |
| // 1. Let number be ? ToNumber(argument). |
| DirectHandle<Number> number; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, number, |
| Object::ToNumber(isolate, argument)); |
| double number_double = Object::NumberValue(*number); |
| // 2. If number is NaN, +∞𝔽 or -∞𝔽, throw a RangeError exception. |
| if (!std::isfinite(number_double)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kFiniteInteger)); |
| } |
| |
| // 3. Return truncate(number). |
| return Just(std::trunc(number_double)); |
| } |
| |
| // Same as ToIntegerWithTruncation, but returns 0 when undefined |
| Maybe<double> ToIntegerWithTruncationOrZero(Isolate* isolate, |
| DirectHandle<Object> argument) { |
| if (IsUndefined(*argument)) { |
| return Just(0.0); |
| } |
| return ToIntegerWithTruncation(isolate, argument); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-topositiveintegerwithtruncation |
| Maybe<double> ToPositiveIntegerWithTruncation(Isolate* isolate, |
| DirectHandle<Object> argument) { |
| // 1. Let integer be ?ToIntegerWithTruncation(argument). |
| double integer; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, integer, |
| ToIntegerWithTruncation(isolate, argument)); |
| // 2. If integer is ≤ 0, throw a RangeError exception |
| if (integer <= 0) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_RANGE_ERROR("Expected positive integer.")); |
| } |
| |
| // 3. Return integer. |
| return Just(integer); |
| } |
| |
| static constexpr uint64_t kU64HighBitMask = uint64_t{1} << 63; |
| |
| // Throws RangeErrors for out-of-range BigInts, checking IsValidEpochNanoseconds |
| Maybe<temporal_rs::I128Nanoseconds> GetI128FromBigInt( |
| Isolate* isolate, DirectHandle<BigInt> bigint) { |
| static constexpr char kNSOutOfRange[] = "Nanoseconds out of range."; |
| |
| if (bigint->Words64Count() > 2) { |
| // 3. ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a |
| // RangeError exception. |
| // |
| // This only performs part of the check, the rest of it is done below with |
| // is_valid() |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kNSOutOfRange)); |
| } |
| |
| uint64_t words[2] = {0, 0}; |
| uint32_t word_count = 2; |
| int sign_bit = 0; |
| bigint->ToWordsArray64(&sign_bit, &word_count, words); |
| |
| if ((words[1] & kU64HighBitMask) != 0) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kNSOutOfRange)); |
| } |
| |
| uint64_t high = words[1]; |
| if (sign_bit == 1) { |
| high |= kU64HighBitMask; |
| } |
| |
| temporal_rs::I128Nanoseconds ns; |
| |
| ns.high = high; |
| ns.low = words[0]; |
| |
| if (!ns.is_valid()) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kNSOutOfRange)); |
| } |
| return Just(ns); |
| #undef NANOSECONDS_RANGE_ERROR |
| } |
| |
| MaybeDirectHandle<BigInt> I128ToBigInt(Isolate* isolate, |
| temporal_rs::I128Nanoseconds ns) { |
| uint64_t words[2]; |
| bool sign_bit; |
| |
| if ((ns.high & temporal::kU64HighBitMask) != 0) { |
| sign_bit = true; |
| words[1] = ns.high & ~temporal::kU64HighBitMask; |
| } else { |
| sign_bit = false; |
| words[1] = ns.high; |
| } |
| words[0] = ns.low; |
| |
| return BigInt::FromWords64(isolate, sign_bit, 2, words); |
| } |
| |
| bool IsValidTime(double hour, double minute, double second, double millisecond, |
| double microsecond, double nanosecond) { |
| // 1. If hour < 0 or hour > 23, then |
| // a. Return false. |
| if (hour < 0 || hour > 23) { |
| return false; |
| } |
| // 2. If minute < 0 or minute > 59, then |
| // a. Return false. |
| if (minute < 0 || minute > 59) { |
| return false; |
| } |
| // 3. If second < 0 or second > 59, then |
| // a. Return false. |
| if (second < 0 || second > 59) { |
| return false; |
| } |
| // 4. If millisecond < 0 or millisecond > 999, then |
| // a. Return false. |
| |
| if (millisecond < 0 || millisecond > 999) { |
| return false; |
| } |
| // 5. If microsecond < 0 or microsecond > 999, then |
| // a. Return false. |
| if (microsecond < 0 || microsecond > 999) { |
| return false; |
| } |
| // 6. If nanosecond < 0 or nanosecond > 999, then |
| // a. Return false. |
| if (nanosecond < 0 || nanosecond > 999) { |
| return false; |
| } |
| // 7. Return true. |
| return true; |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-isodaysinmonth |
| int8_t ISODaysInMonth(int32_t year, uint8_t month) { |
| switch (month) { |
| // 1. If month is 1, 3, 5, 7, 8, 10, or 12, return 31. |
| case 1: |
| case 3: |
| case 5: |
| case 7: |
| case 8: |
| case 10: |
| case 12: |
| return 31; |
| // 4. Return 28 + MathematicalInLeapYear(EpochTimeForYear(year)). |
| case 2: |
| if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) { |
| return 29; |
| } else { |
| return 28; |
| } |
| // 2. If month is 4, 6, 9, or 11, return 30. |
| default: |
| return 30; |
| } |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-isvalidisodate |
| bool IsValidIsoDate(double year, double month, double day) { |
| // 1. If month < 1 or month > 12, then |
| if (month < 1 || month > 12) { |
| // a. Return false. |
| return false; |
| } |
| |
| // This check is technically needed later when we check if things are in the |
| // Temporal range, but we do it now to ensure we can safely cast to int32_t |
| // before passing to Rust See https://github.com/boa-dev/temporal/issues/334. |
| if (!base::IsValueInRangeForNumericType<int32_t, double>(year)) { |
| return false; |
| } |
| |
| // IsValidIsoDate does not care about years that are "out of Temporal range", |
| // that gets handled later. |
| int32_t year_int = static_cast<int32_t>(year); |
| uint8_t month_int = static_cast<uint8_t>(month); |
| // 2. Let daysInMonth be ISODaysInMonth(year, month). |
| // 3. If day < 1 or day > daysInMonth, then |
| if (day < 1 || day > ISODaysInMonth(year_int, month_int)) { |
| // a. Return false. |
| return false; |
| } |
| |
| // 4. Return true. |
| return true; |
| } |
| |
| // ====== Options getters ====== |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-tomonthcode |
| Maybe<std::string> ToMonthCode(Isolate* isolate, |
| DirectHandle<Object> argument) { |
| static constexpr char kMonthCodeOutOfRange[] = "Month code out of range."; |
| |
| // 1. Let monthCode be ?ToPrimitive(argument, string). |
| DirectHandle<Object> mc_prim; |
| if (IsJSReceiver(*argument)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, mc_prim, |
| JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(argument), |
| ToPrimitiveHint::kString)); |
| } else { |
| mc_prim = argument; |
| } |
| |
| // 2. If monthCode is not a String, throw a TypeError exception. |
| if (!IsString(*mc_prim)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kMonthCodeOutOfRange)); |
| } |
| |
| auto month_code = Cast<String>(*mc_prim)->ToStdString(); |
| |
| // 3. If the length of monthCode is not 3 or 4, throw a RangeError exception. |
| if (month_code.size() != 3 && month_code.size() != 4) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kMonthCodeOutOfRange)); |
| } |
| |
| // 4. If the first code unit of monthCode is not 0x004D (LATIN CAPITAL LETTER |
| // M), throw a RangeError exception. |
| if (month_code[0] != 'M') { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kMonthCodeOutOfRange)); |
| } |
| // 5. If the second code unit of monthCode is not in the inclusive interval |
| // from 0x0030 (DIGIT ZERO) to 0x0039 (DIGIT NINE), throw a RangeError |
| // exception. |
| if (month_code[1] < '0' || month_code[1] > '9') { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kMonthCodeOutOfRange)); |
| } |
| // 6. If the third code unit of monthCode is not in the inclusive interval |
| // from 0x0030 (DIGIT ZERO) to 0x0039 (DIGIT NINE), throw a RangeError |
| // exception. |
| if (month_code[2] < '0' || month_code[2] > '9') { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kMonthCodeOutOfRange)); |
| } |
| // 7. If the length of monthCode is 4 and the fourth code unit of monthCode is |
| // not 0x004C (LATIN CAPITAL LETTER L), throw a RangeError exception. |
| if (month_code.size() == 4 && month_code[3] != 'L') { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kMonthCodeOutOfRange)); |
| } |
| // 8. Let monthCodeDigits be the substring of monthCode from 1 to 3. |
| // 9. Let monthCodeInteger be ℝ(StringToNumber(monthCodeDigits)). |
| // 10. If monthCodeInteger is 0 and the length of monthCode is not 4, throw a |
| // RangeError exception. |
| if (month_code[1] == '0' && month_code[2] == '0' && month_code.size() != 4) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kMonthCodeOutOfRange)); |
| } |
| // 11. Return monthCode. |
| return Just(month_code); |
| #undef MONTHCODE_RANGE_ERROR |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporaloverflow |
| // Also handles the undefined check from GetOptionsObject |
| Maybe<temporal_rs::ArithmeticOverflow> ToTemporalOverflowHandleUndefined( |
| Isolate* isolate, MaybeDirectHandle<Object> maybe_options, |
| const char* method_name) { |
| DirectHandle<Object> options; |
| // Default is "constrain" |
| if (!maybe_options.ToHandle(&options) || IsUndefined(*options)) { |
| return Just(temporal_rs::ArithmeticOverflow( |
| temporal_rs::ArithmeticOverflow::Constrain)); |
| } |
| auto overflow_ident = isolate->factory()->overflow_string(); |
| if (!IsJSReceiver(*options)) { |
| // (GetOptionsObject) 3. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR_WITH_ARG( |
| kOptionMustBeObject, overflow_ident)); |
| } |
| // 2. Return ? GetOption(options, "overflow", « String », « "constrain", |
| // "reject" », "constrain"). |
| return GetStringOption<temporal_rs::ArithmeticOverflow>( |
| isolate, Cast<JSReceiver>(options), overflow_ident, method_name, |
| std::to_array<const std::string_view>({"constrain", "reject"}), |
| std::to_array<temporal_rs::ArithmeticOverflow>( |
| {temporal_rs::ArithmeticOverflow::Constrain, |
| temporal_rs::ArithmeticOverflow::Reject}), |
| temporal_rs::ArithmeticOverflow::Constrain); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-getdirectionoption |
| |
| Maybe<temporal_rs::TransitionDirection> GetDirectionOption( |
| Isolate* isolate, DirectHandle<JSReceiver> options, |
| const char* method_name) { |
| // 1. Let stringValue be ? GetOption(options, "direction", string, « "next", |
| // "previous" », required). |
| temporal_rs::TransitionDirection dir; |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, dir, |
| GetStringOption<temporal_rs::TransitionDirection>( |
| isolate, Cast<JSReceiver>(options), |
| isolate->factory()->direction_string(), method_name, |
| std::to_array<const std::string_view>({"next", "previous"}), |
| std::to_array<temporal_rs::TransitionDirection>( |
| {temporal_rs::TransitionDirection::Next, |
| temporal_rs::TransitionDirection::Previous}), |
| std::nullopt)); |
| |
| return Just(dir); |
| } |
| // https://tc39.es/proposal-temporal/#sec-temporal-gettemporaldisambiguationoption |
| // Also handles the undefined check from GetOptionsObject |
| Maybe<temporal_rs::Disambiguation> |
| GetTemporalDisambiguationOptionHandleUndefined(Isolate* isolate, |
| DirectHandle<Object> options, |
| const char* method_name) { |
| // Default is "compatible" |
| if (IsUndefined(*options)) { |
| return Just( |
| temporal_rs::Disambiguation(temporal_rs::Disambiguation::Compatible)); |
| } |
| auto disambiguation_ident = isolate->factory()->disambiguation_string(); |
| if (!IsJSReceiver(*options)) { |
| // (GetOptionsObject) 3. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR_WITH_ARG( |
| kOptionMustBeObject, disambiguation_ident)); |
| } |
| // 1. Let stringValue be ?GetOption(options, "disambiguation", string, « |
| // "compatible", "earlier", "later", "reject" », "compatible"). |
| return GetStringOption<temporal_rs::Disambiguation>( |
| isolate, Cast<JSReceiver>(options), disambiguation_ident, method_name, |
| std::to_array<const std::string_view>( |
| {"compatible", "earlier", "later", "reject"}), |
| std::to_array<temporal_rs::Disambiguation>({ |
| temporal_rs::Disambiguation::Compatible, |
| temporal_rs::Disambiguation::Earlier, |
| temporal_rs::Disambiguation::Later, |
| temporal_rs::Disambiguation::Reject, |
| }), |
| temporal_rs::Disambiguation::Compatible); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-gettemporaloffsetoption |
| // Also handles the undefined check from GetOptionsObject |
| Maybe<temporal_rs::OffsetDisambiguation> GetTemporalOffsetOptionHandleUndefined( |
| Isolate* isolate, DirectHandle<Object> options, |
| temporal_rs::OffsetDisambiguation fallback, const char* method_name) { |
| // Default is fallback |
| if (IsUndefined(*options)) { |
| return Just(fallback); |
| } |
| auto offset_ident = isolate->factory()->offset_string(); |
| if (!IsJSReceiver(*options)) { |
| // (GetOptionsObject) 3. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR_WITH_ARG( |
| kOptionMustBeObject, offset_ident)); |
| } |
| // 5. Let stringValue be ? GetOption(options, "offset", string, « "prefer", |
| // "use", "ignore", "reject" », stringFallback). |
| return GetStringOption<temporal_rs::OffsetDisambiguation>( |
| isolate, Cast<JSReceiver>(options), offset_ident, method_name, |
| std::to_array<const std::string_view>( |
| {"prefer", "use", "ignore", "reject"}), |
| std::to_array<temporal_rs::OffsetDisambiguation>({ |
| temporal_rs::OffsetDisambiguation::Prefer, |
| temporal_rs::OffsetDisambiguation::Use, |
| temporal_rs::OffsetDisambiguation::Ignore, |
| temporal_rs::OffsetDisambiguation::Reject, |
| }), |
| fallback); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-gettemporalfractionalseconddigitsoption |
| Maybe<temporal_rs::Precision> GetTemporalFractionalSecondDigitsOption( |
| Isolate* isolate, DirectHandle<JSReceiver> normalized_options, |
| const char* method_name) { |
| auto auto_val = |
| temporal_rs::Precision{.is_minute = false, .precision = std::nullopt}; |
| |
| Factory* factory = isolate->factory(); |
| // 1. Let digitsValue be ?Get(options, "fractionalSecondDigits"). |
| DirectHandle<Object> digits_val; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, digits_val, |
| JSReceiver::GetProperty(isolate, normalized_options, |
| factory->fractionalSecondDigits_string())); |
| |
| // 2. If digitsValue is undefined, return auto. |
| if (IsUndefined(*digits_val)) { |
| return Just(auto_val); |
| } |
| |
| // 3. If digitsValue is not a Number, then |
| if (!IsNumber(*digits_val)) { |
| DirectHandle<String> string; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, string, |
| Object::ToString(isolate, digits_val)); |
| // a. If ? ToString(digitsValue) is not "auto", throw a RangeError |
| // exception. |
| if (!String::Equals(isolate, string, factory->auto_string())) { |
| THROW_NEW_ERROR(isolate, |
| NewRangeError(MessageTemplate::kPropertyValueOutOfRange, |
| factory->fractionalSecondDigits_string())); |
| } |
| // b. Return auto. |
| return Just(auto_val); |
| } |
| // 4. If digitsValue is NaN, +∞𝔽, or -∞𝔽, throw a RangeError exception. |
| auto digits_num = Cast<Number>(*digits_val); |
| auto digits_float = Object::NumberValue(digits_num); |
| if (std::isnan(digits_float) || std::isinf(digits_float)) { |
| THROW_NEW_ERROR(isolate, |
| NewRangeError(MessageTemplate::kPropertyValueOutOfRange, |
| factory->fractionalSecondDigits_string())); |
| } |
| // 5. Let digitCount be floor(ℝ(digitsValue)). |
| double digit_count = std::floor(digits_float); |
| // 6. If digitCount < 0 or digitCount > 9, throw a RangeError exception. |
| if (digit_count < 0 || digit_count > 9) { |
| THROW_NEW_ERROR(isolate, |
| NewRangeError(MessageTemplate::kPropertyValueOutOfRange, |
| factory->fractionalSecondDigits_string())); |
| } |
| |
| uint8_t clamped = CastIntegralDouble<uint8_t>(digit_count); |
| |
| return Just( |
| temporal_rs::Precision{.is_minute = false, .precision = clamped}); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-gettemporalunitvaluedoption |
| Maybe<std::optional<Unit>> GetTemporalUnitValuedOption( |
| Isolate* isolate, DirectHandle<JSReceiver> normalized_options, |
| DirectHandle<String> key, DefaultValue default_value, |
| const char* method_name) { |
| // 1. Let allowedStrings be a List containing all values in the "Singular |
| // property name" and "Plural property name" columns of Table 21, except the |
| // header row. |
| // 2. Append "auto" to allowedStrings. |
| // 3. NOTE: For each singular Temporal unit name that is contained within |
| // allowedStrings, the corresponding plural name is also contained within it. |
| |
| constexpr auto strs = std::to_array<const std::string_view>( |
| {"year", "month", "week", "day", "hour", |
| "minute", "second", "millisecond", "microsecond", "nanosecond", |
| "auto", "years", "months", "weeks", "days", |
| "hours", "minutes", "seconds", "milliseconds", "microseconds", |
| "nanoseconds"}); |
| constexpr auto enums = std::to_array<const std::optional<Unit::Value>>( |
| {Unit::Year, Unit::Month, Unit::Week, |
| Unit::Day, Unit::Hour, Unit::Minute, |
| Unit::Second, Unit::Millisecond, Unit::Microsecond, |
| Unit::Nanosecond, Unit::Auto, Unit::Year, |
| Unit::Month, Unit::Week, Unit::Day, |
| Unit::Hour, Unit::Minute, Unit::Second, |
| Unit::Millisecond, Unit::Microsecond, Unit::Nanosecond}); |
| |
| // 4. If default is unset, then |
| // a. Let defaultValue be undefined. |
| |
| std::optional<std::optional<Unit>> wrapped_default = std::nullopt; |
| |
| if (default_value == DefaultValue::kUnset) { |
| // GetStringOption treats a null default as REQUIRED |
| // however, we also wish to handle undefined/UNSET here, which we |
| // represent as a None value in the inner optional |
| wrapped_default = std::make_optional(std::optional<Unit>(std::nullopt)); |
| } |
| |
| // 6. Let value be ? GetOption(options, key, string, allowedStrings, |
| // defaultValue). |
| |
| std::optional<Unit::Value> value; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, value, |
| GetStringOption<std::optional<Unit::Value>>( |
| isolate, normalized_options, key, method_name, |
| strs, enums, wrapped_default)); |
| // 7. If value is undefined, return unset. |
| // 8. If value is "auto", return auto. |
| // 9. Return the value in the "Value" column of Table 21 corresponding to the |
| // row with value in its "Singular property name" or "Plural property name" |
| // column. |
| if (value.has_value()) { |
| return Just<std::optional<Unit>>((Unit)value.value()); |
| } else { |
| return Just<std::optional<Unit>>(std::nullopt); |
| } |
| } |
| |
| Maybe<void> ValidateTemporalUnitValue( |
| Isolate* isolate, std::optional<Unit> value_or_unset, UnitGroup unit_group, |
| std::optional<Unit> extra_values = std::nullopt) { |
| // 1. If value is unset, return unused. |
| if (!value_or_unset.has_value()) { |
| return JustVoid(); |
| } |
| auto value = value_or_unset.value(); |
| // 2. If extraValues is present and extraValues contains value, return unused. |
| if (extra_values == value) { |
| return JustVoid(); |
| } |
| |
| // 3. Let category be the value in the “Category” column of the row of Table |
| // 21 whose “Value” column contains value. If there is no such row, throw a |
| // RangeError exception. |
| // 4. If category is date and unitGroup is date or datetime, return unused. |
| // 5. If category is time and unitGroup is time or datetime, return unused. |
| switch (value) { |
| case Unit::Auto: |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_RANGE_ERROR("Auto unit not allowed here")); |
| case Unit::Year: |
| case Unit::Month: |
| case Unit::Week: |
| case Unit::Day: |
| if (unit_group == UnitGroup::kDate || |
| unit_group == UnitGroup::kDateTime) { |
| return JustVoid(); |
| } else { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR( |
| "Found date unit, expect time unit")); |
| } |
| default: |
| if (unit_group == UnitGroup::kTime || |
| unit_group == UnitGroup::kDateTime) { |
| return JustVoid(); |
| } |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR( |
| "Found date unit, expect time unit")); |
| } |
| // 6. Throw a RangeError exception. |
| // (done in branches above) |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-canonicalizecalendar |
| Maybe<temporal_rs::AnyCalendarKind> CanonicalizeCalendar( |
| Isolate* isolate, DirectHandle<String> calendar) { |
| std::string s = calendar->ToStdString(); |
| // 2. If calendars does not contain the ASCII-lowercase of id, throw a |
| // RangeError exception. |
| std::transform(s.begin(), s.end(), s.begin(), ::tolower); |
| |
| auto cal = temporal_rs::AnyCalendarKind::get_for_str(s); |
| |
| if (!cal.has_value()) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR_WITH_ARG( |
| "Unknown calendar type", calendar)); |
| } |
| // Other steps unnecessary, we're not storing these as -u- values but rather |
| // as enums. |
| return Just(cal.value()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-getroundingincrementoption |
| Maybe<uint32_t> GetRoundingIncrementOption( |
| Isolate* isolate, DirectHandle<JSReceiver> normalized_options) { |
| DirectHandle<Object> value; |
| |
| // 1. Let value be ? Get(options, "roundingIncrement"). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, value, |
| JSReceiver::GetProperty(isolate, normalized_options, |
| isolate->factory()->roundingIncrement_string())); |
| // 2. If value is undefined, return 1𝔽. |
| if (IsUndefined(*value)) { |
| return Just(static_cast<uint32_t>(1)); |
| } |
| |
| // 3. Let integerIncrement be ? ToIntegerWithTruncation(value). |
| double integer_increment; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, integer_increment, |
| ToIntegerWithTruncation(isolate, value)); |
| |
| // 4. If integerIncrement < 1 or integerIncrement > 10**9, throw a RangeError |
| // exception. |
| if (integer_increment < 1 || integer_increment > 1e9) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kIntegerOutOfRange)); |
| } |
| |
| return Just(static_cast<uint32_t>(integer_increment)); |
| } |
| |
| // sec-temporal-getroundingmodeoption |
| Maybe<RoundingMode> GetRoundingModeOption(Isolate* isolate, |
| DirectHandle<JSReceiver> options, |
| RoundingMode fallback, |
| const char* method_name) { |
| // 1. Return ? GetOption(normalizedOptions, "roundingMode", "string", « |
| // "ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", |
| // "halfTrunc", "halfEven" », fallback). |
| |
| static const auto values = std::to_array<RoundingMode>( |
| {RoundingMode::Ceil, RoundingMode::Floor, RoundingMode::Expand, |
| RoundingMode::Trunc, RoundingMode::HalfCeil, RoundingMode::HalfFloor, |
| RoundingMode::HalfExpand, RoundingMode::HalfTrunc, |
| RoundingMode::HalfEven}); |
| return GetStringOption<RoundingMode>( |
| isolate, options, isolate->factory()->roundingMode_string(), method_name, |
| std::to_array<const std::string_view>( |
| {"ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", |
| "halfExpand", "halfTrunc", "halfEven"}), |
| values, fallback); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-getshowoffsetoption |
| Maybe<temporal_rs::DisplayOffset> GetTemporalShowOffsetOption( |
| Isolate* isolate, DirectHandle<JSReceiver> options, |
| const char* method_name) { |
| // 1. Let stringValue be ?GetOption(options, "offset", string, « "auto", |
| // "never" », "auto"). |
| // 2. If stringValue is "never", return never. |
| // 3. Return auto. |
| static const auto values = std::to_array<temporal_rs::DisplayOffset>( |
| {temporal_rs::DisplayOffset::Auto, temporal_rs::DisplayOffset::Never}); |
| return GetStringOption<temporal_rs::DisplayOffset>( |
| isolate, options, isolate->factory()->offset_string(), method_name, |
| std::to_array<const std::string_view>({"auto", "never"}), values, |
| temporal_rs::DisplayOffset::Auto); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-gettemporalshowtimezonenameoption |
| Maybe<temporal_rs::DisplayTimeZone> GetTemporalShowTimeZoneNameOption( |
| Isolate* isolate, DirectHandle<JSReceiver> options, |
| const char* method_name) { |
| // 1. Let stringValue be ? GetOption(options, "timeZoneName", string, « |
| // "auto", "never", "critical" », "auto"). |
| // 2. If stringValue is "never", return never. |
| // 3. If stringValue is "critical", return critical. |
| static const auto values = std::to_array<temporal_rs::DisplayTimeZone>( |
| {temporal_rs::DisplayTimeZone::Auto, temporal_rs::DisplayTimeZone::Never, |
| temporal_rs::DisplayTimeZone::Critical}); |
| return GetStringOption<temporal_rs::DisplayTimeZone>( |
| isolate, options, isolate->factory()->timeZoneName_string(), method_name, |
| std::to_array<const std::string_view>({"auto", "never", "critical"}), |
| values, temporal_rs::DisplayTimeZone::Auto); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-getdifferencesettings |
| // |
| // This does not perform any validity checks, it only does the minimum needed |
| // to construct a DifferenceSettings object. temporal_rs handles the rest |
| Maybe<temporal_rs::DifferenceSettings> GetDifferenceSettingsWithoutChecks( |
| Isolate* isolate, DirectHandle<Object> options_obj, UnitGroup unit_group, |
| std::optional<Unit> fallback_smallest_unit, const char* method_name) { |
| DirectHandle<JSReceiver> options; |
| // 1. Set options to ? GetOptionsObject(options). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetOptionsObject(isolate, options_obj, method_name)); |
| |
| // 2. Let largestUnit be ? GetTemporalUnitValuedOption(options, "largestUnit", |
| // unset). |
| std::optional<Unit> largest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, largest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, options, isolate->factory()->largestUnit_string(), |
| DefaultValue::kUnset, method_name)); |
| |
| // 4. Let roundingIncrement be ?GetRoundingIncrementOption(options). |
| uint32_t rounding_increment; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_increment, |
| temporal::GetRoundingIncrementOption(isolate, options)); |
| |
| // 5. Let roundingMode be ?GetRoundingModeOption(options, trunc). |
| RoundingMode rounding_mode; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_mode, |
| temporal::GetRoundingModeOption(isolate, options, RoundingMode::Trunc, |
| method_name)); |
| // 9. Let smallestUnit be ? GetTemporalUnitValuedOption(options, |
| // "smallestUnit", unset). |
| std::optional<Unit> smallest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, smallest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, options, isolate->factory()->smallestUnit_string(), |
| DefaultValue::kUnset, method_name)); |
| |
| // remaining steps are validation, to be performed later |
| |
| return Just(temporal_rs::DifferenceSettings{.largest_unit = largest_unit, |
| .smallest_unit = smallest_unit, |
| .rounding_mode = rounding_mode, |
| .increment = rounding_increment}); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-gettemporalshowcalendarnameoption |
| Maybe<temporal_rs::DisplayCalendar> GetTemporalShowCalendarNameOption( |
| Isolate* isolate, DirectHandle<JSReceiver> options, |
| const char* method_name) { |
| // 1. Return ? GetOption(normalizedOptions, "calendarName", « String », « |
| // "auto", "always", "never" », "auto"). |
| return GetStringOption<temporal_rs::DisplayCalendar>( |
| isolate, options, isolate->factory()->calendarName_string(), method_name, |
| std::to_array<const std::string_view>( |
| {"auto", "always", "never", "critical"}), |
| std::to_array<temporal_rs::DisplayCalendar>( |
| {temporal_rs::DisplayCalendar::Auto, |
| temporal_rs::DisplayCalendar::Always, |
| temporal_rs::DisplayCalendar::Never, |
| temporal_rs::DisplayCalendar::Critical}), |
| temporal_rs::DisplayCalendar::Auto); |
| } |
| |
| // convenience method for getting the "calendar" field off of a Temporal object |
| std::optional<temporal_rs::AnyCalendarKind> ExtractCalendarFrom( |
| Isolate* isolate, Tagged<HeapObject> calendar_like) { |
| InstanceType instance_type = calendar_like->map()->instance_type(); |
| // a. If temporalCalendarLike has an [[InitializedTemporalDate]], |
| // [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], |
| // [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] |
| // internal slot, then |
| // |
| // i. Return temporalCalendarLike.[[Calendar]]. |
| if (InstanceTypeChecker::IsJSTemporalPlainDate(instance_type)) { |
| return Cast<JSTemporalPlainDate>(calendar_like) |
| ->wrapped_rust() |
| ->calendar() |
| .kind(); |
| } else if (InstanceTypeChecker::IsJSTemporalPlainDateTime(instance_type)) { |
| return Cast<JSTemporalPlainDateTime>(calendar_like) |
| ->wrapped_rust() |
| ->calendar() |
| .kind(); |
| } else if (InstanceTypeChecker::IsJSTemporalPlainMonthDay(instance_type)) { |
| return Cast<JSTemporalPlainMonthDay>(calendar_like) |
| ->wrapped_rust() |
| ->calendar() |
| .kind(); |
| } else if (InstanceTypeChecker::IsJSTemporalPlainYearMonth(instance_type)) { |
| return Cast<JSTemporalPlainYearMonth>(calendar_like) |
| ->wrapped_rust() |
| ->calendar() |
| .kind(); |
| } else if (InstanceTypeChecker::IsJSTemporalZonedDateTime(instance_type)) { |
| return Cast<JSTemporalZonedDateTime>(calendar_like) |
| ->wrapped_rust() |
| ->calendar() |
| .kind(); |
| } |
| return std::nullopt; |
| } |
| // https://tc39.es/proposal-temporal/#sec-temporal-gettemporalcalendarslotvaluewithisodefault |
| Maybe<temporal_rs::AnyCalendarKind> ToTemporalCalendarIdentifier( |
| Isolate* isolate, DirectHandle<Object> calendar_like) { |
| // 1. If temporalCalendarLike is an Object, then |
| if (IsHeapObject(*calendar_like)) { |
| // a. If temporalCalendarLike has an [[InitializedTemporalDate]], |
| // [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], |
| // [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] |
| // internal slot, then |
| // |
| // i. Return temporalCalendarLike.[[Calendar]]. |
| auto cal_field = |
| ExtractCalendarFrom(isolate, Cast<HeapObject>(*calendar_like)); |
| if (cal_field.has_value()) { |
| return Just(cal_field.value()); |
| } |
| } |
| |
| // 2. If temporalCalendarLike is not a String, throw a TypeError exception. |
| if (!IsString(*calendar_like)) { |
| THROW_NEW_ERROR( |
| isolate, NEW_TEMPORAL_TYPE_ERROR( |
| "Calendar must be string or calendared Temporal object.")); |
| } |
| auto stdstr = Cast<String>(calendar_like)->ToStdString(); |
| |
| // 3. Let identifier be ?ParseTemporalCalendarString(temporalCalendarLike). |
| // 4. Return ?CanonicalizeCalendar(identifier). |
| auto kind = |
| temporal_rs::AnyCalendarKind::parse_temporal_calendar_string(stdstr); |
| if (kind.has_value()) { |
| return Just(kind.value()); |
| } else { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_RANGE_ERROR("Invalid calendar string")); |
| } |
| } |
| // https://tc39.es/proposal-temporal/#sec-temporal-gettemporalcalendarslotvaluewithisodefault |
| Maybe<temporal_rs::AnyCalendarKind> GetTemporalCalendarIdentifierWithISODefault( |
| Isolate* isolate, DirectHandle<JSReceiver> options) { |
| // 1. If item has an [[InitializedTemporalDate]], |
| // [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], |
| // [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] |
| // internal slot, then |
| // |
| // a. Return item.[[Calendar]]. |
| if (IsHeapObject(*options)) { |
| auto cal_field = ExtractCalendarFrom(isolate, Cast<HeapObject>(*options)); |
| if (cal_field.has_value()) { |
| return Just(cal_field.value()); |
| } |
| } |
| // 2. Let calendarLike be ?Get(item, "calendar"). |
| DirectHandle<Object> calendar; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar, |
| JSReceiver::GetProperty(isolate, options, |
| isolate->factory()->calendar_string())); |
| |
| // 3. If calendarLike is undefined, then |
| if (IsUndefined(*calendar)) { |
| // a. Return "iso8601". |
| return Just( |
| temporal_rs::AnyCalendarKind(temporal_rs::AnyCalendarKind::Iso)); |
| } |
| return ToTemporalCalendarIdentifier(isolate, calendar); |
| } |
| |
| constexpr temporal_rs::ToStringRoundingOptions kToStringAuto = |
| temporal_rs::ToStringRoundingOptions{ |
| .precision = temporal_rs::Precision{.is_minute = false, |
| .precision = std::nullopt}, |
| .smallest_unit = std::nullopt, |
| .rounding_mode = std::nullopt, |
| }; |
| |
| // ====== Stringification operations ====== |
| |
| // Generic code for calling to_string() |
| // |
| // It has two varargs since there may be some implicit conversions between |
| // the args passed to this method and the args expected by Rust. |
| // |
| // https://tc39.es/proposal-temporal/#sec-temporal-temporaldurationtostring |
| // https://tc39.es/proposal-temporal/#sec-temporal-temporalinstanttostring |
| // https://tc39.es/proposal-temporal/#sec-temporal-temporalplaindatetostring |
| // https://tc39.es/proposal-temporal/#sec-temporal-temporaltimerecordtostring |
| // https://tc39.es/proposal-temporal/#sec-temporal-temporalisodatetimetostring |
| // https://tc39.es/proposal-temporal/#sec-temporal-temporalplainyearmonthtostring |
| // https://tc39.es/proposal-temporal/#sec-temporal-temporalplainmonthdaytostring |
| // https://tc39.es/proposal-temporal/#sec-temporal-temporalplainzoneddatetimetostring |
| template <typename JSType, typename... Args, typename... Args2> |
| MaybeDirectHandle<String> GenericTemporalToString( |
| Isolate* isolate, DirectHandle<JSType> val, |
| std::string (JSType::RustType::*method)(Args2...) const, Args... args) { |
| auto rust_object = val->wrapped_rust(); |
| // This is currently inefficient, can be improved after |
| // https://github.com/rust-diplomat/diplomat/issues/866 is fixed |
| auto output = (rust_object.raw()->*method)(args...); |
| |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendString(output); |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| // Same as the above, but bubbles out Rust errors |
| template <typename JSType, typename... Args, typename... Args2> |
| MaybeDirectHandle<String> GenericTemporalToString( |
| Isolate* isolate, DirectHandle<JSType> val, |
| TemporalResult<std::string> (JSType::RustType::*method)(Args2...) const, |
| Args... args) { |
| auto rust_object = val->wrapped_rust(); |
| std::string output; |
| // This is currently inefficient, can be improved after |
| // https://github.com/rust-diplomat/diplomat/issues/866 is fixed |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, output, |
| ExtractRustResult(isolate, (rust_object.raw()->*method)(args...))); |
| |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendString(output); |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| // ====== Record operations ====== |
| |
| constexpr temporal_rs::PartialDate kNullPartialDate = temporal_rs::PartialDate{ |
| .year = std::nullopt, |
| .month = std::nullopt, |
| .month_code = "", |
| .day = std::nullopt, |
| .era = "", |
| .era_year = std::nullopt, |
| .calendar = temporal_rs::AnyCalendarKind::Iso, |
| }; |
| constexpr temporal_rs::PartialTime kNullPartialTime = temporal_rs::PartialTime{ |
| .hour = std::nullopt, |
| .minute = std::nullopt, |
| .second = std::nullopt, |
| .millisecond = std::nullopt, |
| .microsecond = std::nullopt, |
| .nanosecond = std::nullopt, |
| }; |
| |
| constexpr temporal_rs::PartialZonedDateTime kNullPartialZonedDateTime = |
| temporal_rs::PartialZonedDateTime{ |
| .date = kNullPartialDate, |
| .time = kNullPartialTime, |
| .offset = std::nullopt, |
| .timezone = std::nullopt, |
| }; |
| |
| constexpr temporal_rs::PartialDateTime kNullPartialDateTime = |
| temporal_rs::PartialDateTime{.date = kNullPartialDate, |
| .time = kNullPartialTime}; |
| |
| struct TimeRecord { |
| std::optional<double> hour = std::nullopt; |
| std::optional<double> minute = std::nullopt; |
| std::optional<double> second = std::nullopt; |
| std::optional<double> millisecond = std::nullopt; |
| std::optional<double> microsecond = std::nullopt; |
| std::optional<double> nanosecond = std::nullopt; |
| Maybe<temporal_rs::PartialTime> Regulate( |
| Isolate* isolate, temporal_rs::ArithmeticOverflow overflow); |
| }; |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-regulatetime |
| // |
| // N.B. The spec implicitly assumes all fields are set; however |
| // we plan to call this in contexts where they are not (e.g. during |
| // .with(). Fortunately, ) |
| Maybe<temporal_rs::PartialTime> TimeRecord::Regulate( |
| Isolate* isolate, temporal_rs::ArithmeticOverflow overflow) { |
| temporal_rs::PartialTime partial = kNullPartialTime; |
| // 1. If overflow is constrain, then |
| if (overflow == temporal_rs::ArithmeticOverflow::Constrain) { |
| // a. Set hour to the result of clamping hour between 0 and 23. |
| if (hour.has_value()) { |
| partial.hour = ClampIntegralDouble<uint8_t>(hour.value(), 0, 23); |
| } |
| // b. Set minute to the result of clamping minute between 0 and 59. |
| if (minute.has_value()) { |
| partial.minute = ClampIntegralDouble<uint8_t>(minute.value(), 0, 59); |
| } |
| // c. Set second to the result of clamping second between 0 and 59. |
| if (second.has_value()) { |
| partial.second = ClampIntegralDouble<uint8_t>(second.value(), 0, 59); |
| } |
| // d. Set millisecond to the result of clamping millisecond between 0 and |
| // 999. |
| if (millisecond.has_value()) { |
| partial.millisecond = |
| ClampIntegralDouble<uint16_t>(millisecond.value(), 0, 999); |
| } |
| // e. Set microsecond to the result of clamping microsecond between 0 and |
| // 999. |
| if (microsecond.has_value()) { |
| partial.microsecond = |
| ClampIntegralDouble<uint16_t>(microsecond.value(), 0, 999); |
| } |
| // f. Set nanosecond to the result of clamping nanosecond between 0 and 999. |
| if (nanosecond.has_value()) { |
| partial.nanosecond = |
| ClampIntegralDouble<uint16_t>(nanosecond.value(), 0, 999); |
| } |
| } else { |
| // b. If IsValidTime(hour, minute, second, millisecond, microsecond, |
| // nanosecond) is false, throw a RangeError exception. |
| if (!IsValidTime(hour.value_or(0), minute.value_or(0), second.value_or(0), |
| millisecond.value_or(0), microsecond.value_or(0), |
| nanosecond.value_or(0))) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_RANGE_ERROR("Invalid time provided")); |
| } |
| if (hour.has_value()) { |
| partial.hour = CastIntegralDouble<uint8_t>(hour.value()); |
| } |
| if (minute.has_value()) { |
| partial.minute = CastIntegralDouble<uint8_t>(minute.value()); |
| } |
| if (second.has_value()) { |
| partial.second = CastIntegralDouble<uint8_t>(second.value()); |
| } |
| if (millisecond.has_value()) { |
| partial.millisecond = CastIntegralDouble<uint16_t>(millisecond.value()); |
| } |
| if (microsecond.has_value()) { |
| partial.microsecond = CastIntegralDouble<uint16_t>(microsecond.value()); |
| } |
| if (nanosecond.has_value()) { |
| partial.nanosecond = CastIntegralDouble<uint16_t>(nanosecond.value()); |
| } |
| } |
| return Just(partial); |
| } |
| |
| template <typename RustObject> |
| temporal_rs::PartialTime GetPartialTimeFromRust(RustObject& rust_object) { |
| return temporal_rs::PartialTime{ |
| .hour = rust_object->hour(), |
| .minute = rust_object->minute(), |
| .second = rust_object->second(), |
| .millisecond = rust_object->millisecond(), |
| .microsecond = rust_object->microsecond(), |
| .nanosecond = rust_object->nanosecond(), |
| }; |
| } |
| // These can eventually be replaced with methods upstream |
| temporal_rs::PartialTime GetPartialTime( |
| DirectHandle<JSTemporalPlainTime> plain_time) { |
| auto rust_object = plain_time->wrapped_rust(); |
| return GetPartialTimeFromRust(rust_object); |
| } |
| temporal_rs::PartialTime GetPartialTime( |
| DirectHandle<JSTemporalPlainDateTime> date_time) { |
| auto rust_object = date_time->wrapped_rust(); |
| return GetPartialTimeFromRust(rust_object); |
| } |
| temporal_rs::PartialTime GetPartialTime( |
| DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| auto rust_object = zoned_date_time->wrapped_rust(); |
| return GetPartialTimeFromRust(rust_object); |
| } |
| |
| template <typename RustObject> |
| temporal_rs::PartialDate GetPartialDateFromRust(RustObject& rust_object) { |
| return temporal_rs::PartialDate{ |
| .year = rust_object->year(), |
| .month = rust_object->month(), |
| .month_code = "", |
| .day = rust_object->day(), |
| .era = "", |
| .era_year = std::nullopt, |
| .calendar = rust_object->calendar().kind(), |
| }; |
| } |
| temporal_rs::PartialDate GetPartialDate( |
| DirectHandle<JSTemporalPlainDate> plain_date) { |
| auto rust_object = plain_date->wrapped_rust(); |
| return GetPartialDateFromRust(rust_object); |
| } |
| temporal_rs::PartialDate GetPartialDate( |
| DirectHandle<JSTemporalPlainDateTime> date_time) { |
| auto rust_object = date_time->wrapped_rust(); |
| return GetPartialDateFromRust(rust_object); |
| } |
| temporal_rs::PartialDate GetPartialDate( |
| DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| auto rust_object = zoned_date_time->wrapped_rust(); |
| return GetPartialDateFromRust(rust_object); |
| } |
| |
| temporal_rs::PartialDateTime GetPartialDateTime( |
| DirectHandle<JSTemporalPlainDate> plain_date) { |
| auto rust_object = plain_date->wrapped_rust(); |
| return temporal_rs::PartialDateTime{ |
| .date = GetPartialDateFromRust(rust_object), |
| .time = kNullPartialTime, |
| }; |
| } |
| temporal_rs::PartialDateTime GetPartialDateTime( |
| DirectHandle<JSTemporalPlainDateTime> date_time) { |
| auto rust_object = date_time->wrapped_rust(); |
| return temporal_rs::PartialDateTime{ |
| .date = GetPartialDateFromRust(rust_object), |
| .time = GetPartialTimeFromRust(rust_object), |
| }; |
| } |
| temporal_rs::PartialDateTime GetPartialDateTime( |
| DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| auto rust_object = zoned_date_time->wrapped_rust(); |
| return temporal_rs::PartialDateTime{ |
| .date = GetPartialDateFromRust(rust_object), |
| .time = GetPartialTimeFromRust(rust_object), |
| }; |
| } |
| |
| // Helper for ToTemporalPartialDurationRecord |
| // Maybe<std::optional> since the Maybe handles errors and the optional handles |
| // missing fields |
| Maybe<std::optional<double>> GetSingleDurationField( |
| Isolate* isolate, DirectHandle<JSReceiver> duration_like, |
| DirectHandle<String> field_name) { |
| DirectHandle<Object> val; |
| // Let val be ? Get(temporalDurationLike, fieldName). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, val, |
| JSReceiver::GetProperty(isolate, duration_like, field_name)); |
| // c. If val is not undefined, then |
| if (IsUndefined(*val)) { |
| return Just((std::optional<double>)std::nullopt); |
| } else { |
| double field; |
| // 5. If val is not undefined, set result.[[val]] to |
| // ?ToIntegerIfIntegral(val). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, field, |
| temporal::ToIntegerIfIntegral(isolate, val)); |
| |
| return Just(std::optional(field)); |
| } |
| } |
| |
| // GetSingleDurationField, but also casts into an integer, erroring if out of |
| // range. |
| // |
| // Can be removed once https://github.com/boa-dev/temporal/issues/189 has been |
| // fixed. |
| Maybe<std::optional<int64_t>> GetSingleDurationFieldInteger( |
| Isolate* isolate, DirectHandle<JSReceiver> duration_like, |
| DirectHandle<String> field_name) { |
| std::optional<double> ret_opt; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, ret_opt, |
| GetSingleDurationField(isolate, duration_like, field_name)); |
| if (ret_opt.has_value()) { |
| double ret = ret_opt.value(); |
| if (!base::IsValueInRangeForNumericType<int64_t, double>(ret)) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_RANGE_ERROR("Duration field out of range.")); |
| } |
| return Just(std::optional(static_cast<int64_t>(ret))); |
| } else { |
| return Just((std::optional<int64_t>)std::nullopt); |
| } |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-tooffsetstring |
| Maybe<std::string> ToOffsetString(Isolate* isolate, |
| DirectHandle<Object> argument) { |
| // 1. Let offset be ?ToPrimitive(argument, string). |
| DirectHandle<Object> offset_prim; |
| if (IsJSReceiver(*argument)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, offset_prim, |
| JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(argument), |
| ToPrimitiveHint::kString)); |
| } else { |
| offset_prim = argument; |
| } |
| |
| // 2. If offset is not a String, throw a TypeError exception. |
| if (!IsString(*offset_prim)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR("Offset must be string.")); |
| } |
| |
| // 3. Perform ?ParseDateTimeUTCOffset(offset). |
| auto offset_str = Cast<String>(offset_prim); |
| auto offset = offset_str->ToStdString(); |
| |
| // TODO(manishearth) this has a minor unnecessary cost of allocating a |
| // TimeZone, but it can be obviated once |
| // https://github.com/boa-dev/temporal/issues/330 is fixed. |
| RETURN_ON_EXCEPTION( |
| isolate, |
| ExtractRustResult(isolate, |
| temporal_rs::TimeZone::try_from_offset_str(offset))); |
| |
| return Just(std::move(offset)); |
| } |
| |
| Maybe<temporal_rs::TimeZone> ToTemporalTimeZoneIdentifier( |
| Isolate* isolate, DirectHandle<Object> tz_like) { |
| // 1. If temporalTimeZoneLike is an Object, then |
| // a. If temporalTimeZoneLike has an [[InitializedTemporalZonedDateTime]] |
| // internal slot, then |
| if (IsJSTemporalZonedDateTime(*tz_like)) { |
| // i. Return temporalTimeZoneLike.[[TimeZone]]. |
| return Just( |
| Cast<JSTemporalZonedDateTime>(tz_like)->wrapped_rust()->timezone()); |
| } |
| // 2. If temporalTimeZoneLike is not a String, throw a TypeError exception. |
| if (!IsString(*tz_like)) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_TYPE_ERROR( |
| "Time zone must be string or ZonedDateTime object.")); |
| } |
| |
| DirectHandle<String> str = Cast<String>(tz_like); |
| |
| auto std_str = str->ToStdString(); |
| |
| return ExtractRustResult(isolate, |
| temporal_rs::TimeZone::try_from_str_with_provider( |
| std_str, TimeZoneProvider())); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporalpartialdurationrecord |
| Maybe<temporal_rs::PartialDuration> ToTemporalPartialDurationRecord( |
| Isolate* isolate, DirectHandle<Object> duration_like_obj) { |
| Factory* factory = isolate->factory(); |
| |
| // 1. If temporalDurationLike is not an Object, then |
| if (!IsJSReceiver(*duration_like_obj)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_TYPE_ERROR("Must provide a duration.")); |
| } |
| |
| DirectHandle<JSReceiver> duration_like = Cast<JSReceiver>(duration_like_obj); |
| |
| // 2. Let result be a new partial Duration Record with each field set to |
| // undefined. |
| auto result = temporal_rs::PartialDuration{ |
| .years = std::nullopt, |
| .months = std::nullopt, |
| .weeks = std::nullopt, |
| .days = std::nullopt, |
| .hours = std::nullopt, |
| .minutes = std::nullopt, |
| .seconds = std::nullopt, |
| .milliseconds = std::nullopt, |
| .microseconds = std::nullopt, |
| .nanoseconds = std::nullopt, |
| }; |
| |
| // Steps 3-14: get each field in alphabetical order |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.days, |
| |
| temporal::GetSingleDurationFieldInteger(isolate, duration_like, |
| factory->days_string())); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.hours, |
| temporal::GetSingleDurationFieldInteger(isolate, duration_like, |
| factory->hours_string())); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.microseconds, |
| temporal::GetSingleDurationField(isolate, duration_like, |
| factory->microseconds_string())); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.milliseconds, |
| temporal::GetSingleDurationFieldInteger(isolate, duration_like, |
| factory->milliseconds_string())); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.minutes, |
| temporal::GetSingleDurationFieldInteger(isolate, duration_like, |
| factory->minutes_string())); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.months, |
| temporal::GetSingleDurationFieldInteger(isolate, duration_like, |
| factory->months_string())); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.nanoseconds, |
| temporal::GetSingleDurationField(isolate, duration_like, |
| factory->nanoseconds_string())); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.seconds, |
| temporal::GetSingleDurationFieldInteger(isolate, duration_like, |
| factory->seconds_string())); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.weeks, |
| temporal::GetSingleDurationFieldInteger(isolate, duration_like, |
| factory->weeks_string())); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.years, |
| temporal::GetSingleDurationFieldInteger(isolate, duration_like, |
| factory->years_string())); |
| |
| // 24. If years is undefined, and months is undefined, and weeks is undefined, |
| // and days is undefined, and hours is undefined, and minutes is undefined, |
| // and seconds is undefined, and milliseconds is undefined, and microseconds |
| // is undefined, and nanoseconds is undefined, throw a TypeError exception. |
| |
| if (!result.years.has_value() && !result.months.has_value() && |
| !result.weeks.has_value() && !result.days.has_value() && |
| !result.hours.has_value() && !result.minutes.has_value() && |
| !result.seconds.has_value() && !result.milliseconds.has_value() && |
| !result.microseconds.has_value() && !result.nanoseconds.has_value()) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR( |
| "Did not provide any valid Duration fields.")); |
| } |
| |
| return Just(result); |
| } |
| |
| // Helper for ToTemporalTimeRecord |
| // Maybe<std::optional> since the Maybe handles errors and the optional handles |
| // missing fields |
| Maybe<std::optional<double>> GetSingleTimeRecordField( |
| Isolate* isolate, DirectHandle<JSReceiver> time_like, |
| DirectHandle<String> field_name, bool* any) { |
| DirectHandle<Object> val; |
| // Let v be ?Get(temporalTimeLike, field_name). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, val, JSReceiver::GetProperty(isolate, time_like, field_name)); |
| // If val is not undefined, then |
| if (!IsUndefined(*val)) { |
| double field; |
| // 5. a. Set result.[[Hour]] to ?ToIntegerWithTruncation(hour). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, field, |
| temporal::ToIntegerWithTruncation(isolate, val)); |
| // b. Set any to true. |
| *any = true; |
| |
| return Just(std::optional(field)); |
| } else { |
| return Just((std::optional<double>)std::nullopt); |
| } |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-ispartialtemporalobject |
| Maybe<bool> IsPartialTemporalObject(Isolate* isolate, |
| DirectHandle<Object> value) { |
| // 1. If value is not an Object, return false. |
| if (!IsHeapObject(*value)) { |
| return Just(false); |
| } |
| InstanceType instance_type = Cast<HeapObject>(*value)->map()->instance_type(); |
| |
| if (!InstanceTypeChecker::IsJSReceiver(instance_type)) { |
| return Just(false); |
| } |
| |
| auto value_recvr = Cast<JSReceiver>(value); |
| |
| // 2. If value has an [[InitializedTemporalDate]], |
| // [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], |
| // [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or |
| // [[InitializedTemporalZonedDateTime]] internal slot, return false. |
| if (InstanceTypeChecker::IsJSTemporalPlainDate(instance_type) || |
| InstanceTypeChecker::IsJSTemporalPlainDateTime(instance_type) || |
| InstanceTypeChecker::IsJSTemporalPlainMonthDay(instance_type) || |
| InstanceTypeChecker::IsJSTemporalPlainTime(instance_type) || |
| InstanceTypeChecker::IsJSTemporalPlainYearMonth(instance_type) || |
| InstanceTypeChecker::IsJSTemporalZonedDateTime(instance_type)) { |
| return Just(false); |
| } |
| |
| // 3. Let calendarProperty be ? Get(value, "calendar"). |
| DirectHandle<Object> cal; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, cal, |
| JSReceiver::GetProperty(isolate, value_recvr, |
| isolate->factory()->calendar_string())); |
| |
| // 4. If calendarProperty is not undefined, return false. |
| if (!IsUndefined(*cal)) { |
| return Just(false); |
| } |
| |
| // 5. Let timeZoneProperty be ? Get(value, "timeZone"). |
| DirectHandle<Object> tz; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, tz, |
| JSReceiver::GetProperty(isolate, value_recvr, |
| isolate->factory()->timeZone_string())); |
| |
| // 6. If timeZoneProperty is not undefined, return false. |
| if (!IsUndefined(*tz)) { |
| return Just(false); |
| } |
| |
| // 7. return true |
| return Just(true); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimerecord |
| Maybe<TimeRecord> ToTemporalTimeRecord(Isolate* isolate, |
| DirectHandle<JSReceiver> time_like, |
| const char* method_name, |
| Completeness completeness = kComplete) { |
| Factory* factory = isolate->factory(); |
| |
| // 2. If completeness is complete, then |
| // a. Let result be a new TemporalTimeLike Record with each field set to 0. |
| // 3. Else, |
| // a. Let result be a new TemporalTimeLike Record with each field set to |
| // unset. |
| auto result = completeness == kPartial ? TimeRecord { |
| .hour = std::nullopt, |
| .minute = std::nullopt, |
| .second = std::nullopt, |
| .millisecond = std::nullopt, |
| .microsecond = std::nullopt, |
| .nanosecond = std::nullopt, |
| } : TimeRecord { |
| .hour = 0, |
| .minute = 0, |
| .second = 0, |
| .millisecond = 0, |
| .microsecond = 0, |
| .nanosecond = 0, |
| }; |
| |
| bool any = false; |
| |
| // Steps 3-14: get each field in alphabetical order |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.hour, |
| temporal::GetSingleTimeRecordField(isolate, time_like, |
| factory->hour_string(), &any)); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.microsecond, |
| temporal::GetSingleTimeRecordField(isolate, time_like, |
| factory->microsecond_string(), &any)); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.millisecond, |
| temporal::GetSingleTimeRecordField(isolate, time_like, |
| factory->millisecond_string(), &any)); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.minute, |
| temporal::GetSingleTimeRecordField(isolate, time_like, |
| factory->minute_string(), &any)); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.nanosecond, |
| temporal::GetSingleTimeRecordField(isolate, time_like, |
| factory->nanosecond_string(), &any)); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result.second, |
| temporal::GetSingleTimeRecordField(isolate, time_like, |
| factory->second_string(), &any)); |
| |
| if (!any) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR( |
| "Must specify at least one time field.")); |
| } |
| |
| return Just(result); |
| } |
| |
| struct DateRecord { |
| std::optional<double> year; |
| std::optional<double> month; |
| std::optional<std::string> month_code; |
| std::optional<double> day; |
| std::optional<std::string> era; |
| std::optional<double> era_year; |
| temporal_rs::AnyCalendarKind calendar = temporal_rs::AnyCalendarKind::Iso; |
| Maybe<temporal_rs::PartialDate> Regulate( |
| Isolate* isolate, |
| temporal_rs::ArithmeticOverflow overflow) V8_LIFETIME_BOUND; |
| }; |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-regulatetime |
| // |
| // N.B. The spec implicitly assumes all fields are set; however |
| // we plan to call this in contexts where they are not (e.g. during |
| // .with(). Fortunately, ) |
| Maybe<temporal_rs::PartialDate> DateRecord::Regulate( |
| Isolate* isolate, temporal_rs::ArithmeticOverflow overflow) { |
| temporal_rs::PartialDate partial = kNullPartialDate; |
| |
| if (month_code.has_value()) { |
| partial.month_code = month_code.value(); |
| } |
| if (era.has_value()) { |
| partial.era = era.value(); |
| } |
| partial.calendar = calendar; |
| // 1. If overflow is constrain, then |
| if (overflow == temporal_rs::ArithmeticOverflow::Constrain) { |
| if (year.has_value()) { |
| partial.year = ClampIntegralDoubleToRange<int32_t>(year.value()); |
| } |
| if (month.has_value()) { |
| partial.month = ClampIntegralDoubleToRange<int8_t>(month.value()); |
| } |
| if (day.has_value()) { |
| partial.day = ClampIntegralDoubleToRange<int8_t>(day.value()); |
| } |
| if (era_year.has_value()) { |
| partial.era_year = ClampIntegralDoubleToRange<int32_t>(era_year.value()); |
| } |
| } else { |
| if (year.has_value()) { |
| int32_t result; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result, CheckDoubleInRange<int32_t>(isolate, year.value())); |
| partial.year = result; |
| } |
| if (month.has_value()) { |
| uint8_t result; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result, CheckDoubleInRange<uint8_t>(isolate, month.value())); |
| partial.month = result; |
| } |
| if (day.has_value()) { |
| uint8_t result; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result, CheckDoubleInRange<uint8_t>(isolate, day.value())); |
| partial.day = result; |
| } |
| if (era_year.has_value()) { |
| int32_t result; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result, |
| CheckDoubleInRange<int32_t>(isolate, era_year.value())); |
| partial.era_year = result; |
| } |
| } |
| return Just(partial); |
| } |
| // Returned by PrepareCalendarFields |
| struct CombinedRecord { |
| DateRecord date; |
| TimeRecord time; |
| std::optional<std::string> offset; |
| std::optional<temporal_rs::TimeZone> time_zone; |
| |
| // For use in generic contexts |
| template <typename Ret> |
| Maybe<Ret> Regulate(Isolate* isolate, |
| temporal_rs::ArithmeticOverflow overflow) |
| V8_LIFETIME_BOUND; |
| }; |
| |
| template <> |
| Maybe<temporal_rs::PartialDate> CombinedRecord::Regulate( |
| Isolate* isolate, temporal_rs::ArithmeticOverflow overflow) { |
| DCHECK(!offset.has_value() && !time_zone.has_value()); |
| DCHECK(!time.hour.has_value() && !time.minute.has_value() && |
| !time.second.has_value() && !time.millisecond.has_value() && |
| !time.microsecond.has_value() && !time.nanosecond.has_value()); |
| return date.Regulate(isolate, overflow); |
| } |
| template <> |
| Maybe<temporal_rs::PartialTime> CombinedRecord::Regulate( |
| Isolate* isolate, temporal_rs::ArithmeticOverflow overflow) { |
| DCHECK(!offset.has_value() && !time_zone.has_value()); |
| DCHECK(!date.year.has_value() && !date.month.has_value() && |
| date.month_code == "" && !date.day.has_value() && date.era == "" && |
| !date.era_year.has_value() && |
| date.calendar == temporal_rs::AnyCalendarKind::Iso); |
| |
| return time.Regulate(isolate, overflow); |
| } |
| |
| template <> |
| Maybe<temporal_rs::PartialDateTime> CombinedRecord::Regulate( |
| Isolate* isolate, temporal_rs::ArithmeticOverflow overflow) { |
| DCHECK(!offset.has_value() && !time_zone.has_value()); |
| temporal_rs::PartialDate regulated_date = kNullPartialDate; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, regulated_date, |
| date.Regulate(isolate, overflow)); |
| temporal_rs::PartialTime regulated_time = kNullPartialTime; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, regulated_time, |
| time.Regulate(isolate, overflow)); |
| return Just(temporal_rs::PartialDateTime{ |
| .date = regulated_date, |
| .time = regulated_time, |
| }); |
| } |
| |
| template <> |
| Maybe<temporal_rs::PartialZonedDateTime> CombinedRecord::Regulate( |
| Isolate* isolate, temporal_rs::ArithmeticOverflow overflow) { |
| temporal_rs::PartialDate regulated_date = kNullPartialDate; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, regulated_date, |
| date.Regulate(isolate, overflow)); |
| temporal_rs::PartialTime regulated_time = kNullPartialTime; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, regulated_time, |
| time.Regulate(isolate, overflow)); |
| auto record = temporal_rs::PartialZonedDateTime{ |
| .date = regulated_date, |
| .time = regulated_time, |
| .offset = std::nullopt, |
| .timezone = std::nullopt, |
| }; |
| if (time_zone.has_value()) { |
| record.timezone = time_zone.value(); |
| } |
| if (offset.has_value()) { |
| record.offset = offset.value(); |
| } |
| return Just(record); |
| } |
| |
| enum class CalendarFieldsFlag : uint8_t { |
| kDay = 1 << 0, |
| // Month and MonthCode |
| kMonthFields = 1 << 1, |
| // year, era, eraYear |
| kYearFields = 1 << 2, |
| // hour, minute, second, millisecond, microsecont, nanosecond |
| kTimeFields = 1 << 3, |
| kOffset = 1 << 4, |
| kTimeZone = 1 << 5, |
| }; |
| using CalendarFieldsFlags = base::Flags<CalendarFieldsFlag>; |
| |
| DEFINE_OPERATORS_FOR_FLAGS(CalendarFieldsFlags) |
| |
| constexpr CalendarFieldsFlags kAllDateFlags = CalendarFieldsFlag::kDay | |
| CalendarFieldsFlag::kMonthFields | |
| CalendarFieldsFlag::kYearFields; |
| |
| enum class RequiredFields { |
| kNone, |
| kPartial, |
| kTimeZone, |
| }; |
| |
| // A single run of the PrepareCalendarFields iteration (Step 9, substeps a-c, |
| // NOT d) Returns whether or not the field was found. Does not handle the case |
| // when the field is not found. |
| template <typename OutType> |
| Maybe<bool> GetSingleCalendarField( |
| Isolate* isolate, DirectHandle<JSReceiver> fields, |
| DirectHandle<String> field_name, bool& any, OutType& output, |
| Maybe<OutType> (*conversion_func)(Isolate*, DirectHandle<Object>)) { |
| // b. Let value be ?Get(fields, property). |
| DirectHandle<Object> value; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, value, JSReceiver::GetProperty(isolate, fields, field_name)); |
| |
| // c. If value is not undefined, then |
| if (!IsUndefined(*value)) { |
| // i. Set any to true. |
| any = true; |
| // ii. Let Conversion be the Conversion value of the same row. |
| // (perform conversion) |
| // ix. Set result's field whose name is given in the Field Name column of |
| // the same row to value. |
| MOVE_RETURN_ON_EXCEPTION(isolate, output, conversion_func(isolate, value)); |
| return Just(true); |
| } |
| |
| return Just(false); |
| } |
| // Same as above but for DirectHandles |
| template <typename OutType> |
| Maybe<bool> GetSingleCalendarField( |
| Isolate* isolate, DirectHandle<JSReceiver> fields, |
| DirectHandle<String> field_name, bool& any, DirectHandle<OutType>& output, |
| MaybeDirectHandle<OutType> (*conversion_func)(Isolate*, |
| DirectHandle<Object>)) { |
| // b. Let value be ?Get(fields, property). |
| DirectHandle<Object> value; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, value, JSReceiver::GetProperty(isolate, fields, field_name)); |
| |
| // c. If value is not undefined, then |
| if (!IsUndefined(*value)) { |
| // i. Set any to true. |
| any = true; |
| // ii. Let Conversion be the Conversion value of the same row. |
| // (perform conversion) |
| // ix. Set result's field whose name is given in the Field Name column of |
| // the same row to value. |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, output, |
| conversion_func(isolate, value)); |
| return Just(true); |
| } |
| |
| return Just(false); |
| } |
| |
| // Setters take `resultField` (a field expression of CombinedRecords) |
| // and `field` (a variable holding a value, and the name of the property), |
| // and set resultField to field, performing additional work if necessary. |
| #define SIMPLE_SETTER(resultField, field) resultField = field; |
| #define MOVING_SETTER(resultField, field) resultField = std::move(field); |
| #define STR_CONVERSION_SETTER(resultField, field) \ |
| resultField = field->ToStdString(); |
| |
| // Conditions take a boolean expression and wrap it with additional checks |
| #define SIMPLE_CONDITION(cond) cond |
| #define ERA_CONDITION(cond) calendarUsesEras && (cond) |
| |
| #define NOOP_REQUIRED_CHECK |
| #define TIMEZONE_REQUIRED_CHECK \ |
| if (!found && required_fields == RequiredFields::kTimeZone) { \ |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kTimeZoneMissing)); \ |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-calendar-fields-records |
| // V(CalendarFieldsFlags, propertyName, resultField, Type, Conversion, |
| // CONDITION, SETTER, REQUIRED_CHECK, AssignOrMove) |
| #define CALENDAR_FIELDS(V) \ |
| V(kDay, day, result.date.day, double, ToPositiveIntegerWithTruncation, \ |
| SIMPLE_CONDITION, SIMPLE_SETTER, NOOP_REQUIRED_CHECK, ASSIGN) \ |
| V(kYearFields, era, result.date.era, DirectHandle<String>, Object::ToString, \ |
| ERA_CONDITION, STR_CONVERSION_SETTER, NOOP_REQUIRED_CHECK, ASSIGN) \ |
| V(kYearFields, eraYear, result.date.era_year, double, \ |
| ToIntegerWithTruncation, ERA_CONDITION, SIMPLE_SETTER, \ |
| NOOP_REQUIRED_CHECK, ASSIGN) \ |
| V(kTimeFields, hour, result.time.hour, double, ToIntegerWithTruncation, \ |
| SIMPLE_CONDITION, SIMPLE_SETTER, NOOP_REQUIRED_CHECK, ASSIGN) \ |
| V(kTimeFields, microsecond, result.time.microsecond, double, \ |
| ToIntegerWithTruncation, SIMPLE_CONDITION, SIMPLE_SETTER, \ |
| NOOP_REQUIRED_CHECK, ASSIGN) \ |
| V(kTimeFields, millisecond, result.time.millisecond, double, \ |
| ToIntegerWithTruncation, SIMPLE_CONDITION, SIMPLE_SETTER, \ |
| NOOP_REQUIRED_CHECK, ASSIGN) \ |
| V(kTimeFields, minute, result.time.minute, double, ToIntegerWithTruncation, \ |
| SIMPLE_CONDITION, SIMPLE_SETTER, NOOP_REQUIRED_CHECK, ASSIGN) \ |
| V(kMonthFields, month, result.date.month, double, \ |
| ToPositiveIntegerWithTruncation, SIMPLE_CONDITION, SIMPLE_SETTER, \ |
| NOOP_REQUIRED_CHECK, ASSIGN) \ |
| V(kMonthFields, monthCode, result.date.month_code, std::string, ToMonthCode, \ |
| SIMPLE_CONDITION, MOVING_SETTER, NOOP_REQUIRED_CHECK, ASSIGN) \ |
| V(kTimeFields, nanosecond, result.time.nanosecond, double, \ |
| ToIntegerWithTruncation, SIMPLE_CONDITION, SIMPLE_SETTER, \ |
| NOOP_REQUIRED_CHECK, ASSIGN) \ |
| V(kOffset, offset, result.offset, std::string, ToOffsetString, \ |
| SIMPLE_CONDITION, MOVING_SETTER, NOOP_REQUIRED_CHECK, ASSIGN) \ |
| V(kTimeFields, second, result.time.second, double, ToIntegerWithTruncation, \ |
| SIMPLE_CONDITION, SIMPLE_SETTER, NOOP_REQUIRED_CHECK, ASSIGN) \ |
| V(kTimeZone, timeZone, result.time_zone, temporal_rs::TimeZone, \ |
| ToTemporalTimeZoneIdentifier, SIMPLE_CONDITION, MOVING_SETTER, \ |
| TIMEZONE_REQUIRED_CHECK, MOVE) \ |
| V(kYearFields, year, result.date.year, double, ToIntegerWithTruncation, \ |
| SIMPLE_CONDITION, SIMPLE_SETTER, NOOP_REQUIRED_CHECK, ASSIGN) |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-preparecalendarfields |
| Maybe<CombinedRecord> PrepareCalendarFields(Isolate* isolate, |
| temporal_rs::AnyCalendarKind kind, |
| DirectHandle<JSReceiver> fields, |
| CalendarFieldsFlags which_fields, |
| RequiredFields required_fields) { |
| // 1. Assert: If requiredFieldNames is a List, requiredFieldNames contains |
| // zero or one of each of the elements of calendarFieldNames and |
| // nonCalendarFieldNames. |
| // 2. Let fieldNames be the list-concatenation of calendarFieldNames and |
| // nonCalendarFieldNames. |
| // 3. Let extraFieldNames be CalendarExtraFields(calendar, |
| // calendarFieldNames). |
| // Currently al |
| // 4. Set fieldNames to the list-concatenation of fieldNames and |
| // extraFieldNames. |
| // 5. Assert: fieldNames contains no duplicate elements. |
| |
| // All steps handled by RequiredFields/CalendarFieldsFlag being enums, and |
| // CalendarExtraFields is handled by calendarUsesEras below. |
| |
| // Currently all calendars except for iso, chinese, and dangi support eras. |
| // This may change, but is unlikely to. |
| // |
| // https://tc39.es/proposal-intl-era-monthcode/#sec-temporal-calendarsupportsera |
| bool calendarUsesEras = kind != temporal_rs::AnyCalendarKind::Iso && |
| kind != temporal_rs::AnyCalendarKind::Chinese && |
| kind != temporal_rs::AnyCalendarKind::Dangi; |
| |
| // 6. Let result be a Calendar Fields Record with all fields equal to unset. |
| CombinedRecord result; |
| |
| // This is not explicitly specced, but CombinedRecord contains the calendar |
| // kind unlike the spec, and no caller of PrepareCalendarFields does anything |
| // other than pair `fields` with `calendar` when passing to subsequent |
| // algorithms. |
| result.date.calendar = kind; |
| |
| // 7. Let any be false. |
| bool any = false; |
| |
| // 8. Let sortedPropertyNames be a List whose elements are the values in the |
| // Property Key column of Table 19 corresponding to the elements of |
| // fieldNames, sorted according to lexicographic code unit order. (handled by |
| // sorting) |
| |
| // 9. For each property name property of sortedPropertyNames, do |
| // a. Let key be the value in the Enumeration Key column of Table 19 |
| // corresponding to the row whose Property Key value is property. |
| // |
| // b. Let value be ?Get(fields, property). |
| // |
| // c. If value is not undefined, then |
| // i. Set any to true. |
| // ii. Let Conversion be the Conversion value of the same row. |
| // iii. If Conversion is to-integer-with-truncation, then |
| // 1. Set value to ? ToIntegerWithTruncation(value). |
| // 2. Set value to 𝔽(value). |
| // iv. Else if Conversion is to-positive-integer-with-truncation, then |
| // 1. Set value to ? ToPositiveIntegerWithTruncation(value). |
| // 2. Set value to 𝔽(value). |
| // v. Else if Conversion is to-string, then |
| // 1. Set value to ? ToString(value). |
| // vi. Else if Conversion is to-temporal-time-zone-identifier, then |
| // 1. Set value to ? ToTemporalTimeZoneIdentifier(value). |
| // vii. Else if Conversion is to-month-code, then |
| // 1. Set value to ? ToMonthCode(value). |
| // viii. Else, |
| // 1. Assert: Conversion is to-offset-string. |
| // 2. Set value to ? ToOffsetString(value). |
| // ix. Set result's field whose name is given in the Field Name column of |
| // the same row to value. |
| // d. Else if requiredFieldNames is a List, then |
| // i. If requiredFieldNames contains key, then |
| // 1. Throw a TypeError exception. |
| // ii. Set result's field whose name is given in the Field Name column of |
| // the same row to the corresponding Default value of the same row. |
| |
| #define GET_SINGLE_CALENDAR_FIELD(fieldsFlag, propertyName, resultField, Type, \ |
| Conversion, CONDITION, SETTER, \ |
| REQUIRED_CHECK, AssignOrMove) \ |
| if (CONDITION(which_fields & CalendarFieldsFlag::fieldsFlag)) { \ |
| Type propertyName; \ |
| bool found = 0; \ |
| AssignOrMove##_RETURN_ON_EXCEPTION( \ |
| isolate, found, \ |
| GetSingleCalendarField(isolate, fields, \ |
| isolate->factory()->propertyName##_string(), \ |
| any, propertyName, Conversion)); \ |
| if (found) { \ |
| SETTER(resultField, propertyName); \ |
| } \ |
| REQUIRED_CHECK \ |
| } |
| |
| CALENDAR_FIELDS(GET_SINGLE_CALENDAR_FIELD); |
| |
| #undef GET_SINGLE_CALENDAR_FIELD |
| |
| // 10. If requiredFieldNames is partial and any is false, then |
| if (required_fields == RequiredFields::kPartial && !any) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR( |
| "Must specify at least one calendar field.")); |
| } |
| |
| return Just(std::move(result)); |
| } |
| |
| #undef SIMPLE_SETTER |
| #undef MOVING_SETTER |
| #undef ANCHORED_SETTER_WITH_MOVE |
| #undef ANCHORED_SETTER_WITH_STR_CONVERSION |
| #undef SIMPLE_CONDITION |
| #undef ERA_CONDITION |
| #undef NOOP_REQUIRED_CHECK |
| #undef TIMEZONE_REQUIRED_CHECK |
| #undef CALENDAR_FIELDS |
| |
| // ====== System time ====== |
| |
| // https://tc39.es/proposal-temporal/#sec-systemtimezoneidentifier |
| temporal_rs::TimeZone UTCTimeZoneInner() { |
| auto result = temporal_rs::TimeZone::utc_with_provider(TimeZoneProvider()); |
| if (result.is_ok()) { |
| return std::move(result).ok().value(); |
| } |
| // TODO(Manishearth) use https://github.com/boa-dev/temporal/pull/554 instead |
| // when we can |
| return temporal_rs::TimeZone::zero(); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-systemtimezoneidentifier |
| temporal_rs::TimeZone UTCTimeZone() { |
| static temporal_rs::TimeZone UTC_TZ = UTCTimeZoneInner(); |
| return UTC_TZ; |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-systemtimezoneidentifier |
| #ifdef V8_INTL_SUPPORT |
| temporal_rs::TimeZone SystemTimeZoneIdentifier() { |
| auto tz_str = Intl::DefaultTimeZone(); |
| auto tz = temporal_rs::TimeZone::try_from_identifier_str_with_provider( |
| tz_str, TimeZoneProvider()) |
| .ok(); |
| if (tz.has_value()) { |
| return std::move(tz).value(); |
| } |
| return UTCTimeZone(); |
| } |
| #else // V8_INTL_SUPPORT |
| temporal_rs::TimeZone SystemTimeZoneIdentifier() { return UTCTimeZone(); } |
| #endif // V8_INTL_SUPPORT |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-systemutcepochnanoseconds |
| temporal_rs::I128Nanoseconds SystemUTCEpochNanoseconds(Isolate* isolate) { |
| DirectHandle<NativeContext> context = isolate->native_context(); |
| if (!IsUndefined(context->temporal_get_epoch_nanoseconds_callback())) { |
| v8::TemporalHostSystemUTCEpochNanosecondsCallback callback = |
| v8::ToCData<v8::TemporalHostSystemUTCEpochNanosecondsCallback, |
| kApiTemporalHostSystemUTCEpochNanosecondsCallbackTag>( |
| isolate, context->temporal_get_epoch_nanoseconds_callback()); |
| v8::Local<v8::Context> api_context = v8::Utils::ToLocal(context); |
| int64_t ns_int = callback(api_context); |
| absl::int128 ns = ns_int; |
| return temporal_rs::I128Nanoseconds{.high = absl::Uint128High64(ns), |
| .low = absl::Uint128Low64(ns)}; |
| } |
| |
| double ms = |
| V8::GetCurrentPlatform()->CurrentClockTimeMillisecondsHighResolution(); |
| |
| double intPart; |
| double fracPart = std::modf(ms, &intPart); |
| |
| absl::int128 ns = absl::int128(static_cast<int64_t>(intPart)) * 1'000'000; |
| ns += static_cast<int64_t>(std::round(fracPart * 1'000'000)); |
| |
| return temporal_rs::I128Nanoseconds{.high = absl::Uint128High64(ns), |
| .low = absl::Uint128Low64(ns)}; |
| } |
| |
| // Gets a ZonedDateTime in the ISO calendar representing system time, using |
| // either a passed in timezone or the system timezone. |
| // |
| // Primarily patterned off of Now.zonedDateTimeISO(), all other callers can |
| // convert to specific types. |
| // |
| // https://tc39.es/proposal-temporal/#sec-temporal.now.zoneddatetimeiso |
| // https://tc39.es/proposal-temporal/#sec-temporal.now.plaindatetimeiso |
| // https://tc39.es/proposal-temporal/#sec-temporal.now.plaindateiso |
| // https://tc39.es/proposal-temporal/#sec-temporal.now.plaintimeiso |
| Maybe<std::unique_ptr<temporal_rs::ZonedDateTime>> GenericTemporalNowISO( |
| Isolate* isolate, DirectHandle<Object> temporal_time_zone_like) { |
| temporal_rs::TimeZone time_zone; |
| |
| // 1. If temporalTimeZoneLike is undefined, then |
| if (IsUndefined(*temporal_time_zone_like)) { |
| // a. Let timeZone be SystemTimeZoneIdentifier(). |
| time_zone = SystemTimeZoneIdentifier(); |
| // 2. Else, |
| } else { |
| // a. Let timeZone be ? ToTemporalTimeZoneIdentifier(temporalTimeZoneLike). |
| MOVE_RETURN_ON_EXCEPTION(isolate, time_zone, |
| temporal::ToTemporalTimeZoneIdentifier( |
| isolate, temporal_time_zone_like)); |
| } |
| |
| // 3. Let ns be SystemUTCEpochNanoseconds(). |
| auto ns = SystemUTCEpochNanoseconds(isolate); |
| |
| // 4. Return ! CreateTemporalZonedDateTime(ns, timeZone, "iso8601"). |
| // TODO(manishearth) we can avoid the multiple layers of allocation here |
| // once https://github.com/boa-dev/temporal/pull/359 lands. |
| std::unique_ptr<temporal_rs::Instant> instant; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, instant, |
| ExtractRustResult(isolate, temporal_rs::Instant::try_new(ns))); |
| std::unique_ptr<temporal_rs::ZonedDateTime> zdt; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, zdt, |
| ExtractRustResult(isolate, instant->to_zoned_date_time_iso_with_provider( |
| time_zone, TimeZoneProvider()))); |
| |
| return Just(std::move(zdt)); |
| } |
| |
| // ====== Construction operations ====== |
| |
| // Implementing val.toFoo() for different Temporal types Foo |
| // (this is distinct from the ToTemporalFoo abstract operations below) |
| template <typename DstType, typename SrcType> |
| MaybeDirectHandle<DstType> GenericToTemporalMethod( |
| Isolate* isolate, DirectHandle<SrcType> val, |
| TemporalAllocatedResult<typename DstType::RustType> ( |
| SrcType::RustType::*method)() const) { |
| return ConstructRustWrappingType<DstType>( |
| isolate, (val->wrapped_rust().raw()->*method)()); |
| } |
| |
| // Same as above, but for infallible conversions |
| template <typename DstType, typename SrcType> |
| MaybeDirectHandle<DstType> GenericToTemporalMethod( |
| Isolate* isolate, DirectHandle<SrcType> val, |
| std::unique_ptr<typename DstType::RustType> (SrcType::RustType::*method)() |
| const) { |
| return ConstructRustWrappingType<DstType>( |
| isolate, (val->wrapped_rust().raw()->*method)()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporalduration |
| // |
| // We split this out since Temporal is lazy-initialized; so we do not wish |
| // to call ConstructRustWrappingType in a context where Temporal has not yet |
| // been initialized (e.g. DurationFormat) |
| Maybe<std::unique_ptr<temporal_rs::Duration>> ToTemporalDurationRust( |
| Isolate* isolate, DirectHandle<Object> item, const char* method_name) { |
| // 1. If item is an Object and item has an [[InitializedTemporalDuration]] |
| // internal slot, then a. a. Return ! CreateTemporalDuration(item.[[Years]], |
| // item.[[Months]], item.[[Weeks]], item.[[Days]], item.[[Hours]], |
| // item.[[Minutes]], item.[[Seconds]], item.[[Milliseconds]], |
| // item.[[Microseconds]], item.[[Nanoseconds]]). |
| if (IsJSTemporalDuration(*item)) { |
| auto duration = Cast<JSTemporalDuration>(item); |
| // i. Return !CreateTemporalInstant(item.[[EpochNanoseconds]]). |
| return Just(duration->wrapped_rust()->clone()); |
| } |
| |
| // 2. If item is not an Object, then |
| if (!IsJSReceiver(*item)) { |
| // a. If item is not a String, throw a TypeError exception. |
| if (!IsString(*item)) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_TYPE_ERROR( |
| "Duration argument must be Duration or string.")); |
| } |
| DirectHandle<String> str = Cast<String>(item); |
| // b. Let result be ? ParseTemporalDurationString(string). |
| DirectHandle<JSTemporalInstant> result; |
| |
| auto rust_result = |
| HandleStringEncodings<TemporalAllocatedResult<temporal_rs::Duration>>( |
| isolate, str, |
| [](std::string_view view) |
| -> TemporalAllocatedResult<temporal_rs::Duration> { |
| return temporal_rs::Duration::from_utf8(view); |
| }, |
| [](std::u16string_view view) |
| -> TemporalAllocatedResult<temporal_rs::Duration> { |
| return temporal_rs::Duration::from_utf16(view); |
| }); |
| return ExtractRustResult(isolate, std::move(rust_result)); |
| } |
| |
| temporal_rs::PartialDuration partial; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, partial, |
| temporal::ToTemporalPartialDurationRecord(isolate, item)); |
| |
| return ExtractRustResult( |
| isolate, temporal_rs::Duration::from_partial_duration(partial)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporalduration |
| MaybeDirectHandle<JSTemporalDuration> ToTemporalDuration( |
| Isolate* isolate, DirectHandle<Object> item, const char* method_name) { |
| std::unique_ptr<temporal_rs::Duration> duration; |
| MOVE_RETURN_ON_EXCEPTION(isolate, duration, |
| ToTemporalDurationRust(isolate, item, method_name)); |
| return ConstructRustWrappingType<JSTemporalDuration>(isolate, |
| std::move(duration)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporalduration |
| // |
| // Used by DurationFormat |
| Maybe<DurationRecord> ToTemporalDurationAsRecord(Isolate* isolate, |
| DirectHandle<Object> item, |
| const char* method_name) { |
| std::unique_ptr<temporal_rs::Duration> duration; |
| MOVE_RETURN_ON_EXCEPTION(isolate, duration, |
| ToTemporalDurationRust(isolate, item, method_name)); |
| return Just(temporal::DurationRecord{ |
| .years = static_cast<double>(duration->years()), |
| .months = static_cast<double>(duration->months()), |
| .weeks = static_cast<double>(duration->weeks()), |
| .time_duration = { |
| .days = static_cast<double>(duration->days()), |
| .hours = static_cast<double>(duration->hours()), |
| .minutes = static_cast<double>(duration->minutes()), |
| .seconds = static_cast<double>(duration->seconds()), |
| .milliseconds = static_cast<double>(duration->milliseconds()), |
| .microseconds = static_cast<double>(duration->microseconds()), |
| .nanoseconds = static_cast<double>(duration->nanoseconds()), |
| }}); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporalinstant |
| MaybeDirectHandle<JSTemporalInstant> ToTemporalInstant( |
| Isolate* isolate, DirectHandle<Object> item, const char* method_name) { |
| |
| // 1. If item is an Object, then |
| // a. If item has an [[InitializedTemporalInstant]] or |
| // [[InitializedTemporalZonedDateTime]] internal slot, then |
| if (IsJSTemporalInstant(*item)) { |
| auto instant = Cast<JSTemporalInstant>(item); |
| // i. Return !CreateTemporalInstant(item.[[EpochNanoseconds]]). |
| return ConstructRustWrappingType<JSTemporalInstant>( |
| isolate, instant->wrapped_rust()->clone()); |
| // ... or [[InitializedTemporalZonedDateTime]] internal slot |
| } else if (IsJSTemporalZonedDateTime(*item)) { |
| auto zdt = Cast<JSTemporalZonedDateTime>(item); |
| auto ns = zdt->wrapped_rust()->epoch_nanoseconds(); |
| // i. Return !CreateTemporalInstant(item.[[EpochNanoseconds]]). |
| return ConstructRustWrappingType<JSTemporalInstant>( |
| isolate, temporal_rs::Instant::try_new(ns)); |
| } |
| // c. Set item to ?ToPrimitive(item, STRING). |
| DirectHandle<Object> item_prim; |
| if (IsJSReceiver(*item)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, item_prim, |
| JSReceiver::ToPrimitive(isolate, Cast<JSReceiver>(item), |
| ToPrimitiveHint::kString)); |
| } else { |
| item_prim = item; |
| } |
| |
| if (!IsString(*item_prim)) { |
| THROW_NEW_ERROR( |
| isolate, |
| NEW_TEMPORAL_TYPE_ERROR("Instant argument must be Instant or string.")); |
| } |
| |
| DirectHandle<String> item_string = Cast<String>(item_prim); |
| |
| |
| auto rust_result = |
| HandleStringEncodings<TemporalAllocatedResult<temporal_rs::Instant>>( |
| isolate, item_string, |
| [](std::string_view view) |
| -> TemporalAllocatedResult<temporal_rs::Instant> { |
| return temporal_rs::Instant::from_utf8(view); |
| }, |
| [](std::u16string_view view) |
| -> TemporalAllocatedResult<temporal_rs::Instant> { |
| return temporal_rs::Instant::from_utf16(view); |
| }); |
| return ConstructRustWrappingType<JSTemporalInstant>(isolate, |
| std::move(rust_result)); |
| } |
| |
| // A lot of branches in the ToTemporalFoo functions read the overflow object and |
| // do nothing with it |
| #define READ_AND_DISCARD_OVERFLOW(options_obj) \ |
| RETURN_ON_EXCEPTION(isolate, temporal::ToTemporalOverflowHandleUndefined( \ |
| isolate, options_obj, method_name)) |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporaltime |
| MaybeDirectHandle<JSTemporalPlainTime> ToTemporalTime( |
| Isolate* isolate, DirectHandle<Object> item, |
| MaybeDirectHandle<Object> options_obj, const char* method_name) { |
| // 1. If options is not present, set options to undefined. |
| // (handled by caller) |
| |
| // This error is eventually thrown by step 3; we perform a check early |
| // so that we can optimize with InstanceType. Step 1 and 2 are unobservable. |
| if (!IsHeapObject(*item)) { |
| THROW_NEW_ERROR( |
| isolate, |
| NEW_TEMPORAL_TYPE_ERROR("Time-like argument must be object or string")); |
| } |
| InstanceType instance_type = Cast<HeapObject>(*item)->map()->instance_type(); |
| |
| // 2. If item is an Object, then |
| if (InstanceTypeChecker::IsJSReceiver(instance_type)) { |
| auto partial = temporal::kNullPartialTime; |
| // a. If item has an [[InitializedTemporalTime]] internal slot, then |
| if (InstanceTypeChecker::IsJSTemporalPlainTime(instance_type)) { |
| auto cast = Cast<JSTemporalPlainTime>(item); |
| |
| // ii. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| READ_AND_DISCARD_OVERFLOW(options_obj); |
| |
| // iii. Return !CreateTemporalTime(item.[[Time]]). |
| return ConstructRustWrappingType<JSTemporalPlainTime>( |
| isolate, cast->wrapped_rust()->clone()); |
| // b. If item has an [[InitializedTemporalDateTime]] internal slot, then |
| } else if (InstanceTypeChecker::IsJSTemporalPlainDateTime(instance_type)) { |
| // iii. Return ! CreateTemporalTime(item.[[ISODateTime]].[[Time]]). |
| partial = GetPartialTime(Cast<JSTemporalPlainDateTime>(item)); |
| // c. If item has an [[InitializedTemporalZonedDateTime]] internal slot, |
| // then |
| } else if (InstanceTypeChecker::IsJSTemporalZonedDateTime(instance_type)) { |
| // i. Let isoDateTime be GetISODateTimeFor(item.[[TimeZone]], |
| // item.[[EpochNanoseconds]]). |
| partial = GetPartialTime(Cast<JSTemporalZonedDateTime>(item)); |
| // iv. Return !CreateTemporalTime(isoDateTime.[[Time]]). |
| } else { |
| // d. Let result be ?ToTemporalTimeRecord(item). |
| DirectHandle<JSReceiver> item_recvr = Cast<JSReceiver>(item); |
| temporal::TimeRecord record; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, record, |
| temporal::ToTemporalTimeRecord(isolate, item_recvr, method_name)); |
| |
| // e. Let resolvedOptions be ? GetOptionsObject(options). |
| // f. Let overflow be ? GetTemporalOverflowOption(resolvedOptions). |
| temporal_rs::ArithmeticOverflow overflow; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, overflow, |
| temporal::ToTemporalOverflowHandleUndefined( |
| isolate, options_obj, method_name)); |
| // g. Set result to ? RegulateTime(result.[[Hour]], result.[[Minute]], |
| // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], |
| // result.[[Nanosecond]], overflow). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, partial, |
| record.Regulate(isolate, overflow)); |
| |
| return ConstructRustWrappingType<JSTemporalPlainTime>( |
| isolate, temporal_rs::PlainTime::from_partial(partial, overflow)); |
| } |
| |
| // (found in each branch above) |
| temporal_rs::ArithmeticOverflow overflow; |
| // e. Let resolvedOptions be ?GetOptionsObject(options). |
| // f. f. Let overflow be ? GetTemporalOverflowOption(resolvedOptions). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, overflow, |
| temporal::ToTemporalOverflowHandleUndefined( |
| isolate, options_obj, method_name)); |
| |
| return ConstructRustWrappingType<JSTemporalPlainTime>( |
| isolate, temporal_rs::PlainTime::from_partial(partial, overflow)); |
| |
| // 3. Else, |
| } else { |
| // a. If item is not a String, throw a TypeError exception. |
| if (!InstanceTypeChecker::IsString(instance_type)) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_TYPE_ERROR( |
| "Time-like argument must be object or string")); |
| } |
| DirectHandle<String> str = Cast<String>(item); |
| |
| auto rust_result = |
| HandleStringEncodings<TemporalAllocatedResult<temporal_rs::PlainTime>>( |
| isolate, str, |
| [](std::string_view view) |
| -> TemporalAllocatedResult<temporal_rs::PlainTime> { |
| return temporal_rs::PlainTime::from_utf8(view); |
| }, |
| [](std::u16string_view view) |
| -> TemporalAllocatedResult<temporal_rs::PlainTime> { |
| return temporal_rs::PlainTime::from_utf16(view); |
| }); |
| |
| std::unique_ptr<temporal_rs::PlainTime> time; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, time, ExtractRustResult(isolate, std::move(rust_result))); |
| |
| READ_AND_DISCARD_OVERFLOW(options_obj); |
| return ConstructRustWrappingType<JSTemporalPlainTime>(isolate, |
| std::move(time)); |
| } |
| } |
| |
| // Instead of returning Midnight, just returns null, since temporal_rs |
| // handles the midnight-setting |
| // |
| // https://tc39.es/proposal-temporal/#sec-temporal-totimerecordormidnight |
| Maybe<CppGCManaged<temporal_rs::PlainTime>::Ptr> ToTimeRecordOrMidnight( |
| Isolate* isolate, DirectHandle<Object> item, |
| DirectHandle<JSTemporalPlainTime>& output_time, const char* method_name) { |
| // 1. If item is undefined, return MidnightTimeRecord(). |
| if (IsUndefined(*item)) { |
| return Just(CppGCManaged<temporal_rs::PlainTime>::Ptr()); |
| } |
| |
| // 2. Let plainTime be ? ToTemporalTime(item). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, output_time, |
| ToTemporalTime(isolate, item, {}, method_name)); |
| |
| // 3. Return plainTime.[[Time]]. |
| CppGCManaged<temporal_rs::PlainTime>::Ptr empty = output_time->wrapped_rust(); |
| return Just(std::move(empty)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporaldate |
| MaybeDirectHandle<JSTemporalPlainDate> ToTemporalDate( |
| Isolate* isolate, DirectHandle<Object> item, |
| MaybeDirectHandle<Object> options_obj, const char* method_name) { |
| // 1. If options is not present, set options to undefined. |
| // (handled by caller) |
| |
| // This error is eventually thrown by step 3a; we perform a check early |
| // so that we can optimize with InstanceType. Step 1 and 2 are unobservable. |
| if (!IsHeapObject(*item)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR( |
| "Date argument must be object or string.")); |
| } |
| InstanceType instance_type = Cast<HeapObject>(*item)->map()->instance_type(); |
| // 2. If item is an Object, then |
| if (InstanceTypeChecker::IsJSReceiver(instance_type)) { |
| auto partial = temporal::kNullPartialDate; |
| // a. If item has an [[InitializedTemporalDate]] internal slot, then |
| if (InstanceTypeChecker::IsJSTemporalPlainDate(instance_type)) { |
| auto cast = Cast<JSTemporalPlainDate>(item); |
| // ii. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| READ_AND_DISCARD_OVERFLOW(options_obj); |
| // iii. Return !CreateTemporalDate(item.[[Date]], item.[[Calendar]]). |
| return ConstructRustWrappingType<JSTemporalPlainDate>( |
| isolate, cast->wrapped_rust()->clone()); |
| // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, |
| // then |
| } else if (InstanceTypeChecker::IsJSTemporalZonedDateTime(instance_type)) { |
| // i. Let isoDateTime be GetISODateTimeFor(item.[[TimeZone]], |
| // item.[[EpochNanoseconds]]). |
| // |
| // iv. Return !CreateTemporalDate(isoDateTime.[[ISODate]], |
| // item.[[Calendar]]). |
| partial = GetPartialDate(Cast<JSTemporalZonedDateTime>(item)); |
| // c. If item has an [[InitializedTemporalDateTime]] internal slot, then |
| } else if (InstanceTypeChecker::IsJSTemporalPlainDateTime(instance_type)) { |
| // iii. Return !CreateTemporalDate(item.[[ISODateTime]].[[ISODate]], |
| // item.[[Calendar]]). |
| partial = GetPartialDate(Cast<JSTemporalPlainDateTime>(item)); |
| } else { |
| // d. Let calendar be ?GetTemporalCalendarIdentifierWithISODefault(item). |
| temporal_rs::AnyCalendarKind kind = temporal_rs::AnyCalendarKind::Iso; |
| DirectHandle<JSReceiver> item_recvr = Cast<JSReceiver>(item); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, kind, |
| temporal::GetTemporalCalendarIdentifierWithISODefault(isolate, |
| item_recvr)); |
| |
| // e. Let fields be ?PrepareCalendarFields(calendar, item, « year, month, |
| // month-code, day», «», «»). |
| CombinedRecord fields; |
| |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, fields, |
| PrepareCalendarFields(isolate, kind, item_recvr, kAllDateFlags, |
| RequiredFields::kNone)); |
| temporal_rs::ArithmeticOverflow overflow; |
| // f. Let resolvedOptions be ?GetOptionsObject(options). |
| // g. f. Let overflow be ? GetTemporalOverflowOption(resolvedOptions). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, overflow, |
| temporal::ToTemporalOverflowHandleUndefined( |
| isolate, options_obj, method_name)); |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, partial, |
| fields.Regulate<temporal_rs::PartialDate>(isolate, overflow)); |
| return ConstructRustWrappingType<JSTemporalPlainDate>( |
| isolate, temporal_rs::PlainDate::from_partial(partial, overflow)); |
| } |
| |
| // (from each branch above) |
| // i. Let resolvedOptions be ? GetOptionsObject(options). |
| // ii. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| READ_AND_DISCARD_OVERFLOW(options_obj); |
| |
| return ConstructRustWrappingType<JSTemporalPlainDate>( |
| isolate, temporal_rs::PlainDate::from_partial(partial, std::nullopt)); |
| // 3. Else, |
| } else { |
| // a. If item is not a String, throw a TypeError exception. |
| if (!InstanceTypeChecker::IsString(instance_type)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR( |
| "Date argument must be object or string.")); |
| } |
| DirectHandle<String> str = Cast<String>(item); |
| |
| // Rest of the steps handled in Rust. |
| |
| std::unique_ptr<temporal_rs::ParsedDate> date; |
| auto rust_result = |
| HandleStringEncodings<TemporalAllocatedResult<temporal_rs::ParsedDate>>( |
| isolate, str, |
| [](std::string_view view) |
| -> TemporalAllocatedResult<temporal_rs::ParsedDate> { |
| return temporal_rs::ParsedDate::from_utf8(view); |
| }, |
| [](std::u16string_view view) |
| -> TemporalAllocatedResult<temporal_rs::ParsedDate> { |
| return temporal_rs::ParsedDate::from_utf16(view); |
| }); |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, date, ExtractRustResult(isolate, std::move(rust_result))); |
| |
| // 9. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| READ_AND_DISCARD_OVERFLOW(options_obj); |
| |
| return ConstructRustWrappingType<JSTemporalPlainDate>( |
| isolate, temporal_rs::PlainDate::from_parsed(*date)); |
| } |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporaldatetime |
| MaybeDirectHandle<JSTemporalPlainDateTime> ToTemporalDateTime( |
| Isolate* isolate, DirectHandle<Object> item, |
| MaybeDirectHandle<Object> options_obj, const char* method_name) { |
| // 1. If options is not present, set options to undefined. |
| // (handled by caller) |
| |
| // This error is eventually thrown by step 3; we perform a check early |
| // so that we can optimize with InstanceType. Step 1 and 2 are unobservable. |
| if (!IsHeapObject(*item)) { |
| THROW_NEW_ERROR( |
| isolate, |
| NEW_TEMPORAL_TYPE_ERROR("DateTime argument must be object or string.")); |
| } |
| InstanceType instance_type = Cast<HeapObject>(*item)->map()->instance_type(); |
| |
| // 2. If item is an Object, then |
| if (InstanceTypeChecker::IsJSReceiver(instance_type)) { |
| auto partial = temporal::kNullPartialDateTime; |
| |
| // a. If item has an [[InitializedTemporalDateTime]] internal slot, then |
| if (InstanceTypeChecker::IsJSTemporalPlainDateTime(instance_type)) { |
| auto cast = Cast<JSTemporalPlainDateTime>(item); |
| // ii. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| READ_AND_DISCARD_OVERFLOW(options_obj); |
| // iii. Return !CreateTemporalDate(item.[[Date]], item.[[Calendar]]). |
| return ConstructRustWrappingType<JSTemporalPlainDateTime>( |
| isolate, cast->wrapped_rust()->clone()); |
| // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, |
| // then |
| } else if (InstanceTypeChecker::IsJSTemporalZonedDateTime(instance_type)) { |
| // i. Let isoDateTime be GetISODateTimeFor(item.[[TimeZone]], |
| // item.[[EpochNanoseconds]]). |
| // |
| // iv. Return !CreateTemporalDateTime(isoDateTime, item.[[Calendar]]). |
| partial = GetPartialDateTime(Cast<JSTemporalZonedDateTime>(item)); |
| // c. If item has an [[InitializedTemporalDate]] internal slot, then |
| } else if (InstanceTypeChecker::IsJSTemporalPlainDate(instance_type)) { |
| // iii. Return !CreateTemporalDate(item.[[ISODateTime]].[[ISODate]], |
| // item.[[Calendar]]). |
| partial = GetPartialDateTime(Cast<JSTemporalPlainDate>(item)); |
| } else { |
| // d. Let calendar be ?GetTemporalCalendarIdentifierWithISODefault(item). |
| temporal_rs::AnyCalendarKind kind = temporal_rs::AnyCalendarKind::Iso; |
| DirectHandle<JSReceiver> item_recvr = Cast<JSReceiver>(item); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, kind, |
| temporal::GetTemporalCalendarIdentifierWithISODefault(isolate, |
| item_recvr)); |
| |
| // e. Let fields be ?PrepareCalendarFields(calendar, item, « year, month, |
| // month-code, day», « hour, minute, second, millisecond, microsecond, |
| // nanosecond», «»). |
| CombinedRecord fields; |
| using enum CalendarFieldsFlag; |
| |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, fields, |
| PrepareCalendarFields(isolate, kind, item_recvr, |
| kAllDateFlags | kTimeFields, |
| RequiredFields::kNone)); |
| |
| temporal_rs::ArithmeticOverflow overflow; |
| // f. Let resolvedOptions be ?GetOptionsObject(options). |
| // g. f. Let overflow be ? GetTemporalOverflowOption(resolvedOptions). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, overflow, |
| temporal::ToTemporalOverflowHandleUndefined( |
| isolate, options_obj, method_name)); |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, partial, |
| fields.Regulate<temporal_rs::PartialDateTime>(isolate, overflow)); |
| return ConstructRustWrappingType<JSTemporalPlainDateTime>( |
| isolate, temporal_rs::PlainDateTime::from_partial(partial, overflow)); |
| } |
| |
| temporal_rs::ArithmeticOverflow overflow; |
| // f. Let resolvedOptions be ?GetOptionsObject(options). |
| // g. f. Let overflow be ? GetTemporalOverflowOption(resolvedOptions). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, overflow, |
| temporal::ToTemporalOverflowHandleUndefined( |
| isolate, options_obj, method_name)); |
| |
| return ConstructRustWrappingType<JSTemporalPlainDateTime>( |
| isolate, temporal_rs::PlainDateTime::from_partial(partial, overflow)); |
| |
| } else { |
| // 3. If item is not a String, throw a TypeError exception. |
| if (!InstanceTypeChecker::IsString(instance_type)) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_TYPE_ERROR( |
| "DateTime argument must be object or string.")); |
| } |
| DirectHandle<String> str = Cast<String>(item); |
| |
| // Rest of the steps handled in Rust |
| |
| std::unique_ptr<temporal_rs::ParsedDateTime> date; |
| auto rust_result = HandleStringEncodings< |
| TemporalAllocatedResult<temporal_rs::ParsedDateTime>>( |
| isolate, str, |
| [](std::string_view view) |
| -> TemporalAllocatedResult<temporal_rs::ParsedDateTime> { |
| return temporal_rs::ParsedDateTime::from_utf8(view); |
| }, |
| [](std::u16string_view view) |
| -> TemporalAllocatedResult<temporal_rs::ParsedDateTime> { |
| return temporal_rs::ParsedDateTime::from_utf16(view); |
| }); |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, date, ExtractRustResult(isolate, std::move(rust_result))); |
| |
| // 9. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| READ_AND_DISCARD_OVERFLOW(options_obj); |
| |
| return ConstructRustWrappingType<JSTemporalPlainDateTime>( |
| isolate, temporal_rs::PlainDateTime::from_parsed(*date)); |
| } |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporalyearmonth |
| MaybeDirectHandle<JSTemporalPlainYearMonth> ToTemporalYearMonth( |
| Isolate* isolate, DirectHandle<Object> item, |
| MaybeDirectHandle<Object> options_obj, const char* method_name) { |
| // 1. If options is not present, set options to undefined. |
| // (handled by caller) |
| |
| // This error is eventually thrown by step 3; we perform a check early |
| // so that we can optimize with InstanceType. Step 1 and 2 are unobservable. |
| if (!IsHeapObject(*item)) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_TYPE_ERROR( |
| "YearMonth argument must be object or string.")); |
| } |
| InstanceType instance_type = Cast<HeapObject>(*item)->map()->instance_type(); |
| // 2. If item is an Object, then |
| if (InstanceTypeChecker::IsJSReceiver(instance_type)) { |
| // a. If item has an [[InitializedTemporalYearMonth]] internal slot, then |
| if (InstanceTypeChecker::IsJSTemporalPlainYearMonth(instance_type)) { |
| auto cast = Cast<JSTemporalPlainYearMonth>(item); |
| |
| // ii. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| READ_AND_DISCARD_OVERFLOW(options_obj); |
| // iii. Return ! CreateTemporalYearMonth(item.[[ISODate]], |
| // item.[[Calendar]]). |
| return ConstructRustWrappingType<JSTemporalPlainYearMonth>( |
| isolate, cast->wrapped_rust()->clone()); |
| } else { |
| // b. Let calendar be ?GetTemporalCalendarIdentifierWithISODefault(item). |
| DirectHandle<JSReceiver> item_recvr = Cast<JSReceiver>(item); |
| |
| temporal_rs::AnyCalendarKind kind; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, kind, |
| temporal::GetTemporalCalendarIdentifierWithISODefault(isolate, |
| item_recvr)); |
| |
| // c. Let fields be ?PrepareCalendarFields(calendar, item, « year, month, |
| // month-code», «», «»). |
| CombinedRecord fields; |
| |
| using enum CalendarFieldsFlag; |
| |
| MOVE_RETURN_ON_EXCEPTION(isolate, fields, |
| PrepareCalendarFields(isolate, kind, item_recvr, |
| kYearFields | kMonthFields, |
| RequiredFields::kNone)); |
| |
| // e. Let overflow be ? GetTemporalOverflowOption(resolvedOptions). |
| |
| temporal_rs::ArithmeticOverflow overflow; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, overflow, |
| temporal::ToTemporalOverflowHandleUndefined( |
| isolate, options_obj, method_name)); |
| |
| temporal_rs::PartialDate partial = temporal::kNullPartialDate; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, partial, |
| fields.Regulate<temporal_rs::PartialDate>(isolate, overflow)); |
| // f. Let isoDate be ? CalendarYearMonthFromFields(calendar, fields, |
| // overflow). |
| // |
| // g. Return !CreateTemporalYearMonth(isoDate, calendar). |
| return ConstructRustWrappingType<JSTemporalPlainYearMonth>( |
| isolate, |
| temporal_rs::PlainYearMonth::from_partial(partial, overflow)); |
| } |
| } else { |
| // 3. If item is not a String, throw a TypeError exception. |
| if (!InstanceTypeChecker::IsString(instance_type)) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_TYPE_ERROR( |
| "YearMonth argument must be object or string.")); |
| } |
| DirectHandle<String> str = Cast<String>(item); |
| |
| // Rest of the steps handled in Rust |
| |
| std::unique_ptr<temporal_rs::ParsedDate> date; |
| auto rust_result = |
| HandleStringEncodings<TemporalAllocatedResult<temporal_rs::ParsedDate>>( |
| isolate, str, |
| [](std::string_view view) |
| -> TemporalAllocatedResult<temporal_rs::ParsedDate> { |
| return temporal_rs::ParsedDate::year_month_from_utf8(view); |
| }, |
| [](std::u16string_view view) |
| -> TemporalAllocatedResult<temporal_rs::ParsedDate> { |
| return temporal_rs::ParsedDate::year_month_from_utf16(view); |
| }); |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, date, ExtractRustResult(isolate, std::move(rust_result))); |
| |
| // 9. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| READ_AND_DISCARD_OVERFLOW(options_obj); |
| |
| return ConstructRustWrappingType<JSTemporalPlainYearMonth>( |
| isolate, temporal_rs::PlainYearMonth::from_parsed(*date)); |
| } |
| } |
| |
| struct ZDTOptions { |
| temporal_rs::Disambiguation disambiguation; |
| temporal_rs::OffsetDisambiguation offset_option; |
| temporal_rs::ArithmeticOverflow overflow; |
| }; |
| |
| Maybe<ZDTOptions> GetZDTOptions(Isolate* isolate, |
| MaybeDirectHandle<Object> maybe_options_obj, |
| const char* method_name) { |
| ZDTOptions options = |
| ZDTOptions{.disambiguation = temporal_rs::Disambiguation::Compatible, |
| .offset_option = temporal_rs::OffsetDisambiguation::Reject, |
| .overflow = temporal_rs::ArithmeticOverflow::Constrain}; |
| |
| DirectHandle<Object> options_obj; |
| |
| if (!maybe_options_obj.ToHandle(&options_obj)) { |
| return Just(options); |
| } |
| // (ToTemporalZonedDateTime) g. Let resolvedOptions be |
| // ?GetOptionsObject(options). |
| // |
| // (ToTemporalZonedDateTime) h. Let disambiguation be |
| // ?GetTemporalDisambiguationOption(resolvedOptions). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options.disambiguation, |
| temporal::GetTemporalDisambiguationOptionHandleUndefined( |
| isolate, options_obj, method_name)); |
| |
| // (ToTemporalZonedDateTime) i. Let offsetOption be |
| // ?GetTemporalOffsetOption(resolvedOptions, reject). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options.offset_option, |
| temporal::GetTemporalOffsetOptionHandleUndefined( |
| isolate, options_obj, temporal_rs::OffsetDisambiguation::Reject, |
| method_name)); |
| // (ToTemporalZonedDateTime) ii. Perform |
| // ?GetTemporalOverflowOption(resolvedOptions). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, options.overflow, |
| temporal::ToTemporalOverflowHandleUndefined( |
| isolate, options_obj, method_name)); |
| |
| return Just(options); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporalzoneddatetime |
| // Note this skips the options-parsing steps and instead asks the caller to pass |
| // it in |
| MaybeDirectHandle<JSTemporalZonedDateTime> ToTemporalZonedDateTime( |
| Isolate* isolate, DirectHandle<Object> item, |
| MaybeDirectHandle<Object> options_obj, const char* method_name) { |
| // 1. If options is not present, set options to undefined. |
| // (handled by caller) |
| |
| // This error is eventually thrown by step 3; we perform a check early |
| // so that we can optimize with InstanceType. Step 1 and 2 are unobservable. |
| if (!IsHeapObject(*item)) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_TYPE_ERROR( |
| "ZonedDateTime argument must be object or string.")); |
| } |
| InstanceType instance_type = Cast<HeapObject>(*item)->map()->instance_type(); |
| |
| // 2. Let offsetBehaviour be option. |
| // 3. Let matchBehaviour be match-exactly. |
| // (handled in Rust) |
| |
| // 4. If item is an Object, then |
| if (InstanceTypeChecker::IsJSReceiver(instance_type)) { |
| // a. If item has an [[InitializedTemporalZonedDateTime]] internal slot, |
| // then |
| if (InstanceTypeChecker::IsJSTemporalZonedDateTime(instance_type)) { |
| auto cast = Cast<JSTemporalZonedDateTime>(item); |
| |
| // iii. Perform ? GetTemporalDisambiguationOption(resolvedOptions). |
| // iv. Perform ? GetTemporalOffsetOption(resolvedOptions, reject). |
| // v. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| ZDTOptions options; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetZDTOptions(isolate, options_obj, method_name)); |
| // vi. Return !CreateTemporalZonedDateTime(item.[[EpochNanoseconds]], |
| // item.[[TimeZone]], item.[[Calendar]]). |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>( |
| isolate, cast->wrapped_rust()->clone()); |
| |
| } else { |
| // b. Let calendar be ?GetTemporalCalendarIdentifierWithISODefault(item). |
| temporal_rs::AnyCalendarKind kind = temporal_rs::AnyCalendarKind::Iso; |
| DirectHandle<JSReceiver> item_recvr = Cast<JSReceiver>(item); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, kind, |
| temporal::GetTemporalCalendarIdentifierWithISODefault(isolate, |
| item_recvr)); |
| |
| // c. Let fields be ? PrepareCalendarFields(calendar, item, « year, |
| // month, month-code, day», « hour, minute, second, millisecond, |
| // microsecond, nanosecond, offset, time-zone», « time-zone»). |
| CombinedRecord fields; |
| using enum CalendarFieldsFlag; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, fields, |
| PrepareCalendarFields( |
| isolate, kind, item_recvr, |
| kAllDateFlags | kTimeFields | kOffset | kTimeZone, |
| RequiredFields::kTimeZone)); |
| |
| // h. Perform ? GetTemporalDisambiguationOption(resolvedOptions). |
| // i. Perform ? GetTemporalOffsetOption(resolvedOptions, reject). |
| // j. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| ZDTOptions options; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetZDTOptions(isolate, options_obj, method_name)); |
| |
| temporal_rs::PartialZonedDateTime partial = kNullPartialZonedDateTime; |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, partial, |
| fields.Regulate<temporal_rs::PartialZonedDateTime>(isolate, |
| options.overflow)); |
| |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>( |
| isolate, temporal_rs::ZonedDateTime::from_partial_with_provider( |
| partial, options.overflow, options.disambiguation, |
| options.offset_option, TimeZoneProvider())); |
| } |
| |
| } else { |
| // 3. If item is not a String, throw a TypeError exception. |
| if (!InstanceTypeChecker::IsString(instance_type)) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_TYPE_ERROR( |
| "ZonedDateTime argument must be object or string.")); |
| } |
| DirectHandle<String> str = Cast<String>(item); |
| |
| // b. Let result be ? ParseISODateTime(item, « |
| // TemporalDateTimeString[+Zoned] »). |
| // |
| // Steps b-l handled in Rust |
| std::unique_ptr<temporal_rs::ParsedZonedDateTime> parsed; |
| |
| auto rust_result = HandleStringEncodings< |
| TemporalAllocatedResult<temporal_rs::ParsedZonedDateTime>>( |
| isolate, str, |
| [](std::string_view view) |
| -> TemporalAllocatedResult<temporal_rs::ParsedZonedDateTime> { |
| return temporal_rs::ParsedZonedDateTime::from_utf8_with_provider( |
| view, TimeZoneProvider()); |
| }, |
| [](std::u16string_view view) |
| -> TemporalAllocatedResult<temporal_rs::ParsedZonedDateTime> { |
| return temporal_rs::ParsedZonedDateTime::from_utf16_with_provider( |
| view, TimeZoneProvider()); |
| }); |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, parsed, ExtractRustResult(isolate, std::move(rust_result))); |
| |
| // o. Perform ? GetTemporalDisambiguationOption(resolvedOptions). |
| // p. Perform ? GetTemporalOffsetOption(resolvedOptions, reject). |
| // q. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| ZDTOptions options; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetZDTOptions(isolate, options_obj, method_name)); |
| |
| // Rest of the steps handled in Rust |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>( |
| isolate, temporal_rs::ZonedDateTime::from_parsed_with_provider( |
| *parsed, options.disambiguation, options.offset_option, |
| TimeZoneProvider())); |
| } |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-totemporalmonthday |
| MaybeDirectHandle<JSTemporalPlainMonthDay> ToTemporalMonthDay( |
| Isolate* isolate, DirectHandle<Object> item, |
| MaybeDirectHandle<Object> options_obj, const char* method_name) { |
| // 1. If options is not present, set options to undefined. |
| // (handled by caller) |
| |
| // This error is eventually thrown by step 3; we perform a check early |
| // so that we can optimize with InstanceType. Step 1 and 2 are unobservable. |
| if (!IsHeapObject(*item)) { |
| THROW_NEW_ERROR( |
| isolate, |
| NEW_TEMPORAL_TYPE_ERROR("MonthDay argument must be object or string.")); |
| } |
| InstanceType instance_type = Cast<HeapObject>(*item)->map()->instance_type(); |
| // 2. If item is an Object, then |
| if (InstanceTypeChecker::IsJSReceiver(instance_type)) { |
| // a. If item has an [[InitializedTemporalMonthDay]] internal slot, then |
| if (InstanceTypeChecker::IsJSTemporalPlainMonthDay(instance_type)) { |
| auto cast = Cast<JSTemporalPlainMonthDay>(item); |
| |
| // ii. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| READ_AND_DISCARD_OVERFLOW(options_obj); |
| |
| // iii. Return ! CreateTemporalMonthDay(item.[[ISODate]], |
| // item.[[Calendar]]). |
| return ConstructRustWrappingType<JSTemporalPlainMonthDay>( |
| isolate, cast->wrapped_rust()->clone()); |
| } else { |
| // b. Let calendar be ?GetTemporalCalendarIdentifierWithISODefault(item). |
| DirectHandle<JSReceiver> item_recvr = Cast<JSReceiver>(item); |
| |
| temporal_rs::AnyCalendarKind kind; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, kind, |
| temporal::GetTemporalCalendarIdentifierWithISODefault(isolate, |
| item_recvr)); |
| |
| // c. Let fields be ?PrepareCalendarFields(calendar, item, « year, month, |
| // month-code, day», «», «»). |
| CombinedRecord fields; |
| |
| using enum CalendarFieldsFlag; |
| |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, fields, |
| PrepareCalendarFields(isolate, kind, item_recvr, |
| kYearFields | kMonthFields | kDay, |
| RequiredFields::kNone)); |
| |
| // Remaining steps handled in Rust |
| |
| // e. Let overflow be ? GetTemporalOverflowOption(resolvedOptions). |
| |
| temporal_rs::ArithmeticOverflow overflow; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, overflow, |
| temporal::ToTemporalOverflowHandleUndefined( |
| isolate, options_obj, method_name)); |
| // f. Let isoDate be ? CalendarMonthDayFromFields(calendar, fields, |
| // overflow). |
| // |
| // g. Return ! CreateTemporalMonthDay(isoDate, calendar). |
| temporal_rs::PartialDate partial = temporal::kNullPartialDate; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, partial, |
| fields.Regulate<temporal_rs::PartialDate>(isolate, overflow)); |
| |
| return ConstructRustWrappingType<JSTemporalPlainMonthDay>( |
| isolate, temporal_rs::PlainMonthDay::from_partial(partial, overflow)); |
| } |
| } else { |
| // 3. If item is not a String, throw a TypeError exception. |
| if (!InstanceTypeChecker::IsString(instance_type)) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_TYPE_ERROR( |
| "MonthDay argument must be object or string.")); |
| } |
| DirectHandle<String> str = Cast<String>(item); |
| DirectHandle<JSTemporalPlainDate> result; |
| |
| // Rest of the steps handled in Rust |
| |
| std::unique_ptr<temporal_rs::ParsedDate> date; |
| auto rust_result = |
| HandleStringEncodings<TemporalAllocatedResult<temporal_rs::ParsedDate>>( |
| isolate, str, |
| [](std::string_view view) |
| -> TemporalAllocatedResult<temporal_rs::ParsedDate> { |
| return temporal_rs::ParsedDate::month_day_from_utf8(view); |
| }, |
| [](std::u16string_view view) |
| -> TemporalAllocatedResult<temporal_rs::ParsedDate> { |
| return temporal_rs::ParsedDate::month_day_from_utf16(view); |
| }); |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, date, ExtractRustResult(isolate, std::move(rust_result))); |
| |
| // 9. Perform ? GetTemporalOverflowOption(resolvedOptions). |
| READ_AND_DISCARD_OVERFLOW(options_obj); |
| |
| return ConstructRustWrappingType<JSTemporalPlainMonthDay>( |
| isolate, temporal_rs::PlainMonthDay::from_parsed(*date)); |
| } |
| } |
| |
| // For calling ToTemporalFoo in generic contexts. |
| // Note that this ignores all options `overflow`. |
| template <typename JSType> |
| MaybeDirectHandle<JSType> ToTemporalGeneric( |
| Isolate* isolate, DirectHandle<Object> item, |
| const char* method_name); |
| |
| #define DEFINE_TO_TEMPORAL_GENERIC(Type) \ |
| template <> \ |
| MaybeDirectHandle<JSTemporalPlain##Type> \ |
| ToTemporalGeneric<JSTemporalPlain##Type>( \ |
| Isolate * isolate, DirectHandle<Object> item, const char* method_name) { \ |
| return ToTemporal##Type(isolate, item, {}, method_name); \ |
| } |
| |
| DEFINE_TO_TEMPORAL_GENERIC(Time) |
| DEFINE_TO_TEMPORAL_GENERIC(Date) |
| DEFINE_TO_TEMPORAL_GENERIC(DateTime) |
| DEFINE_TO_TEMPORAL_GENERIC(YearMonth) |
| DEFINE_TO_TEMPORAL_GENERIC(MonthDay) |
| |
| #undef DEFINE_TO_TEMPORAL_GENERIC |
| |
| // These two have a different number of arguments |
| template <> |
| MaybeDirectHandle<JSTemporalInstant> ToTemporalGeneric<JSTemporalInstant>( |
| Isolate* isolate, DirectHandle<Object> item, |
| const char* method_name) { |
| return ToTemporalInstant(isolate, item, method_name); |
| } |
| |
| template <> |
| MaybeDirectHandle<JSTemporalZonedDateTime> |
| ToTemporalGeneric<JSTemporalZonedDateTime>( |
| Isolate* isolate, DirectHandle<Object> item, |
| const char* method_name) { |
| return ToTemporalZonedDateTime(isolate, item, {}, method_name); |
| } |
| |
| // A class that wraps a PlainDate or a ZonedDateTime that allows |
| // them to be either owned in a unique_ptr or borrowed. |
| // |
| // Setters should only be called once (this is not a safety |
| // invariant, but the spec should not be setting things multiple |
| // times) |
| class RelativeTo { |
| public: |
| RelativeTo() = default; |
| |
| explicit RelativeTo(std::shared_ptr<const temporal_rs::PlainDate> val) |
| : date_(std::move(val)), zoned_() {} |
| explicit RelativeTo(std::shared_ptr<const temporal_rs::ZonedDateTime> val) |
| : date_(), zoned_(std::move(val)) {} |
| |
| explicit RelativeTo(temporal_rs::OwnedRelativeTo&& owned) { |
| date_ = std::move(owned.date); |
| zoned_ = std::move(owned.zoned); |
| } |
| |
| temporal_rs::RelativeTo ToRust() const V8_LIFETIME_BOUND { |
| return temporal_rs::RelativeTo{ |
| .date = date_.get(), |
| .zoned = zoned_.get(), |
| }; |
| } |
| |
| private: |
| std::shared_ptr<const temporal_rs::PlainDate> date_; |
| std::shared_ptr<const temporal_rs::ZonedDateTime> zoned_; |
| }; |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-gettemporalrelativetooption |
| // Also handles the undefined case from GetOptionsObject |
| Maybe<RelativeTo> GetTemporalRelativeToOptionHandleUndefined( |
| Isolate* isolate, DirectHandle<Object> options) { |
| RelativeTo ret; |
| |
| // Default is empty |
| if (IsUndefined(*options)) { |
| return Just(RelativeTo()); |
| } |
| |
| auto relativeto_ident = isolate->factory()->relativeTo_string(); |
| if (!IsJSReceiver(*options)) { |
| // (GetOptionsObject) 3. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR_WITH_ARG( |
| kOptionMustBeObject, relativeto_ident)); |
| } |
| |
| // 1. Let value be ?Get(options, "relativeTo"). |
| DirectHandle<Object> value; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, value, |
| JSReceiver::GetProperty(isolate, Cast<JSReceiver>(options), |
| relativeto_ident)); |
| |
| // 2. If value is undefined, return the Record { [[PlainRelativeTo]]: |
| // undefined, [[ZonedRelativeTo]]: undefined }. |
| if (IsUndefined(*value)) { |
| return Just(RelativeTo()); |
| } |
| |
| // 3. Let offsetBehaviour be option. |
| // 4. Let matchBehaviour be match-exactly. |
| |
| // This error is eventually thrown by step 6a; we perform a check early |
| // so that we can optimize with InstanceType. Step 5-6 are unobservable |
| // in this case. |
| if (!IsHeapObject(*value)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR( |
| "relativeTo must be object or string.")); |
| } |
| InstanceType instance_type = Cast<HeapObject>(*value)->map()->instance_type(); |
| |
| // 5. If value is an Object, then |
| if (InstanceTypeChecker::IsJSReceiver(instance_type)) { |
| // a. If value has an [[InitializedTemporalZonedDateTime]] internal slot, |
| // then |
| if (InstanceTypeChecker::IsJSTemporalZonedDateTime(instance_type)) { |
| // i. Return the Record { [[PlainRelativeTo]]: undefined, |
| // [[ZonedRelativeTo]]: value }. |
| return Just(RelativeTo(Cast<JSTemporalZonedDateTime>(value) |
| ->wrapped_rust() |
| .as_shared_ptr())); |
| } |
| |
| // b. If value has an [[InitializedTemporalDate]] internal slot, then |
| if (InstanceTypeChecker::IsJSTemporalPlainDate(instance_type)) { |
| // i. Return the Record { [[PlainRelativeTo]]: value, [[ZonedRelativeTo]]: |
| // undefined }. |
| return Just(RelativeTo( |
| Cast<JSTemporalPlainDate>(value)->wrapped_rust().as_shared_ptr())); |
| } |
| |
| // c. If value has an [[InitializedTemporalDateTime]] internal slot, then |
| if (InstanceTypeChecker::IsJSTemporalPlainDateTime(instance_type)) { |
| // i. Let plainDate be |
| // !CreateTemporalDate(value.[[ISODateTime]].[[ISODate]], |
| // value.[[Calendar]]). |
| auto date_record = GetPartialDate(Cast<JSTemporalPlainDateTime>(value)); |
| std::unique_ptr<temporal_rs::PlainDate> plain_date = nullptr; |
| |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, plain_date, |
| ExtractRustResult(isolate, temporal_rs::PlainDate::from_partial( |
| date_record, std::nullopt))); |
| // ii. Return the Record { [[PlainRelativeTo]]: plainDate, |
| // [[ZonedRelativeTo]]: undefined }. |
| return Just(RelativeTo(std::move(plain_date))); |
| } |
| // d. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(value). |
| temporal_rs::AnyCalendarKind kind = temporal_rs::AnyCalendarKind::Iso; |
| auto value_recvr = Cast<JSReceiver>(value); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, kind, |
| temporal::GetTemporalCalendarIdentifierWithISODefault(isolate, |
| value_recvr)); |
| // e. Let fields be ? PrepareCalendarFields(calendar, value, « year, month, |
| // month-code, day », « hour, minute, second, millisecond, microsecond, |
| // nanosecond, offset, time-zone », «»). |
| CombinedRecord fields; |
| |
| using enum CalendarFieldsFlag; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, fields, |
| PrepareCalendarFields(isolate, kind, value_recvr, |
| kAllDateFlags | kTimeFields | kOffset | kTimeZone, |
| RequiredFields::kNone)); |
| |
| auto partial = kNullPartialZonedDateTime; |
| |
| // f. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, |
| // constrain). |
| // g. Let timeZone be fields.[[TimeZone]]. |
| // h. Let offsetString be fields.[[OffsetString]]. |
| // j. Let isoDate be result.[[ISODate]]. |
| // k. Let time be result.[[Time]]. |
| auto overflow = temporal_rs::ArithmeticOverflow::Constrain; |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, partial, |
| fields.Regulate<temporal_rs::PartialZonedDateTime>(isolate, overflow)); |
| |
| // We use different construction methods for ZonedDateTime in these two |
| // branches, so we've pulled steps 7-12 into this branch |
| |
| // 7. If timeZone is unset, then |
| if (!partial.timezone) { |
| // a. Let plainDate be ? CreateTemporalDate(isoDate, calendar). |
| std::unique_ptr<temporal_rs::PlainDate> plain_relative_to; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, plain_relative_to, |
| ExtractRustResult(isolate, temporal_rs::PlainDate::from_partial( |
| partial.date, overflow))); |
| |
| // b. Return the Record { [[PlainRelativeTo]]: plainDate, |
| // [[ZonedRelativeTo]]: undefined }. |
| return Just(RelativeTo(std::move(plain_relative_to))); |
| } |
| // 8. If offsetBehaviour is option, then |
| // a. Let offsetNs be ! ParseDateTimeUTCOffset(offsetString). |
| // 9. Else, |
| // a. Let offsetNs be 0. |
| // 10. Let epochNanoseconds be ? InterpretISODateTimeOffset(isoDate, time, |
| // offsetBehaviour, offsetNs, timeZone, compatible, reject, matchBehaviour). |
| |
| // 11. Let zonedRelativeTo be ! |
| // CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar). |
| |
| // (All these steps handled in Rust) |
| |
| std::unique_ptr<temporal_rs::ZonedDateTime> zoned_relative_to; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, zoned_relative_to, |
| ExtractRustResult( |
| isolate, |
| temporal_rs::ZonedDateTime::from_partial_with_provider( |
| partial, overflow, temporal_rs::Disambiguation::Compatible, |
| temporal_rs::OffsetDisambiguation::Reject, |
| TimeZoneProvider()))); |
| // 12. Return the Record { [[PlainRelativeTo]]: undefined, |
| // [[ZonedRelativeTo]]: zonedRelativeTo }. |
| return Just(RelativeTo(std::move(zoned_relative_to))); |
| |
| // 6. Else, |
| } else { |
| // a. If value is not a String, throw a TypeError exception. |
| if (!InstanceTypeChecker::IsString(instance_type)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR( |
| "relativeTo must be object or string.")); |
| } |
| |
| DirectHandle<String> str = Cast<String>(value); |
| |
| // b. Let result be ? ParseISODateTime(value, « |
| // TemporalDateTimeString[+Zoned], TemporalDateTimeString[~Zoned] »). Rest |
| // of the steps handled in Rust |
| temporal_rs::OwnedRelativeTo relative_to; |
| |
| auto rust_result = |
| HandleStringEncodings<TemporalResult<temporal_rs::OwnedRelativeTo>>( |
| isolate, str, |
| [](std::string_view view) |
| -> TemporalResult<temporal_rs::OwnedRelativeTo> { |
| return temporal_rs::OwnedRelativeTo::from_utf8_with_provider( |
| view, TimeZoneProvider()); |
| }, |
| [](std::u16string_view view) |
| -> TemporalResult<temporal_rs::OwnedRelativeTo> { |
| return temporal_rs::OwnedRelativeTo::from_utf16_with_provider( |
| view, TimeZoneProvider()); |
| }); |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, relative_to, |
| ExtractRustResult(isolate, std::move(rust_result))); |
| |
| // 12. Return the Record { [[PlainRelativeTo]]: undefined, |
| // [[ZonedRelativeTo]]: zonedRelativeTo }. |
| return Just(RelativeTo(std::move(relative_to))); |
| } |
| } |
| |
| // ====== Difference operations ====== |
| |
| // ProviderArg is only for wrapping temporal_rs::Provider& |
| template <typename RustType, typename... ProviderArg> |
| using DifferenceOperation = TemporalAllocatedResult<temporal_rs::Duration> ( |
| RustType::*)(const RustType&, temporal_rs::DifferenceSettings, |
| const ProviderArg&...) const; |
| |
| // Generic function for all DifferenceTemporalFoo operations |
| // |
| // https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant |
| // https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaindate |
| // https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaindatetime |
| // https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplainyearmonth |
| // https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalzoneddatetime |
| template <typename JSType, typename... ProviderArg> |
| MaybeDirectHandle<JSTemporalDuration> GenericDifferenceTemporal( |
| Isolate* isolate, |
| DifferenceOperation<typename JSType::RustType, ProviderArg...> operation, |
| UnitGroup group, Unit fallback_smallest_unit, DirectHandle<JSType> handle, |
| DirectHandle<Object> other_obj, DirectHandle<Object> options, |
| const char* method_name, const ProviderArg&... provider) { |
| // Steps are written for PlainDate, but are similar for other types |
| |
| // 1. Set other to ?ToTemporalDate(other). |
| DirectHandle<JSType> other; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, other, |
| temporal::ToTemporalGeneric<JSType>(isolate, other_obj, method_name)); |
| |
| auto settings = temporal_rs::DifferenceSettings{.largest_unit = std::nullopt, |
| .smallest_unit = std::nullopt, |
| .rounding_mode = std::nullopt, |
| .increment = std::nullopt}; |
| |
| auto this_rust = handle->wrapped_rust(); |
| auto other_rust = other->wrapped_rust(); |
| |
| // This step only exists for calendared types (everything other than PlainTime |
| // and Instant) |
| if constexpr (JSType::kTypeContainsCalendar) { |
| // 2. If CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]) is |
| // false, throw a RangeError exception. |
| if (this_rust->calendar().kind() != other_rust->calendar().kind()) { |
| THROW_NEW_ERROR(isolate, |
| NewRangeError(MessageTemplate::kMismatchedCalendars)); |
| } |
| } |
| |
| // 3. Let resolvedOptions be ?GetOptionsObject(options). |
| // 4. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, |
| // date, « », day, day). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, settings, |
| temporal::GetDifferenceSettingsWithoutChecks( |
| isolate, options, group, fallback_smallest_unit, method_name)); |
| |
| // Remaining steps handled by temporal_rs |
| // operation negation (step 6) is also handled in temporal_rs, we should not |
| // negate again here. |
| |
| auto diff = (this_rust.raw()->*operation)(*other_rust, settings, provider...); |
| |
| return ConstructRustWrappingType<JSTemporalDuration>(isolate, |
| std::move(diff)); |
| } |
| |
| // ====== Add operations ====== |
| |
| // Wrapping Rust-side ::add and ::subtract methods |
| // OverflowArgument is generic since some of these |
| // methods take std::optional. |
| // |
| // ProviderArg is only for wrapping temporal_rs::Provider& |
| template <typename RustType, typename OverflowArgument, typename... ProviderArg> |
| using BinaryOperation = TemporalAllocatedResult<RustType> (RustType::*)( |
| const temporal_rs::Duration&, OverflowArgument, |
| const ProviderArg&...) const; |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-adddurationtodate |
| // https://tc39.es/proposal-temporal/#sec-temporal-adddurationtodatetime |
| // https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoyearmonth |
| // https://tc39.es/proposal-temporal/#sec-temporal-adddurationtozoneddatetime |
| template < |
| typename JSType, |
| typename OverflowArgument = std::optional<temporal_rs::ArithmeticOverflow>, |
| typename... ProviderArg> |
| MaybeDirectHandle<JSType> AddDurationToGeneric( |
| Isolate* isolate, |
| BinaryOperation<typename JSType::RustType, OverflowArgument, ProviderArg...> |
| operation, |
| DirectHandle<JSType> temporal_js_type, |
| DirectHandle<Object> temporal_duration_like, |
| DirectHandle<Object> options_obj, const char* method_name, |
| const ProviderArg&... provider) { |
| // 1. Let duration be ? ToTemporalDuration(temporalDurationLike). |
| DirectHandle<JSTemporalDuration> other_duration; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, other_duration, |
| temporal::ToTemporalDuration( |
| isolate, temporal_duration_like, method_name)); |
| |
| // 2. If operation is subtract, set duration to |
| // CreateNegatedTemporalDuration(duration). |
| |
| // (handled by Rust, we should not double-negate) |
| |
| // 3. Let resolvedOptions be ?GetOptionsObject(options). |
| // 4. Let overflow be ?GetTemporalOverflowOption(resolvedOptions). |
| temporal_rs::ArithmeticOverflow overflow; |
| |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, overflow, |
| temporal::ToTemporalOverflowHandleUndefined( |
| isolate, options_obj, method_name)); |
| |
| auto rust_object = temporal_js_type->wrapped_rust(); |
| // Remaining steps handled in Rust. |
| auto added = (rust_object.raw()->*operation)(*other_duration->wrapped_rust(), |
| overflow, provider...); |
| |
| return ConstructRustWrappingType<JSType>(isolate, std::move(added)); |
| } |
| // ====== .with() methods ====== |
| |
| // Helper for GenericWith: extract relevant options from options_obj, call |
| // .with() on Rust type. |
| // |
| // Steps 8 and later from |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.with |
| template <typename JSType, typename PartialType> |
| MaybeDirectHandle<JSType> GenericWithHelper( |
| Isolate* isolate, |
| const typename CppGCManaged<typename JSType::RustType>::Ptr& rust_object, |
| CombinedRecord& fields, DirectHandle<Object> options_obj, |
| const char* method_name) { |
| // 8. Let resolvedOptions be ? GetOptionsObject(options). |
| // 9. Let overflow be ? GetTemporalOverflowOption(resolvedOptions). |
| temporal_rs::ArithmeticOverflow overflow; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, overflow, |
| ToTemporalOverflowHandleUndefined(isolate, options_obj, method_name)); |
| |
| PartialType partial; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, partial, |
| fields.Regulate<PartialType>(isolate, overflow)); |
| // Rest handled by Rust. |
| return ConstructRustWrappingType<JSType>( |
| isolate, rust_object->with(partial, overflow)); |
| } |
| |
| // ZonedDateTime needs to extract extra options |
| // |
| // Steps 19 and later in |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.with |
| template <> |
| MaybeDirectHandle<JSTemporalZonedDateTime> |
| GenericWithHelper<JSTemporalZonedDateTime, temporal_rs::PartialZonedDateTime>( |
| Isolate* isolate, |
| const CppGCManaged<JSTemporalZonedDateTime::RustType>::Ptr& rust_object, |
| CombinedRecord& fields, DirectHandle<Object> options_obj, |
| const char* method_name) { |
| // 19. Let resolvedOptions be ? GetOptionsObject(options). |
| |
| // (Not explicitly needed since we use HandleUndefined methods) |
| |
| temporal_rs::Disambiguation disambiguation; |
| temporal_rs::OffsetDisambiguation offset_option; |
| temporal_rs::ArithmeticOverflow overflow; |
| |
| // 20. Let disambiguation be |
| // ? GetTemporalDisambiguationOption(resolvedOptions). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, disambiguation, |
| temporal::GetTemporalDisambiguationOptionHandleUndefined( |
| isolate, options_obj, method_name)); |
| // 21. Let offset be ? GetTemporalOffsetOption(resolvedOptions, prefer). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, offset_option, |
| temporal::GetTemporalOffsetOptionHandleUndefined( |
| isolate, options_obj, temporal_rs::OffsetDisambiguation::Prefer, |
| method_name)); |
| // 22. Let overflow be ? GetTemporalOverflowOption(resolvedOptions). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, overflow, |
| ToTemporalOverflowHandleUndefined(isolate, options_obj, method_name)); |
| |
| temporal_rs::PartialZonedDateTime partial = kNullPartialZonedDateTime; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, partial, |
| fields.Regulate<temporal_rs::PartialZonedDateTime>(isolate, overflow)); |
| // Rest handled by Rust. |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>( |
| isolate, |
| rust_object->with_with_provider(partial, disambiguation, offset_option, |
| overflow, TimeZoneProvider())); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.with |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.with |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.with |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.with |
| // |
| // The function has comments for PlainDate, but the non-Rust-side code for the |
| // others is identical. |
| template <typename JSType, typename PartialType> |
| MaybeDirectHandle<JSType> GenericWith(Isolate* isolate, |
| DirectHandle<JSType> this_obj, |
| DirectHandle<Object> temporal_like_obj, |
| DirectHandle<Object> options_obj, |
| CalendarFieldsFlags flags, |
| const char* method_name) { |
| // 1. Let temporalDate be the this value. |
| // 2. Perform ? RequireInternalSlot(temporalDate, |
| // [[InitializedTemporalDate]]). |
| |
| // (handled by type system) |
| |
| // 3. If ? IsPartialTemporalObject(temporalDateLike) is false, throw a |
| // TypeError exception. |
| |
| bool is_partial = false; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, is_partial, IsPartialTemporalObject(isolate, temporal_like_obj)); |
| if (!is_partial) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kWithNoPartial)); |
| } |
| // IsPartialTemporalObject only returns true for valid JSReceivers. |
| auto options_recvr = Cast<JSReceiver>(temporal_like_obj); |
| |
| auto rust_object = this_obj->wrapped_rust(); |
| // 4. Let calendar be temporalDate.[[Calendar]]. |
| auto kind = rust_object->calendar().kind(); |
| |
| // 5. Let fields be ISODateToFields(calendar, temporalDate.[[ISODate]], date). |
| |
| // (handled in Rust code, not observable) |
| |
| // 6. Let partialDate be ? PrepareCalendarFields(calendar, temporalDateLike, « |
| // year, month, month-code, day », « », partial). |
| |
| CombinedRecord fields; |
| |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, fields, |
| PrepareCalendarFields(isolate, kind, options_recvr, flags, |
| RequiredFields::kPartial)); |
| |
| // 7. Set fields to CalendarMergeFields(calendar, fields, partialDate). |
| |
| // (handled in Rust code, not observable) |
| |
| // Fetching options handled by GenericWithHelper. |
| // Remaining steps handled by Rust code called by GenericWithHelper. |
| |
| return GenericWithHelper<JSType, PartialType>(isolate, rust_object, fields, |
| options_obj, method_name); |
| } |
| |
| // ====== Misc ====== |
| |
| V8_WARN_UNUSED_RESULT Maybe<temporal_rs::TimeZone> ToRustTimeZone( |
| Isolate* isolate, std::string_view tz) { |
| return ExtractRustResult(isolate, |
| temporal_rs::TimeZone::try_from_str_with_provider( |
| tz, TimeZoneProvider())); |
| } |
| |
| } // namespace temporal |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalDuration::Constructor( |
| Isolate* isolate, DirectHandle<JSFunction> target, |
| DirectHandle<HeapObject> new_target, DirectHandle<Object> years, |
| DirectHandle<Object> months, DirectHandle<Object> weeks, |
| DirectHandle<Object> days, DirectHandle<Object> hours, |
| DirectHandle<Object> minutes, DirectHandle<Object> seconds, |
| DirectHandle<Object> milliseconds, DirectHandle<Object> microseconds, |
| DirectHandle<Object> nanoseconds) { |
| // 1. If NewTarget is undefined, then |
| if (IsUndefined(*new_target)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, |
| isolate->factory()->NewStringFromAsciiChecked( |
| "Temporal.Duration"))); |
| } |
| // 2. If years is undefined, let y be 0; else let y be |
| // ? ToIntegerIfIntegral(years). |
| int64_t y = 0; |
| if (!IsUndefined(*years)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, y, temporal::ToIntegerTypeIfIntegral<int64_t>(isolate, years)); |
| } |
| |
| // 3. If months is undefined, let mo be 0; else let mo be |
| // ? ToIntegerIfIntegral(months). |
| int64_t mo = 0; |
| if (!IsUndefined(*months)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, mo, |
| temporal::ToIntegerTypeIfIntegral<int64_t>(isolate, months)); |
| } |
| |
| // 4. If weeks is undefined, let w be 0; else let w be |
| // ? ToIntegerIfIntegral(weeks). |
| int64_t w = 0; |
| if (!IsUndefined(*weeks)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, w, temporal::ToIntegerTypeIfIntegral<int64_t>(isolate, weeks)); |
| } |
| |
| // 5. If days is undefined, let d be 0; else let d be |
| // ? ToIntegerIfIntegral(days). |
| int64_t d = 0; |
| if (!IsUndefined(*days)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, d, temporal::ToIntegerTypeIfIntegral<int64_t>(isolate, days)); |
| } |
| |
| // 6. If hours is undefined, let h be 0; else let h be |
| // ? ToIntegerIfIntegral(hours). |
| int64_t h = 0; |
| if (!IsUndefined(*hours)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, h, temporal::ToIntegerTypeIfIntegral<int64_t>(isolate, hours)); |
| } |
| |
| // 7. If minutes is undefined, let m be 0; else let m be |
| // ? ToIntegerIfIntegral(minutes). |
| int64_t m = 0; |
| if (!IsUndefined(*minutes)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, m, |
| temporal::ToIntegerTypeIfIntegral<int64_t>(isolate, minutes)); |
| } |
| |
| // 8. If seconds is undefined, let s be 0; else let s be |
| // ? ToIntegerIfIntegral(seconds). |
| int64_t s = 0; |
| if (!IsUndefined(*seconds)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, s, |
| temporal::ToIntegerTypeIfIntegral<int64_t>(isolate, seconds)); |
| } |
| |
| // 9. If milliseconds is undefined, let ms be 0; else let ms be |
| // ? ToIntegerIfIntegral(milliseconds). |
| int64_t ms = 0; |
| if (!IsUndefined(*milliseconds)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, ms, |
| temporal::ToIntegerTypeIfIntegral<int64_t>(isolate, milliseconds)); |
| } |
| |
| // 10. If microseconds is undefined, let mis be 0; else let mis be |
| // ? ToIntegerIfIntegral(microseconds). |
| double mis = 0; |
| if (!IsUndefined(*microseconds)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, mis, temporal::ToIntegerIfIntegral(isolate, microseconds)); |
| } |
| |
| // 11. If nanoseconds is undefined, let ns be 0; else let ns be |
| // ? ToIntegerIfIntegral(nanoseconds). |
| double ns = 0; |
| if (!IsUndefined(*nanoseconds)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, ns, temporal::ToIntegerIfIntegral(isolate, nanoseconds)); |
| } |
| |
| // 12. Return ?CreateTemporalDuration(y, mo, w, d, h, m, s, ms, mis, ns, |
| // NewTarget). |
| return ConstructRustWrappingType<JSTemporalDuration>( |
| isolate, target, new_target, |
| temporal_rs::Duration::create(y, mo, w, d, h, m, s, ms, mis, ns)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.compare |
| MaybeDirectHandle<Smi> JSTemporalDuration::Compare( |
| Isolate* isolate, DirectHandle<Object> one_obj, |
| DirectHandle<Object> two_obj, DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.Duration.compare"; |
| DirectHandle<JSTemporalDuration> one; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, one, |
| temporal::ToTemporalDuration(isolate, one_obj, method_name)); |
| DirectHandle<JSTemporalDuration> two; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, two, |
| temporal::ToTemporalDuration(isolate, two_obj, method_name)); |
| |
| temporal::RelativeTo relative_to; |
| |
| MOVE_RETURN_ON_EXCEPTION(isolate, relative_to, |
| temporal::GetTemporalRelativeToOptionHandleUndefined( |
| isolate, options_obj)); |
| |
| int8_t comparison = 0; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, comparison, |
| ExtractRustResult(isolate, one->wrapped_rust()->compare_with_provider( |
| *two->wrapped_rust(), relative_to.ToRust(), |
| TimeZoneProvider()))); |
| |
| return direct_handle(Smi::FromInt(comparison), isolate); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.from |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalDuration::From( |
| Isolate* isolate, DirectHandle<Object> item) { |
| static const char method_name[] = "Temporal.Duration.from"; |
| |
| return temporal::ToTemporalDuration(isolate, item, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.round |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalDuration::Round( |
| Isolate* isolate, DirectHandle<JSTemporalDuration> duration, |
| DirectHandle<Object> round_to_obj) { |
| static const char method_name[] = "Temporal.Duration.prototype.round"; |
| // 1. Let duration be the this value. |
| // 2. Perform ? RequireInternalSlot(duration, |
| // [[InitializedTemporalDuration]]). |
| |
| // (handled by type system) |
| // 3. If roundTo is undefined, then |
| if (IsUndefined(*round_to_obj)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kRoundToMissing)); |
| } |
| |
| DirectHandle<JSReceiver> round_to; |
| auto factory = isolate->factory(); |
| |
| // 4. If roundTo is a String, then |
| if (IsString(*round_to_obj)) { |
| // a. Let paramString be roundTo. |
| DirectHandle<String> param_string = Cast<String>(round_to_obj); |
| // b. Set roundTo to ! OrdinaryObjectCreate(null). |
| round_to = factory->NewJSObjectWithNullProto(); |
| // TODO(manishearth) Ideally we don't have to perform this allocation |
| // c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", |
| // paramString). |
| CHECK(JSReceiver::CreateDataProperty(isolate, round_to, |
| factory->smallestUnit_string(), |
| param_string, Just(kThrowOnError)) |
| .FromJust()); |
| // 5. Else, |
| } else { |
| // a. Set roundTo to ? GetOptionsObject(roundTo). |
| // We have already checked for undefined, we can hoist the JSReceiver |
| // check out and just cast |
| if (!IsJSReceiver(*round_to_obj)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kRoundToMustBeObject)); |
| } |
| round_to = Cast<JSReceiver>(round_to_obj); |
| } |
| |
| // 6. Let smallestUnitPresent be true. |
| // 7. Let largestUnitPresent be true. |
| |
| // (handled by Rust) |
| |
| // 8. NOTE: (...) |
| |
| // 9. Let largestUnit be ? GetTemporalUnitValuedOption(roundTo, "largestUnit", |
| // unset). |
| std::optional<Unit> largest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, largest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, round_to, isolate->factory()->largestUnit_string(), |
| DefaultValue::kUnset, method_name)); |
| |
| // 10. Let relativeToRecord be ? GetTemporalRelativeToOption(roundTo). |
| // 11. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]]. |
| // 12. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]]. |
| |
| temporal::RelativeTo relative_to; |
| |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, relative_to, |
| temporal::GetTemporalRelativeToOptionHandleUndefined(isolate, round_to)); |
| |
| // 13. Let roundingIncrement be ? GetRoundingIncrementOption(roundTo). |
| |
| uint32_t rounding_increment; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_increment, |
| temporal::GetRoundingIncrementOption(isolate, round_to)); |
| |
| // 14. Let roundingMode be ? GetRoundingModeOption(roundTo, half-expand). |
| RoundingMode rounding_mode; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_mode, |
| temporal::GetRoundingModeOption(isolate, round_to, |
| RoundingMode::HalfExpand, method_name)); |
| |
| // 15. Let smallestUnit be ? GetTemporalUnitValuedOption(roundTo, |
| // "smallestUnit", unset). |
| std::optional<Unit> smallest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, smallest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, round_to, isolate->factory()->smallestUnit_string(), |
| DefaultValue::kUnset, method_name)); |
| // 16. Perform ? ValidateTemporalUnitValue(smallestUnit, datetime). |
| RETURN_ON_EXCEPTION( |
| isolate, temporal::ValidateTemporalUnitValue(isolate, smallest_unit, |
| UnitGroup::kDateTime)); |
| |
| // Rest of the steps handled in Rust |
| |
| auto options = temporal_rs::RoundingOptions{.largest_unit = largest_unit, |
| .smallest_unit = smallest_unit, |
| .rounding_mode = rounding_mode, |
| .increment = rounding_increment}; |
| |
| auto rounded = duration->wrapped_rust()->round_with_provider( |
| options, relative_to.ToRust(), TimeZoneProvider()); |
| return ConstructRustWrappingType<JSTemporalDuration>(isolate, |
| std::move(rounded)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.total |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.total |
| MaybeDirectHandle<Number> JSTemporalDuration::Total( |
| Isolate* isolate, DirectHandle<JSTemporalDuration> duration, |
| DirectHandle<Object> total_of_obj) { |
| static const char method_name[] = "Temporal.Duration.prototype.total"; |
| // 1. Let duration be the this value. |
| // 2. Perform ? RequireInternalSlot(duration, |
| // [[InitializedTemporalDuration]]). |
| |
| // (handled by type system) |
| |
| // 3. If totalOf is undefined, throw a TypeError exception. |
| if (IsUndefined(*total_of_obj)) { |
| THROW_NEW_ERROR( |
| isolate, NEW_TEMPORAL_TYPE_ERROR("Must specify a totalOf parameter")); |
| } |
| |
| DirectHandle<JSReceiver> total_of; |
| auto factory = isolate->factory(); |
| |
| // 4. If totalOf is a String, then |
| if (IsString(*total_of_obj)) { |
| // a. Let paramString be totalOf. |
| DirectHandle<String> param_string = Cast<String>(total_of_obj); |
| // b. Set totalOf to ! OrdinaryObjectCreate(null). |
| total_of = factory->NewJSObjectWithNullProto(); |
| // c. Perform ! CreateDataPropertyOrThrow(total_of, "unit", paramString). |
| CHECK(JSReceiver::CreateDataProperty(isolate, total_of, |
| factory->unit_string(), param_string, |
| Just(kThrowOnError)) |
| .FromJust()); |
| // 5. Else, |
| } else { |
| // a. Set totalOf to ? GetOptionsObject(totalOf). |
| // We have already checked for undefined, we can hoist the JSReceiver |
| // check out and just cast |
| if (!IsJSReceiver(*total_of_obj)) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_TYPE_ERROR("totalOf must be an object.")); |
| } |
| total_of = Cast<JSReceiver>(total_of_obj); |
| } |
| |
| // 6. NOTE (...) |
| |
| // 7. Let relativeToRecord be ? GetTemporalRelativeToOption(totalOf). |
| // 8. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]]. |
| // 9. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]]. |
| |
| temporal::RelativeTo relative_to; |
| |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, relative_to, |
| temporal::GetTemporalRelativeToOptionHandleUndefined(isolate, total_of)); |
| |
| // 10. Let unit be ? GetTemporalUnitValuedOption(totalOf, "unit", required). |
| std::optional<Unit> unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, total_of, isolate->factory()->unit_string(), |
| DefaultValue::kRequired, method_name)); |
| // 11. Perform ? ValidateTemporalUnitValue(unit, datetime). |
| RETURN_ON_EXCEPTION(isolate, temporal::ValidateTemporalUnitValue( |
| isolate, unit, UnitGroup::kDateTime)); |
| |
| // We set required to true. |
| DCHECK(unit.has_value()); |
| |
| // Remaining steps handled in Rust |
| double ret; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, ret, |
| ExtractRustResult(isolate, duration->wrapped_rust()->total_with_provider( |
| unit.value(), relative_to.ToRust(), |
| TimeZoneProvider()))); |
| |
| return factory->NewNumber(ret); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.with |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalDuration::With( |
| Isolate* isolate, DirectHandle<JSTemporalDuration> duration, |
| DirectHandle<Object> temporal_duration_like) { |
| temporal_rs::PartialDuration partial; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, partial, |
| temporal::ToTemporalPartialDurationRecord( |
| isolate, temporal_duration_like)); |
| if (!partial.years.has_value()) { |
| partial.years = duration->wrapped_rust()->years(); |
| } |
| if (!partial.months.has_value()) { |
| partial.months = duration->wrapped_rust()->months(); |
| } |
| if (!partial.months.has_value()) { |
| partial.months = duration->wrapped_rust()->months(); |
| } |
| if (!partial.weeks.has_value()) { |
| partial.weeks = duration->wrapped_rust()->weeks(); |
| } |
| if (!partial.days.has_value()) { |
| partial.days = duration->wrapped_rust()->days(); |
| } |
| if (!partial.hours.has_value()) { |
| partial.hours = duration->wrapped_rust()->hours(); |
| } |
| if (!partial.minutes.has_value()) { |
| partial.minutes = duration->wrapped_rust()->minutes(); |
| } |
| if (!partial.seconds.has_value()) { |
| partial.seconds = duration->wrapped_rust()->seconds(); |
| } |
| if (!partial.milliseconds.has_value()) { |
| partial.milliseconds = duration->wrapped_rust()->milliseconds(); |
| } |
| if (!partial.microseconds.has_value()) { |
| partial.microseconds = duration->wrapped_rust()->microseconds(); |
| } |
| if (!partial.nanoseconds.has_value()) { |
| partial.nanoseconds = duration->wrapped_rust()->nanoseconds(); |
| } |
| return ConstructRustWrappingType<JSTemporalDuration>( |
| isolate, temporal_rs::Duration::from_partial_duration(partial)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.sign |
| MaybeDirectHandle<Smi> JSTemporalDuration::Sign( |
| Isolate* isolate, DirectHandle<JSTemporalDuration> duration) { |
| auto sign = duration->wrapped_rust()->sign(); |
| return direct_handle(Smi::FromInt((temporal_rs::Sign::Value)sign), isolate); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.blank |
| MaybeDirectHandle<Oddball> JSTemporalDuration::Blank( |
| Isolate* isolate, DirectHandle<JSTemporalDuration> duration) { |
| return isolate->factory()->ToBoolean(duration->wrapped_rust()->sign() == |
| temporal_rs::Sign::Zero); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.negated |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalDuration::Negated( |
| Isolate* isolate, DirectHandle<JSTemporalDuration> duration) { |
| return ConstructRustWrappingType<JSTemporalDuration>( |
| isolate, duration->wrapped_rust()->negated()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.abs |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalDuration::Abs( |
| Isolate* isolate, DirectHandle<JSTemporalDuration> duration) { |
| return ConstructRustWrappingType<JSTemporalDuration>( |
| isolate, duration->wrapped_rust()->abs()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.add |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalDuration::Add( |
| Isolate* isolate, DirectHandle<JSTemporalDuration> duration, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.Duration.prototype.add"; |
| |
| DirectHandle<JSTemporalDuration> other_duration; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, other_duration, |
| temporal::ToTemporalDuration(isolate, other, method_name)); |
| |
| auto result = duration->wrapped_rust()->add(*other_duration->wrapped_rust()); |
| return ConstructRustWrappingType<JSTemporalDuration>(isolate, |
| std::move(result)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.subtract |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalDuration::Subtract( |
| Isolate* isolate, DirectHandle<JSTemporalDuration> duration, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.Duration.prototype.subtract"; |
| |
| DirectHandle<JSTemporalDuration> other_duration; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, other_duration, |
| temporal::ToTemporalDuration(isolate, other, method_name)); |
| |
| auto result = |
| duration->wrapped_rust()->subtract(*other_duration->wrapped_rust()); |
| return ConstructRustWrappingType<JSTemporalDuration>(isolate, |
| std::move(result)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tostring |
| MaybeDirectHandle<String> JSTemporalDuration::ToString( |
| Isolate* isolate, DirectHandle<JSTemporalDuration> duration, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.Duration.prototype.toString"; |
| |
| // 3. Let resolvedOptions be ?GetOptionsObject(options). |
| DirectHandle<JSReceiver> options; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetOptionsObject(isolate, options_obj, method_name)); |
| |
| // 5. Let digits be ?GetTemporalFractionalSecondDigitsOption(resolvedOptions). |
| |
| temporal_rs::Precision digits; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, digits, |
| temporal::GetTemporalFractionalSecondDigitsOption( |
| isolate, options, method_name)); |
| |
| // 6. Let roundingMode be ? GetRoundingModeOption(resolvedOptions, trunc). |
| |
| RoundingMode rounding_mode; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_mode, |
| temporal::GetRoundingModeOption(isolate, options, RoundingMode::Trunc, |
| method_name)); |
| |
| // 7. Let smallestUnit be ? GetTemporalUnitValuedOption(resolvedOptions, |
| // "smallestUnit", unset). |
| std::optional<Unit> smallest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, smallest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, options, isolate->factory()->smallestUnit_string(), |
| DefaultValue::kUnset, method_name)); |
| |
| // 8. Perform ? ValidateTemporalUnitValue(smallestUnit, time). |
| RETURN_ON_EXCEPTION(isolate, temporal::ValidateTemporalUnitValue( |
| isolate, smallest_unit, UnitGroup::kTime)); |
| |
| // 8-17 performed by Rust |
| auto rust_options = temporal_rs::ToStringRoundingOptions{ |
| .precision = digits, |
| .smallest_unit = smallest_unit, |
| .rounding_mode = rounding_mode, |
| }; |
| |
| return temporal::GenericTemporalToString(isolate, duration, |
| &temporal_rs::Duration::to_string, |
| std::move(rust_options)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tojson |
| MaybeDirectHandle<String> JSTemporalDuration::ToJSON( |
| Isolate* isolate, DirectHandle<JSTemporalDuration> duration) { |
| return temporal::GenericTemporalToString(isolate, duration, |
| &temporal_rs::Duration::to_string, |
| std::move(temporal::kToStringAuto)); |
| } |
| |
| MaybeDirectHandle<String> JSTemporalDuration::ToLocaleString( |
| Isolate* isolate, DirectHandle<JSTemporalDuration> duration, |
| DirectHandle<Object> locales, DirectHandle<Object> options) { |
| #ifdef V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sup-temporal.duration.prototype.tolocalestring |
| return JSDurationFormat::TemporalToLocaleString(isolate, duration, locales, |
| options); |
| #else // V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tolocalestring |
| return temporal::GenericTemporalToString(isolate, duration, |
| &temporal_rs::Duration::to_string, |
| std::move(temporal::kToStringAuto)); |
| #endif // V8_INTL_SUPPORT |
| } |
| |
| MaybeDirectHandle<JSTemporalPlainDate> JSTemporalPlainDate::Constructor( |
| Isolate* isolate, DirectHandle<JSFunction> target, |
| DirectHandle<HeapObject> new_target, DirectHandle<Object> iso_year_obj, |
| DirectHandle<Object> iso_month_obj, DirectHandle<Object> iso_day_obj, |
| DirectHandle<Object> calendar_like) { |
| // 1. If NewTarget is undefined, then |
| if (IsUndefined(*new_target)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, |
| isolate->factory()->NewStringFromAsciiChecked( |
| "Temporal.PlainDate"))); |
| } |
| // 2. Let y be ? ToIntegerWithTruncation(isoYear). |
| double y = 0; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, y, temporal::ToIntegerWithTruncation(isolate, iso_year_obj)); |
| // 3. Let m be ? ToIntegerWithTruncation(isoMonth). |
| double m = 0; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, m, temporal::ToIntegerWithTruncation(isolate, iso_month_obj)); |
| // 4. Let d be ? ToIntegerWithTruncation(isoDay). |
| double d = 0; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, d, temporal::ToIntegerWithTruncation(isolate, iso_day_obj)); |
| |
| // 5. If calendar is undefined, set calendar to "iso8601". |
| temporal_rs::AnyCalendarKind calendar = temporal_rs::AnyCalendarKind::Iso; |
| |
| if (!IsUndefined(*calendar_like)) { |
| // 6. If calendar is not a String, throw a TypeError exception. |
| if (!IsString(*calendar_like)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kCalendarMustBeString)); |
| } |
| |
| // 7. Set calendar to ?CanonicalizeCalendar(calendar). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar, |
| temporal::CanonicalizeCalendar(isolate, Cast<String>(calendar_like))); |
| } |
| // 8. If IsValidISODate(y, m, d) is false, throw a RangeError exception. |
| if (!temporal::IsValidIsoDate(y, m, d)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kInvalidIsoDate)); |
| } |
| |
| // Rest of the steps handled in Rust |
| |
| // These static_casts are fine to perform since IsValid* will have constrained |
| // these values to range already. |
| // https://github.com/boa-dev/temporal/issues/334 for moving this logic into |
| // the Rust code. |
| auto rust_object = temporal_rs::PlainDate::try_new( |
| static_cast<int32_t>(y), static_cast<uint8_t>(m), static_cast<uint8_t>(d), |
| calendar); |
| return ConstructRustWrappingType<JSTemporalPlainDate>( |
| isolate, target, new_target, std::move(rust_object)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.compare |
| MaybeDirectHandle<Smi> JSTemporalPlainDate::Compare( |
| Isolate* isolate, DirectHandle<Object> one_obj, |
| DirectHandle<Object> two_obj) { |
| static const char method_name[] = "Temporal.PlainDate.compare"; |
| DirectHandle<JSTemporalPlainDate> one; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, one, |
| temporal::ToTemporalDate(isolate, one_obj, {}, method_name)); |
| DirectHandle<JSTemporalPlainDate> two; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, two, |
| temporal::ToTemporalDate(isolate, two_obj, {}, method_name)); |
| |
| return direct_handle(Smi::FromInt(temporal_rs::PlainDate::compare( |
| *one->wrapped_rust(), *two->wrapped_rust())), |
| isolate); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.equals |
| MaybeDirectHandle<Oddball> JSTemporalPlainDate::Equals( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> temporal_date, |
| DirectHandle<Object> other_obj) { |
| static const char method_name[] = "Temporal.PlainDate.prototype.equals"; |
| |
| DirectHandle<JSTemporalPlainDate> other; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, other, |
| temporal::ToTemporalDate(isolate, other_obj, {}, method_name)); |
| |
| auto equals = temporal_date->wrapped_rust()->equals(*other->wrapped_rust()); |
| |
| return isolate->factory()->ToBoolean(equals); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.toplainyearmonth |
| MaybeDirectHandle<JSTemporalPlainYearMonth> |
| JSTemporalPlainDate::ToPlainYearMonth( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> temporal_date) { |
| return temporal::GenericToTemporalMethod<JSTemporalPlainYearMonth>( |
| isolate, temporal_date, &temporal_rs::PlainDate::to_plain_year_month); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.toplainmonthday |
| MaybeDirectHandle<JSTemporalPlainMonthDay> JSTemporalPlainDate::ToPlainMonthDay( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> temporal_date) { |
| return temporal::GenericToTemporalMethod<JSTemporalPlainMonthDay>( |
| isolate, temporal_date, &temporal_rs::PlainDate::to_plain_month_day); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.toplaindatetime |
| MaybeDirectHandle<JSTemporalPlainDateTime> JSTemporalPlainDate::ToPlainDateTime( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> temporal_date, |
| DirectHandle<Object> temporal_time_obj) { |
| static const char method_name[] = "Temporal.PlainDate.toPlainDateTime"; |
| |
| // 3. Let time be ? ToTimeRecordOrMidnight(temporalTime). |
| CppGCManaged<temporal_rs::PlainTime>::Ptr maybe_time; |
| DirectHandle<JSTemporalPlainTime> time_output; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, maybe_time, |
| temporal::ToTimeRecordOrMidnight(isolate, temporal_time_obj, time_output, |
| method_name)); |
| |
| // Rest of the steps handled in Rust. |
| return ConstructRustWrappingType<JSTemporalPlainDateTime>( |
| isolate, |
| temporal_date->wrapped_rust()->to_plain_date_time(maybe_time.raw())); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.with |
| MaybeDirectHandle<JSTemporalPlainDate> JSTemporalPlainDate::With( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> temporal_date, |
| DirectHandle<Object> temporal_date_like_obj, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainDate.prototype.with"; |
| return temporal::GenericWith<JSTemporalPlainDate, temporal_rs::PartialDate>( |
| isolate, temporal_date, temporal_date_like_obj, options_obj, |
| temporal::kAllDateFlags, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.withcalendar |
| MaybeDirectHandle<JSTemporalPlainDate> JSTemporalPlainDate::WithCalendar( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> temporal_date, |
| DirectHandle<Object> calendar_id) { |
| // 3. Let calendar be ? ToTemporalCalendarIdentifier(calendarLike). |
| temporal_rs::AnyCalendarKind calendar_kind; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar_kind, |
| temporal::ToTemporalCalendarIdentifier(isolate, calendar_id)); |
| |
| // Rest of the steps handled in Rust |
| return ConstructRustWrappingType<JSTemporalPlainDate>( |
| isolate, temporal_date->wrapped_rust()->with_calendar(calendar_kind)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tozoneddatetime |
| MaybeDirectHandle<JSTemporalZonedDateTime> JSTemporalPlainDate::ToZonedDateTime( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> temporal_date, |
| DirectHandle<Object> item_obj) { |
| static const char method_name[] = "Temporal.PlainDate.toZonedDateTime"; |
| // 1. Let temporalDate be the this value. |
| // 2. Perform ? RequireInternalSlot(temporalDate, |
| // [[InitializedTemporalDate]]). |
| |
| temporal_rs::TimeZone time_zone; |
| DirectHandle<Object> temporal_time_obj; |
| |
| // 3. If item is an Object, then |
| if (IsJSReceiver(*item_obj)) { |
| DirectHandle<JSReceiver> item = Cast<JSReceiver>(item_obj); |
| // a. Let timeZoneLike be ? Get(item, "timeZone"). |
| DirectHandle<Object> time_zone_like; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, time_zone_like, |
| JSReceiver::GetProperty(isolate, item, |
| isolate->factory()->timeZone_string())); |
| // b. If timeZoneLike is undefined, then |
| if (IsUndefined(*time_zone_like)) { |
| // i. Let timeZone be ? ToTemporalTimeZoneIdentifier(item). |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, time_zone, |
| temporal::ToTemporalTimeZoneIdentifier(isolate, item)); |
| // ii. Let temporalTime be undefined. |
| |
| // c. Else, |
| } else { |
| // i. Let timeZone be ? ToTemporalTimeZoneIdentifier(timeZoneLike). |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, time_zone, |
| temporal::ToTemporalTimeZoneIdentifier(isolate, time_zone_like)); |
| // ii. Let temporalTime be ? Get(item, "plainTime"). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, temporal_time_obj, |
| JSReceiver::GetProperty(isolate, item, |
| isolate->factory()->plainTime_string())); |
| } |
| // 4. Else, |
| } else { |
| // a. Let timeZone be ? ToTemporalTimeZoneIdentifier(item). |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, time_zone, |
| temporal::ToTemporalTimeZoneIdentifier(isolate, item_obj)); |
| // b. Let temporalTime be undefined. |
| } |
| |
| DirectHandle<JSTemporalPlainTime> temporal_time; |
| CppGCManaged<temporal_rs::PlainTime>::Ptr temporal_time_rust; |
| |
| // 5. If temporalTime is undefined, then |
| if (temporal_time_obj.is_null() || IsUndefined(*temporal_time_obj)) { |
| // a. Let epochNs be ? GetStartOfDay(timeZone, temporalDate.[[ISODate]]). |
| // (handled in Rust by temporal_time_rust being a null pointer) |
| |
| // 6. Else, |
| } else { |
| // a. Set temporalTime to ? ToTemporalTime(temporalTime). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, temporal_time, |
| temporal::ToTemporalTime(isolate, temporal_time_obj, {}, method_name)); |
| temporal_time_rust = temporal_time->wrapped_rust(); |
| // (Rest of the steps handled in Rust) |
| } |
| |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>( |
| isolate, temporal_date->wrapped_rust()->to_zoned_date_time_with_provider( |
| time_zone, temporal_time_rust.raw(), TimeZoneProvider())); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.add |
| MaybeDirectHandle<JSTemporalPlainDate> JSTemporalPlainDate::Add( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> temporal_date, |
| DirectHandle<Object> temporal_duration_like, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainDate.prototype.add"; |
| // 3. Return ? AddDurationToDate(add, temporalDate, temporalDurationLike, |
| // options). |
| return temporal::AddDurationToGeneric(isolate, &temporal_rs::PlainDate::add, |
| temporal_date, temporal_duration_like, |
| options_obj, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.subtract |
| MaybeDirectHandle<JSTemporalPlainDate> JSTemporalPlainDate::Subtract( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> temporal_date, |
| DirectHandle<Object> temporal_duration_like, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainDate.prototype.subtract"; |
| // 3. Return ? AddDurationToDate(subtract, temporalDate, temporalDurationLike, |
| // options). |
| return temporal::AddDurationToGeneric<JSTemporalPlainDate>( |
| isolate, &temporal_rs::PlainDate::subtract, temporal_date, |
| temporal_duration_like, options_obj, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.until |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalPlainDate::Until( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> handle, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.PlainDate.prototype.until"; |
| |
| return temporal::GenericDifferenceTemporal( |
| isolate, &temporal_rs::PlainDate::until, UnitGroup::kDate, Unit::Day, |
| handle, other, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.since |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalPlainDate::Since( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> handle, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.PlainDate.prototype.since"; |
| |
| return temporal::GenericDifferenceTemporal( |
| isolate, &temporal_rs::PlainDate::since, UnitGroup::kDate, Unit::Day, |
| handle, other, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.now.plaindateiso |
| MaybeDirectHandle<JSTemporalPlainDate> JSTemporalPlainDate::NowISO( |
| Isolate* isolate, DirectHandle<Object> temporal_time_zone_like) { |
| std::unique_ptr<temporal_rs::ZonedDateTime> zdt; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, zdt, |
| temporal::GenericTemporalNowISO(isolate, temporal_time_zone_like)); |
| return ConstructRustWrappingType<JSTemporalPlainDate>(isolate, |
| zdt->to_plain_date()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.from |
| MaybeDirectHandle<JSTemporalPlainDate> JSTemporalPlainDate::From( |
| Isolate* isolate, DirectHandle<Object> item_obj, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainDate.from"; |
| DirectHandle<JSTemporalPlainDate> item; |
| |
| return temporal::ToTemporalDate(isolate, item_obj, options_obj, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tostring |
| MaybeDirectHandle<String> JSTemporalPlainDate::ToString( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> temporal_date, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainDate.prototype.toString"; |
| |
| // 3. Let resolvedOptions be ?GetOptionsObject(options). |
| DirectHandle<JSReceiver> options; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetOptionsObject(isolate, options_obj, method_name)); |
| |
| // 4. Let showCalendar be ?GetTemporalShowCalendarNameOption(resolvedOptions). |
| temporal_rs::DisplayCalendar show_calendar; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, show_calendar, |
| temporal::GetTemporalShowCalendarNameOption( |
| isolate, options, method_name)); |
| |
| // 5. Return TemporalDateToString(temporalDate, showCalendar). |
| return temporal::GenericTemporalToString( |
| isolate, temporal_date, &temporal_rs::PlainDate::to_ixdtf_string, |
| show_calendar); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tojson |
| MaybeDirectHandle<String> JSTemporalPlainDate::ToJSON( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> temporal_date) { |
| return temporal::GenericTemporalToString( |
| isolate, temporal_date, &temporal_rs::PlainDate::to_ixdtf_string, |
| temporal_rs::DisplayCalendar::Auto); |
| } |
| |
| MaybeDirectHandle<String> JSTemporalPlainDate::ToLocaleString( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDate> temporal_date, |
| DirectHandle<Object> locales, DirectHandle<Object> options) { |
| #ifdef V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sup-temporal.plaindate.prototype.tolocalestring |
| return JSDateTimeFormat::TemporalToLocaleString( |
| isolate, temporal_date, locales, options, |
| JSDateTimeFormat::RequiredOption::kDate, |
| JSDateTimeFormat::DefaultsOption::kDate, |
| "Temporal.PlainDate.prototype.toLocaleString"); |
| #else // V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tolocalestring |
| return temporal::GenericTemporalToString( |
| isolate, temporal_date, &temporal_rs::PlainDate::to_ixdtf_string, |
| temporal_rs::DisplayCalendar::Auto); |
| #endif // V8_INTL_SUPPORT |
| } |
| |
| Maybe<int64_t> JSTemporalPlainDate::GetEpochMillisecondsForUtc( |
| Isolate* isolate) { |
| return ExtractRustResult(isolate, this->wrapped_rust()->epoch_ms_for_utc()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-createtemporaldatetime |
| MaybeDirectHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::Constructor( |
| Isolate* isolate, DirectHandle<JSFunction> target, |
| DirectHandle<HeapObject> new_target, DirectHandle<Object> iso_year_obj, |
| DirectHandle<Object> iso_month_obj, DirectHandle<Object> iso_day_obj, |
| DirectHandle<Object> hour_obj, DirectHandle<Object> minute_obj, |
| DirectHandle<Object> second_obj, DirectHandle<Object> millisecond_obj, |
| DirectHandle<Object> microsecond_obj, DirectHandle<Object> nanosecond_obj, |
| DirectHandle<Object> calendar_like) { |
| // 1. If NewTarget is undefined, then |
| if (IsUndefined(*new_target)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, |
| isolate->factory()->NewStringFromAsciiChecked( |
| "Temporal.PlainDateTime"))); |
| } |
| // 2. Set isoYear to ?ToIntegerWithTruncation(isoYear). |
| double y = 0; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, y, temporal::ToIntegerWithTruncation(isolate, iso_year_obj)); |
| // 3. Set isoMonth to ?ToIntegerWithTruncation(isoMonth). |
| double m = 0; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, m, temporal::ToIntegerWithTruncation(isolate, iso_month_obj)); |
| // 4. Set isoDay to ?ToIntegerWithTruncation(isoDay). |
| double d = 0; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, d, temporal::ToIntegerWithTruncation(isolate, iso_day_obj)); |
| |
| // 5. If hour is undefined, set hour to 0; else set hour to ? |
| // ToIntegerWithTruncation(hour). |
| double hour = 0; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, hour, |
| temporal::ToIntegerWithTruncationOrZero(isolate, hour_obj)); |
| // 6. If minute is undefined, set minute to 0; else set minute to ? |
| // ToIntegerWithTruncation(minute). |
| double minute = 0; |
| if (!IsUndefined(*minute_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, minute, |
| temporal::ToIntegerWithTruncationOrZero(isolate, minute_obj)); |
| } |
| // 7. If second is undefined, set second to 0; else set second to ? |
| // ToIntegerWithTruncation(second). |
| double second = 0; |
| if (!IsUndefined(*second_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, second, |
| temporal::ToIntegerWithTruncationOrZero(isolate, second_obj)); |
| } |
| // 8. If millisecond is undefined, set millisecond to 0; else set millisecond |
| // to ? ToIntegerWithTruncation(millisecond). |
| double millisecond = 0; |
| if (!IsUndefined(*millisecond_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, millisecond, |
| temporal::ToIntegerWithTruncationOrZero(isolate, millisecond_obj)); |
| } |
| // 9. If microsecond is undefined, set microsecond to 0; else set microsecond |
| // to ? ToIntegerWithTruncation(microsecond). |
| |
| double microsecond = 0; |
| if (!IsUndefined(*microsecond_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, microsecond, |
| temporal::ToIntegerWithTruncationOrZero(isolate, microsecond_obj)); |
| } |
| // 10. If nanosecond is undefined, set nanosecond to 0; else set nanosecond to |
| // ? ToIntegerWithTruncation(nanosecond). |
| double nanosecond = 0; |
| if (!IsUndefined(*nanosecond_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, nanosecond, |
| temporal::ToIntegerWithTruncationOrZero(isolate, nanosecond_obj)); |
| } |
| |
| // 11. If calendar is undefined, set calendar to "iso8601". |
| temporal_rs::AnyCalendarKind calendar = temporal_rs::AnyCalendarKind::Iso; |
| |
| if (!IsUndefined(*calendar_like)) { |
| // 12. If calendar is not a String, throw a TypeError exception. |
| if (!IsString(*calendar_like)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kCalendarMustBeString)); |
| } |
| |
| // 13. Set calendar to ?CanonicalizeCalendar(calendar). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar, |
| temporal::CanonicalizeCalendar(isolate, Cast<String>(calendar_like))); |
| } |
| // 14. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a |
| // RangeError exception. |
| if (!temporal::IsValidIsoDate(y, m, d)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kInvalidIsoDate)); |
| } |
| // 16. If IsValidTime(hour, minute, second, millisecond, microsecond, |
| // nanosecond) is false, throw a RangeError exception. |
| if (!temporal::IsValidTime(hour, minute, second, millisecond, microsecond, |
| nanosecond)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kInvalidTime)); |
| } |
| |
| // Rest of the steps handled in Rust. |
| |
| // These static_casts are fine to perform since IsValid* will have constrained |
| // these values to range already. |
| // https://github.com/boa-dev/temporal/issues/334 for moving this logic into |
| // the Rust code. |
| auto rust_object = temporal_rs::PlainDateTime::try_new( |
| static_cast<int32_t>(y), static_cast<uint8_t>(m), static_cast<uint8_t>(d), |
| static_cast<uint8_t>(hour), static_cast<uint8_t>(minute), |
| static_cast<uint8_t>(second), static_cast<uint16_t>(millisecond), |
| static_cast<uint16_t>(microsecond), static_cast<uint16_t>(nanosecond), |
| calendar); |
| return ConstructRustWrappingType<JSTemporalPlainDateTime>( |
| isolate, target, new_target, std::move(rust_object)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.from |
| MaybeDirectHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::From( |
| Isolate* isolate, DirectHandle<Object> item_obj, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainDateTime.from"; |
| DirectHandle<JSTemporalPlainDateTime> item; |
| |
| return temporal::ToTemporalDateTime(isolate, item_obj, options_obj, |
| method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.compare |
| MaybeDirectHandle<Smi> JSTemporalPlainDateTime::Compare( |
| Isolate* isolate, DirectHandle<Object> one_obj, |
| DirectHandle<Object> two_obj) { |
| static const char method_name[] = "Temporal.PlainDateTime.compare"; |
| DirectHandle<JSTemporalPlainDateTime> one; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, one, |
| temporal::ToTemporalDateTime(isolate, one_obj, {}, method_name)); |
| DirectHandle<JSTemporalPlainDateTime> two; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, two, |
| temporal::ToTemporalDateTime(isolate, two_obj, {}, method_name)); |
| |
| return direct_handle(Smi::FromInt(temporal_rs::PlainDateTime::compare( |
| *one->wrapped_rust(), *two->wrapped_rust())), |
| isolate); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.equals |
| MaybeDirectHandle<Oddball> JSTemporalPlainDateTime::Equals( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> date_time, |
| DirectHandle<Object> other_obj) { |
| static const char method_name[] = "Temporal.PlainDateTime.prototype.equals"; |
| |
| DirectHandle<JSTemporalPlainDateTime> other; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, other, |
| temporal::ToTemporalDateTime(isolate, other_obj, {}, method_name)); |
| |
| auto equals = date_time->wrapped_rust()->equals(*other->wrapped_rust()); |
| |
| return isolate->factory()->ToBoolean(equals); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with |
| MaybeDirectHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::With( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> date_time, |
| DirectHandle<Object> temporal_date_time_like_obj, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainDateTime.prototype.with"; |
| |
| using enum temporal::CalendarFieldsFlag; |
| |
| return temporal::GenericWith<JSTemporalPlainDateTime, |
| temporal_rs::PartialDateTime>( |
| isolate, date_time, temporal_date_time_like_obj, options_obj, |
| temporal::kAllDateFlags | kTimeFields, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.withcalendar |
| MaybeDirectHandle<JSTemporalPlainDateTime> |
| JSTemporalPlainDateTime::WithCalendar( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> temporal_date, |
| DirectHandle<Object> calendar_id) { |
| // 3. Let calendar be ? ToTemporalCalendarIdentifier(calendarLike). |
| temporal_rs::AnyCalendarKind calendar_kind; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar_kind, |
| temporal::ToTemporalCalendarIdentifier(isolate, calendar_id)); |
| |
| // Rest of the steps handled in Rust |
| return ConstructRustWrappingType<JSTemporalPlainDateTime>( |
| isolate, temporal_date->wrapped_rust()->with_calendar(calendar_kind)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.withplaintime |
| MaybeDirectHandle<JSTemporalPlainDateTime> |
| JSTemporalPlainDateTime::WithPlainTime( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> date_time, |
| DirectHandle<Object> plain_time_like) { |
| static const char method_name[] = |
| "Temporal.PlainDateTime.prototype.withPlainTime"; |
| |
| // 3. Let time be ? ToTimeRecordOrMidnight(plainTimeLike). |
| CppGCManaged<temporal_rs::PlainTime>::Ptr maybe_time; |
| DirectHandle<JSTemporalPlainTime> time_output; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, maybe_time, |
| temporal::ToTimeRecordOrMidnight(isolate, plain_time_like, time_output, |
| method_name)); |
| |
| // Rest of the steps handled in Rust. |
| return ConstructRustWrappingType<JSTemporalPlainDateTime>( |
| isolate, date_time->wrapped_rust()->with_time(maybe_time.raw())); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.tozoneddatetime |
| MaybeDirectHandle<JSTemporalZonedDateTime> |
| JSTemporalPlainDateTime::ToZonedDateTime( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> date_time, |
| DirectHandle<Object> temporal_time_zone_like, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = |
| "Temporal.PlainDateTime.prototype.toZonedDateTime"; |
| // 3. Let timeZone be ? ToTemporalTimeZoneIdentifier(temporalTimeZoneLike). |
| temporal_rs::TimeZone time_zone; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, time_zone, |
| temporal::ToTemporalTimeZoneIdentifier(isolate, temporal_time_zone_like)); |
| // 4. Let resolvedOptions be ? GetOptionsObject(options). |
| // 5. Let disambiguation be ? |
| // GetTemporalDisambiguationOption(resolvedOptions). |
| |
| temporal_rs::Disambiguation disambiguation; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, disambiguation, |
| temporal::GetTemporalDisambiguationOptionHandleUndefined( |
| isolate, options_obj, method_name)); |
| |
| // Rest of the steps handled in Rust. |
| |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>( |
| isolate, date_time->wrapped_rust()->to_zoned_date_time_with_provider( |
| time_zone, disambiguation, TimeZoneProvider())); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.tostring |
| MaybeDirectHandle<String> JSTemporalPlainDateTime::ToString( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> date_time, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.DateTime.prototype.toString"; |
| |
| // 3. Let resolvedOptions be ?GetOptionsObject(options). |
| DirectHandle<JSReceiver> options; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetOptionsObject(isolate, options_obj, method_name)); |
| |
| // 5. Let showCalendar be ?GetTemporalShowCalendarNameOption(resolvedOptions). |
| temporal_rs::DisplayCalendar show_calendar; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, show_calendar, |
| temporal::GetTemporalShowCalendarNameOption( |
| isolate, options, method_name)); |
| |
| // 6. Let digits be ?GetTemporalFractionalSecondDigitsOption(resolvedOptions). |
| temporal_rs::Precision digits; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, digits, |
| temporal::GetTemporalFractionalSecondDigitsOption( |
| isolate, options, method_name)); |
| |
| // 7. Let roundingMode be ? GetRoundingModeOption(resolvedOptions, trunc). |
| RoundingMode rounding_mode; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_mode, |
| temporal::GetRoundingModeOption(isolate, options, RoundingMode::Trunc, |
| method_name)); |
| |
| // 8. Let smallestUnit be ? GetTemporalUnitValuedOption(resolvedOptions, |
| // "smallestUnit", unset). |
| |
| std::optional<Unit> smallest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, smallest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, options, isolate->factory()->smallestUnit_string(), |
| DefaultValue::kUnset, method_name)); |
| // 9. Perform ? ValidateTemporalUnitValue(smallestUnit, time). |
| RETURN_ON_EXCEPTION(isolate, temporal::ValidateTemporalUnitValue( |
| isolate, smallest_unit, UnitGroup::kTime)); |
| |
| // Rest of the steps handled in Rust |
| auto rust_options = temporal_rs::ToStringRoundingOptions{ |
| .precision = digits, |
| .smallest_unit = smallest_unit, |
| .rounding_mode = rounding_mode, |
| }; |
| return temporal::GenericTemporalToString( |
| isolate, date_time, &temporal_rs::PlainDateTime::to_ixdtf_string, |
| std::move(rust_options), show_calendar); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.tojson |
| MaybeDirectHandle<String> JSTemporalPlainDateTime::ToJSON( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> date_time) { |
| return temporal::GenericTemporalToString( |
| isolate, date_time, &temporal_rs::PlainDateTime::to_ixdtf_string, |
| std::move(temporal::kToStringAuto), temporal_rs::DisplayCalendar::Auto); |
| } |
| |
| MaybeDirectHandle<String> JSTemporalPlainDateTime::ToLocaleString( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> date_time, |
| DirectHandle<Object> locales, DirectHandle<Object> options) { |
| #ifdef V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sup-temporal.plaindatetime.prototype.tolocalestring |
| return JSDateTimeFormat::TemporalToLocaleString( |
| isolate, date_time, locales, options, |
| JSDateTimeFormat::RequiredOption::kAny, |
| JSDateTimeFormat::DefaultsOption::kAll, |
| "Temporal.PlainDateTime.prototype.toLocaleString"); |
| #else // V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.tolocalestring |
| return temporal::GenericTemporalToString( |
| isolate, date_time, &temporal_rs::PlainDateTime::to_ixdtf_string, |
| std::move(temporal::kToStringAuto), temporal_rs::DisplayCalendar::Auto); |
| #endif // V8_INTL_SUPPORT |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.now.plaindatetimeiso |
| MaybeDirectHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::NowISO( |
| Isolate* isolate, DirectHandle<Object> temporal_time_zone_like) { |
| std::unique_ptr<temporal_rs::ZonedDateTime> zdt; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, zdt, |
| temporal::GenericTemporalNowISO(isolate, temporal_time_zone_like)); |
| return ConstructRustWrappingType<JSTemporalPlainDateTime>( |
| isolate, zdt->to_plain_datetime()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.round |
| MaybeDirectHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::Round( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> date_time, |
| DirectHandle<Object> round_to_obj) { |
| static const char method_name[] = "Temporal.PlainDateTime.prototype.round"; |
| // 1. Let dateTime be the this value. |
| // 2. Perform ? RequireInternalSlot(dateTime, |
| // [[InitializedTemporalDateTime]]). |
| |
| // (handled by type system) |
| |
| // 3. If roundTo is undefined, then |
| if (IsUndefined(*round_to_obj)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kRoundToMissing)); |
| } |
| |
| DirectHandle<JSReceiver> round_to; |
| auto factory = isolate->factory(); |
| |
| // 4. If roundTo is a String, then |
| if (IsString(*round_to_obj)) { |
| // a. Let paramString be roundTo. |
| DirectHandle<String> param_string = Cast<String>(round_to_obj); |
| // b. Set roundTo to ! OrdinaryObjectCreate(null). |
| round_to = factory->NewJSObjectWithNullProto(); |
| // c. c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", |
| // paramString). |
| CHECK(JSReceiver::CreateDataProperty(isolate, round_to, |
| factory->smallestUnit_string(), |
| param_string, Just(kThrowOnError)) |
| .FromJust()); |
| // 5. Else, |
| } else { |
| // a. Set roundTo to ? GetOptionsObject(roundTo). |
| // We have already checked for undefined, we can hoist the JSReceiver |
| // check out and just cast |
| if (!IsJSReceiver(*round_to_obj)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kRoundToMustBeObject)); |
| } |
| round_to = Cast<JSReceiver>(round_to_obj); |
| } |
| |
| // 6. NOTE: (...) |
| |
| // 7. Let roundingIncrement be ? GetRoundingIncrementOption(roundTo). |
| |
| uint32_t rounding_increment; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_increment, |
| temporal::GetRoundingIncrementOption(isolate, round_to)); |
| |
| // 8. Let roundingMode be ? GetRoundingModeOption(roundTo, half-expand). |
| RoundingMode rounding_mode; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_mode, |
| temporal::GetRoundingModeOption(isolate, round_to, |
| RoundingMode::HalfExpand, method_name)); |
| |
| // 9. Let smallestUnit be ? GetTemporalUnitValuedOption(roundTo, |
| // "smallestUnit", required). |
| std::optional<Unit> smallest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, smallest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, round_to, isolate->factory()->smallestUnit_string(), |
| DefaultValue::kRequired, method_name)); |
| // 10. Perform ? ValidateTemporalUnitValue(smallestUnit, time, « day »). |
| RETURN_ON_EXCEPTION(isolate, |
| temporal::ValidateTemporalUnitValue( |
| isolate, smallest_unit, UnitGroup::kTime, Unit::Day)); |
| |
| // Rest of the steps handled in Rust |
| |
| auto options = temporal_rs::RoundingOptions{.largest_unit = std::nullopt, |
| .smallest_unit = smallest_unit, |
| .rounding_mode = rounding_mode, |
| .increment = rounding_increment}; |
| |
| auto rounded = date_time->wrapped_rust()->round(options); |
| return ConstructRustWrappingType<JSTemporalPlainDateTime>(isolate, |
| std::move(rounded)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.add |
| MaybeDirectHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::Add( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> date_time, |
| DirectHandle<Object> temporal_duration_like, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.PlainDateTime.prototype.add"; |
| // 3. Return ? AddDurationToDateTime(add, temporalDate, temporalDurationLike, |
| // options). |
| return temporal::AddDurationToGeneric( |
| isolate, &temporal_rs::PlainDateTime::add, date_time, |
| temporal_duration_like, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.subtract |
| MaybeDirectHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::Subtract( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> date_time, |
| DirectHandle<Object> temporal_duration_like, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.PlainDateTime.prototype.subtract"; |
| // 3. Return ? AddDurationToDateTime(subtract, temporalDate, |
| // temporalDurationLike, options). |
| return temporal::AddDurationToGeneric( |
| isolate, &temporal_rs::PlainDateTime::subtract, date_time, |
| temporal_duration_like, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.until |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalPlainDateTime::Until( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> handle, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.PlainDateTime.prototype.until"; |
| |
| return temporal::GenericDifferenceTemporal( |
| isolate, &temporal_rs::PlainDateTime::until, UnitGroup::kDateTime, |
| Unit::Nanosecond, handle, other, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.since |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalPlainDateTime::Since( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> handle, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.PlainDateTime.prototype.since"; |
| |
| return temporal::GenericDifferenceTemporal( |
| isolate, &temporal_rs::PlainDateTime::since, UnitGroup::kDateTime, |
| Unit::Nanosecond, handle, other, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.toplaindate |
| MaybeDirectHandle<JSTemporalPlainDate> JSTemporalPlainDateTime::ToPlainDate( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> date_time) { |
| return temporal::GenericToTemporalMethod<JSTemporalPlainDate>( |
| isolate, date_time, &temporal_rs::PlainDateTime::to_plain_date); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.toplaintime |
| MaybeDirectHandle<JSTemporalPlainTime> JSTemporalPlainDateTime::ToPlainTime( |
| Isolate* isolate, DirectHandle<JSTemporalPlainDateTime> date_time) { |
| return temporal::GenericToTemporalMethod<JSTemporalPlainTime>( |
| isolate, date_time, &temporal_rs::PlainDateTime::to_plain_time); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-handledatetimetemporaldate |
| V8_WARN_UNUSED_RESULT Maybe<int64_t> |
| JSTemporalPlainDateTime::GetEpochMillisecondsForUtc(Isolate* isolate) { |
| return ExtractRustResult(isolate, this->wrapped_rust()->epoch_ms_for_utc()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday |
| MaybeDirectHandle<JSTemporalPlainMonthDay> JSTemporalPlainMonthDay::Constructor( |
| Isolate* isolate, DirectHandle<JSFunction> target, |
| DirectHandle<HeapObject> new_target, DirectHandle<Object> iso_month_obj, |
| DirectHandle<Object> iso_day_obj, DirectHandle<Object> calendar_like, |
| DirectHandle<Object> reference_iso_year_obj) { |
| // 1. If NewTarget is undefined, then |
| if (IsUndefined(*new_target)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, |
| isolate->factory()->NewStringFromAsciiChecked( |
| "Temporal.PlainYearMonth"))); |
| } |
| |
| // 2. If referenceISOYear is undefined, then |
| // a. Set referenceISOYear to 1𝔽. |
| double reference_iso_year = 1972.0; |
| |
| // 3. Let m be ? ToIntegerWithTruncation(isoMonth). |
| double m = 0; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, m, temporal::ToIntegerWithTruncation(isolate, iso_month_obj)); |
| // 4. Let d be ? ToIntegerWithTruncation(isoYear). |
| double d = 0; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, d, temporal::ToIntegerWithTruncation(isolate, iso_day_obj)); |
| |
| // 5. If calendar is undefined, set calendar to "iso8601". |
| temporal_rs::AnyCalendarKind calendar = temporal_rs::AnyCalendarKind::Iso; |
| |
| if (!IsUndefined(*calendar_like)) { |
| // 6. If calendar is not a String, throw a TypeError exception. |
| if (!IsString(*calendar_like)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kCalendarMustBeString)); |
| } |
| |
| // 7. Set calendar to ?CanonicalizeCalendar(calendar). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar, |
| temporal::CanonicalizeCalendar(isolate, Cast<String>(calendar_like))); |
| } |
| |
| if (!IsUndefined(*reference_iso_year_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, reference_iso_year, |
| temporal::ToIntegerWithTruncation(isolate, reference_iso_year_obj)); |
| } |
| |
| // 9. If IsValidISODate(ref, m, d) is false, throw a RangeError exception. |
| if (!temporal::IsValidIsoDate(reference_iso_year, m, d)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kInvalidIsoDate)); |
| } |
| // Rest of the steps handled in Rust. |
| |
| // These static_casts are fine to perform since IsValid* will have constrained |
| // these values to range already. |
| // https://github.com/boa-dev/temporal/issues/334 for moving this logic into |
| // the Rust code. |
| auto rust_object = temporal_rs::PlainMonthDay::try_new_with_overflow( |
| static_cast<uint8_t>(m), static_cast<uint8_t>(d), calendar, |
| temporal_rs::ArithmeticOverflow::Reject, |
| static_cast<int32_t>(reference_iso_year)); |
| return ConstructRustWrappingType<JSTemporalPlainMonthDay>( |
| isolate, target, new_target, std::move(rust_object)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.from |
| MaybeDirectHandle<JSTemporalPlainMonthDay> JSTemporalPlainMonthDay::From( |
| Isolate* isolate, DirectHandle<Object> item_obj, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainMonthDay.from"; |
| DirectHandle<JSTemporalPlainMonthDay> item; |
| |
| return temporal::ToTemporalMonthDay(isolate, item_obj, options_obj, |
| method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.equals |
| MaybeDirectHandle<Oddball> JSTemporalPlainMonthDay::Equals( |
| Isolate* isolate, DirectHandle<JSTemporalPlainMonthDay> month_day, |
| DirectHandle<Object> other_obj) { |
| static const char method_name[] = "Temporal.PlainMonthDay.prototype.equals"; |
| |
| DirectHandle<JSTemporalPlainMonthDay> other; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, other, |
| temporal::ToTemporalMonthDay(isolate, other_obj, {}, method_name)); |
| |
| auto equals = month_day->wrapped_rust()->equals(*other->wrapped_rust()); |
| |
| return isolate->factory()->ToBoolean(equals); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.with |
| MaybeDirectHandle<JSTemporalPlainMonthDay> JSTemporalPlainMonthDay::With( |
| Isolate* isolate, DirectHandle<JSTemporalPlainMonthDay> temporal_month_day, |
| DirectHandle<Object> temporal_month_day_like_obj, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainYearMonth.prototype.with"; |
| |
| using enum temporal::CalendarFieldsFlag; |
| |
| // 6. Let partialMonthDay be ? PrepareCalendarFields(calendar, |
| // temporalMonthDayLike, « year, month, month-code, day », « », partial). |
| return temporal::GenericWith<JSTemporalPlainMonthDay, |
| temporal_rs::PartialDate>( |
| isolate, temporal_month_day, temporal_month_day_like_obj, options_obj, |
| kYearFields | kMonthFields | kDay, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.toplaindate |
| MaybeDirectHandle<JSTemporalPlainDate> JSTemporalPlainMonthDay::ToPlainDate( |
| Isolate* isolate, DirectHandle<JSTemporalPlainMonthDay> month_day, |
| DirectHandle<Object> item_obj) { |
| // 1. Let monthDay be the this value. |
| // 2. Perform ? RequireInternalSlot(monthDay, |
| // [[InitializedTemporalMonthDay]]). |
| |
| // (handled by type system) |
| |
| // 3. If item is not an Object, then |
| if (!IsJSReceiver(*item_obj)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kYearMustBeObject)); |
| } |
| DirectHandle<JSReceiver> item = Cast<JSReceiver>(item_obj); |
| // 4. Let calendar be monthDay.[[Calendar]]. |
| auto calendar = month_day->wrapped_rust()->calendar().kind(); |
| // 5. Let fields be ISODateToFields(calendar, monthDay.[[ISODate]], |
| // month-day). |
| |
| // (handled in Rust) |
| |
| // 6. Let inputFields be ? PrepareCalendarFields(calendar, item, « year », « |
| // », « »). |
| |
| using enum temporal::CalendarFieldsFlag; |
| temporal::CombinedRecord fields; |
| |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, fields, |
| temporal::PrepareCalendarFields(isolate, calendar, item, kYearFields, |
| temporal::RequiredFields::kNone)); |
| |
| temporal_rs::PartialDate partial_date = temporal::kNullPartialDate; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, partial_date, |
| fields.Regulate<temporal_rs::PartialDate>( |
| isolate, temporal_rs::ArithmeticOverflow::Constrain)); |
| return ConstructRustWrappingType<JSTemporalPlainDate>( |
| isolate, month_day->wrapped_rust()->to_plain_date(partial_date)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.tostring |
| MaybeDirectHandle<String> JSTemporalPlainMonthDay::ToString( |
| Isolate* isolate, DirectHandle<JSTemporalPlainMonthDay> month_day, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainMonthDay.prototype.toString"; |
| |
| // 3. Let resolvedOptions be ?GetOptionsObject(options). |
| DirectHandle<JSReceiver> options; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetOptionsObject(isolate, options_obj, method_name)); |
| |
| // 4. Let showCalendar be ?GetTemporalShowCalendarNameOption(resolvedOptions). |
| temporal_rs::DisplayCalendar show_calendar; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, show_calendar, |
| temporal::GetTemporalShowCalendarNameOption( |
| isolate, options, method_name)); |
| |
| // 5. Return TemporalYearMonthToString(monthDay, showCalendar). |
| return temporal::GenericTemporalToString( |
| isolate, month_day, &temporal_rs::PlainMonthDay::to_ixdtf_string, |
| show_calendar); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.tojson |
| MaybeDirectHandle<String> JSTemporalPlainMonthDay::ToJSON( |
| Isolate* isolate, DirectHandle<JSTemporalPlainMonthDay> month_day) { |
| return temporal::GenericTemporalToString( |
| isolate, month_day, &temporal_rs::PlainMonthDay::to_ixdtf_string, |
| temporal_rs::DisplayCalendar::Auto); |
| } |
| |
| MaybeDirectHandle<String> JSTemporalPlainMonthDay::ToLocaleString( |
| Isolate* isolate, DirectHandle<JSTemporalPlainMonthDay> month_day, |
| DirectHandle<Object> locales, DirectHandle<Object> options) { |
| #ifdef V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sup-temporal.plainmonthday.prototype.tolocalestring |
| return JSDateTimeFormat::TemporalToLocaleString( |
| isolate, month_day, locales, options, |
| JSDateTimeFormat::RequiredOption::kDate, |
| JSDateTimeFormat::DefaultsOption::kDate, |
| "Temporal.PlainMonthDay.prototype.toLocaleString"); |
| #else // V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.tolocalestring |
| return temporal::GenericTemporalToString( |
| isolate, month_day, &temporal_rs::PlainMonthDay::to_ixdtf_string, |
| temporal_rs::DisplayCalendar::Auto); |
| #endif // V8_INTL_SUPPORT |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-handledatetimetemporalmonthday |
| Maybe<int64_t> JSTemporalPlainMonthDay::GetEpochMillisecondsForUtc( |
| Isolate* isolate) { |
| return ExtractRustResult(isolate, this->wrapped_rust()->epoch_ms_for_utc()); |
| } |
| |
| MaybeDirectHandle<JSTemporalPlainYearMonth> |
| JSTemporalPlainYearMonth::Constructor( |
| Isolate* isolate, DirectHandle<JSFunction> target, |
| DirectHandle<HeapObject> new_target, DirectHandle<Object> iso_year_obj, |
| DirectHandle<Object> iso_month_obj, DirectHandle<Object> calendar_like, |
| DirectHandle<Object> reference_iso_day_obj) { |
| // 1. If NewTarget is undefined, then |
| if (IsUndefined(*new_target)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, |
| isolate->factory()->NewStringFromAsciiChecked( |
| "Temporal.PlainYearMonth"))); |
| } |
| |
| // 2. If referenceISODay is undefined, then |
| // a. Set referenceISODay to 1𝔽. |
| double reference_iso_day = 1.0; |
| |
| // 3. Let y be ? ToIntegerWithTruncation(isoYear). |
| double y = 0; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, y, temporal::ToIntegerWithTruncation(isolate, iso_year_obj)); |
| // 4. Let m be ? ToIntegerWithTruncation(isoMonth). |
| double m = 0; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, m, temporal::ToIntegerWithTruncation(isolate, iso_month_obj)); |
| |
| // 5. If calendar is undefined, set calendar to "iso8601". |
| temporal_rs::AnyCalendarKind calendar = temporal_rs::AnyCalendarKind::Iso; |
| |
| if (!IsUndefined(*calendar_like)) { |
| // 6. If calendar is not a String, throw a TypeError exception. |
| if (!IsString(*calendar_like)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kCalendarMustBeString)); |
| } |
| |
| // 7. Set calendar to ?CanonicalizeCalendar(calendar). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar, |
| temporal::CanonicalizeCalendar(isolate, Cast<String>(calendar_like))); |
| } |
| |
| if (!IsUndefined(*reference_iso_day_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, reference_iso_day, |
| temporal::ToIntegerWithTruncation(isolate, reference_iso_day_obj)); |
| } |
| |
| // 9. If IsValidISODate(y, m, ref) is false, throw a RangeError exception. |
| if (!temporal::IsValidIsoDate(y, m, reference_iso_day)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kInvalidIsoDate)); |
| } |
| // Rest of the steps handled in Rust. |
| |
| // These static_casts are fine to perform since IsValid* will have constrained |
| // these values to range already. |
| // https://github.com/boa-dev/temporal/issues/334 for moving this logic into |
| // the Rust code. |
| auto rust_object = temporal_rs::PlainYearMonth::try_new_with_overflow( |
| static_cast<int32_t>(y), static_cast<uint8_t>(m), |
| static_cast<uint8_t>(reference_iso_day), calendar, |
| temporal_rs::ArithmeticOverflow::Reject); |
| return ConstructRustWrappingType<JSTemporalPlainYearMonth>( |
| isolate, target, new_target, std::move(rust_object)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.from |
| MaybeDirectHandle<JSTemporalPlainYearMonth> JSTemporalPlainYearMonth::From( |
| Isolate* isolate, DirectHandle<Object> item_obj, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainYearMonth.from"; |
| DirectHandle<JSTemporalPlainYearMonth> item; |
| |
| return temporal::ToTemporalYearMonth(isolate, item_obj, options_obj, |
| method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.compare |
| MaybeDirectHandle<Smi> JSTemporalPlainYearMonth::Compare( |
| Isolate* isolate, DirectHandle<Object> one_obj, |
| DirectHandle<Object> two_obj) { |
| static const char method_name[] = "Temporal.PlainYearMonth.compare"; |
| DirectHandle<JSTemporalPlainYearMonth> one; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, one, |
| temporal::ToTemporalYearMonth(isolate, one_obj, {}, method_name)); |
| DirectHandle<JSTemporalPlainYearMonth> two; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, two, |
| temporal::ToTemporalYearMonth(isolate, two_obj, {}, method_name)); |
| |
| return direct_handle(Smi::FromInt(temporal_rs::PlainYearMonth::compare( |
| *one->wrapped_rust(), *two->wrapped_rust())), |
| isolate); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.equals |
| MaybeDirectHandle<Oddball> JSTemporalPlainYearMonth::Equals( |
| Isolate* isolate, DirectHandle<JSTemporalPlainYearMonth> year_month, |
| DirectHandle<Object> other_obj) { |
| static const char method_name[] = "Temporal.PlainYearMonth.prototype.equals"; |
| |
| DirectHandle<JSTemporalPlainYearMonth> other; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, other, |
| temporal::ToTemporalYearMonth(isolate, other_obj, {}, method_name)); |
| |
| auto equals = year_month->wrapped_rust()->equals(*other->wrapped_rust()); |
| |
| return isolate->factory()->ToBoolean(equals); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.add |
| MaybeDirectHandle<JSTemporalPlainYearMonth> JSTemporalPlainYearMonth::Add( |
| Isolate* isolate, DirectHandle<JSTemporalPlainYearMonth> year_month, |
| DirectHandle<Object> temporal_duration_like, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.PlainYearMonth.prototype.add"; |
| // 3. Return ? AddDurationToYearMonth(add, temporalDate, temporalDurationLike, |
| // options). |
| return temporal::AddDurationToGeneric( |
| isolate, &temporal_rs::PlainYearMonth::add, year_month, |
| temporal_duration_like, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.subtract |
| MaybeDirectHandle<JSTemporalPlainYearMonth> JSTemporalPlainYearMonth::Subtract( |
| Isolate* isolate, DirectHandle<JSTemporalPlainYearMonth> year_month, |
| DirectHandle<Object> temporal_duration_like, DirectHandle<Object> options) { |
| static const char method_name[] = |
| "Temporal.PlainYearMonth.prototype.subtract"; |
| // 3. Return ? AddDurationToYearMonth(subtract, temporalDate, |
| // temporalDurationLike, options). |
| return temporal::AddDurationToGeneric( |
| isolate, &temporal_rs::PlainYearMonth::subtract, year_month, |
| temporal_duration_like, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.until |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalPlainYearMonth::Until( |
| Isolate* isolate, DirectHandle<JSTemporalPlainYearMonth> handle, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.PlainYearMonth.prototype.until"; |
| |
| // Uplifted from step 5 of DifferenceTemporalPlainYearMonth |
| // |
| // (Differ..YearMonth) 5. Let settings be |
| // ? GetDifferenceSettings(operation, resolvedOptions, date, « week, day », |
| // month, year). |
| return temporal::GenericDifferenceTemporal( |
| isolate, &temporal_rs::PlainYearMonth::until, UnitGroup::kDate, |
| Unit::Month, handle, other, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.since |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalPlainYearMonth::Since( |
| Isolate* isolate, DirectHandle<JSTemporalPlainYearMonth> handle, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.PlainYearMonth.prototype.since"; |
| |
| // Uplifted from step 5 of DifferenceTemporalPlainYearMonth |
| // |
| // (Differ..YearMonth) 5. Let settings be |
| // ? GetDifferenceSettings(operation, resolvedOptions, date, « week, day », |
| // month, year). |
| return temporal::GenericDifferenceTemporal( |
| isolate, &temporal_rs::PlainYearMonth::since, UnitGroup::kDate, |
| Unit::Month, handle, other, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.with |
| MaybeDirectHandle<JSTemporalPlainYearMonth> JSTemporalPlainYearMonth::With( |
| Isolate* isolate, |
| DirectHandle<JSTemporalPlainYearMonth> temporal_year_month, |
| DirectHandle<Object> temporal_year_month_like_obj, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainYearMonth.prototype.with"; |
| |
| using enum temporal::CalendarFieldsFlag; |
| |
| return temporal::GenericWith<JSTemporalPlainYearMonth, |
| temporal_rs::PartialDate>( |
| isolate, temporal_year_month, temporal_year_month_like_obj, options_obj, |
| kYearFields | kMonthFields, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.toplaindate |
| MaybeDirectHandle<JSTemporalPlainDate> JSTemporalPlainYearMonth::ToPlainDate( |
| Isolate* isolate, DirectHandle<JSTemporalPlainYearMonth> year_month, |
| DirectHandle<Object> item_obj) { |
| // 1. Let yearMonth be the this value. |
| // 2. Perform ? RequireInternalSlot(yearMonth, |
| // [[InitializedTemporalYearMonth]]). |
| |
| // (handled by type system) |
| |
| // 3. If item is not an Object, then |
| if (!IsJSReceiver(*item_obj)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kYearMustBeObject)); |
| } |
| DirectHandle<JSReceiver> item = Cast<JSReceiver>(item_obj); |
| // 4. Let calendar be yearMonth.[[Calendar]]. |
| auto calendar = year_month->wrapped_rust()->calendar().kind(); |
| // 5. Let fields be ISODateToFields(calendar, yearMonth.[[ISODate]], |
| // year-month). |
| |
| // (handled in Rust) |
| |
| // 6. Let inputFields be ? PrepareCalendarFields(calendar, item, « day », « », |
| // « »). |
| |
| using enum temporal::CalendarFieldsFlag; |
| temporal::CombinedRecord fields; |
| |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, fields, |
| temporal::PrepareCalendarFields(isolate, calendar, item, kDay, |
| temporal::RequiredFields::kNone)); |
| temporal_rs::PartialDate partial_date = temporal::kNullPartialDate; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, partial_date, |
| fields.Regulate<temporal_rs::PartialDate>( |
| isolate, temporal_rs::ArithmeticOverflow::Constrain)); |
| return ConstructRustWrappingType<JSTemporalPlainDate>( |
| isolate, year_month->wrapped_rust()->to_plain_date(partial_date)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.tostring |
| MaybeDirectHandle<String> JSTemporalPlainYearMonth::ToString( |
| Isolate* isolate, DirectHandle<JSTemporalPlainYearMonth> year_month, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = |
| "Temporal.PlainYearMonth.prototype.toString"; |
| |
| // 3. Let resolvedOptions be ?GetOptionsObject(options). |
| DirectHandle<JSReceiver> options; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetOptionsObject(isolate, options_obj, method_name)); |
| |
| // 4. Let showCalendar be ?GetTemporalShowCalendarNameOption(resolvedOptions). |
| temporal_rs::DisplayCalendar show_calendar; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, show_calendar, |
| temporal::GetTemporalShowCalendarNameOption( |
| isolate, options, method_name)); |
| |
| // 5. Return TemporalYearMonthToString(yearMonth, showCalendar). |
| return temporal::GenericTemporalToString( |
| isolate, year_month, &temporal_rs::PlainYearMonth::to_ixdtf_string, |
| show_calendar); |
| } |
| |
| MaybeDirectHandle<String> JSTemporalPlainYearMonth::ToLocaleString( |
| Isolate* isolate, DirectHandle<JSTemporalPlainYearMonth> year_month, |
| DirectHandle<Object> locales, DirectHandle<Object> options) { |
| #ifdef V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sup-temporal.plainyearmonth.prototype.tolocalestring |
| return JSDateTimeFormat::TemporalToLocaleString( |
| isolate, year_month, locales, options, |
| JSDateTimeFormat::RequiredOption::kDate, |
| JSDateTimeFormat::DefaultsOption::kDate, |
| "Temporal.PlainYearMonth.prototype.toLocaleString"); |
| #else // V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.tolocalestring |
| return temporal::GenericTemporalToString( |
| isolate, year_month, &temporal_rs::PlainYearMonth::to_ixdtf_string, |
| temporal_rs::DisplayCalendar::Auto); |
| #endif // V8_INTL_SUPPORT |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-handledatetimetemporalyearmonth |
| Maybe<int64_t> JSTemporalPlainYearMonth::GetEpochMillisecondsForUtc( |
| Isolate* isolate) { |
| return ExtractRustResult(isolate, this->wrapped_rust()->epoch_ms_for_utc()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.tojson |
| MaybeDirectHandle<String> JSTemporalPlainYearMonth::ToJSON( |
| Isolate* isolate, DirectHandle<JSTemporalPlainYearMonth> year_month) { |
| return temporal::GenericTemporalToString( |
| isolate, year_month, &temporal_rs::PlainYearMonth::to_ixdtf_string, |
| temporal_rs::DisplayCalendar::Auto); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-plaintime-constructor |
| MaybeDirectHandle<JSTemporalPlainTime> JSTemporalPlainTime::Constructor( |
| Isolate* isolate, DirectHandle<JSFunction> target, |
| DirectHandle<HeapObject> new_target, DirectHandle<Object> hour_obj, |
| DirectHandle<Object> minute_obj, DirectHandle<Object> second_obj, |
| DirectHandle<Object> millisecond_obj, DirectHandle<Object> microsecond_obj, |
| DirectHandle<Object> nanosecond_obj) { |
| // 1. If NewTarget is undefined, then |
| if (IsUndefined(*new_target)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, |
| isolate->factory()->NewStringFromAsciiChecked( |
| "Temporal.PlainTime"))); |
| } |
| // 2. If hour is undefined, set hour to 0; else set hour to ? |
| // ToIntegerWithTruncation(hour). |
| double hour = 0; |
| if (!IsUndefined(*hour_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, hour, |
| temporal::ToIntegerWithTruncationOrZero(isolate, hour_obj)); |
| } |
| // 3. If minute is undefined, set minute to 0; else set minute to ? |
| // ToIntegerWithTruncation(minute). |
| double minute = 0; |
| if (!IsUndefined(*minute_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, minute, |
| temporal::ToIntegerWithTruncationOrZero(isolate, minute_obj)); |
| } |
| // 4. If second is undefined, set second to 0; else set second to ? |
| // ToIntegerWithTruncation(second). |
| double second = 0; |
| if (!IsUndefined(*second_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, second, |
| temporal::ToIntegerWithTruncationOrZero(isolate, second_obj)); |
| } |
| // 5. If millisecond is undefined, set millisecond to 0; else set millisecond |
| // to ? ToIntegerWithTruncation(millisecond). |
| double millisecond = 0; |
| if (!IsUndefined(*millisecond_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, millisecond, |
| temporal::ToIntegerWithTruncationOrZero(isolate, millisecond_obj)); |
| } |
| // 6. If microsecond is undefined, set microsecond to 0; else set microsecond |
| // to ? ToIntegerWithTruncation(microsecond). |
| |
| double microsecond = 0; |
| if (!IsUndefined(*microsecond_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, microsecond, |
| temporal::ToIntegerWithTruncationOrZero(isolate, microsecond_obj)); |
| } |
| // 7. If nanosecond is undefined, set nanosecond to 0; else set nanosecond to |
| // ? ToIntegerWithTruncation(nanosecond). |
| |
| double nanosecond = 0; |
| if (!IsUndefined(*nanosecond_obj)) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, nanosecond, |
| temporal::ToIntegerWithTruncationOrZero(isolate, nanosecond_obj)); |
| } |
| |
| // 8. If IsValidTime(hour, minute, second, millisecond, microsecond, |
| // nanosecond) is false, throw a RangeError exception. |
| if (!temporal::IsValidTime(hour, minute, second, millisecond, microsecond, |
| nanosecond)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR(kInvalidTime)); |
| } |
| |
| // Rest of the steps handled in Rust |
| |
| // These static_casts are fine to perform since IsValid* will have constrained |
| // these values to range already. |
| // https://github.com/boa-dev/temporal/issues/334 for moving this logic into |
| // the Rust code |
| auto rust_object = temporal_rs::PlainTime::try_new( |
| static_cast<uint8_t>(hour), static_cast<uint8_t>(minute), |
| static_cast<uint8_t>(second), static_cast<uint16_t>(millisecond), |
| static_cast<uint16_t>(microsecond), static_cast<uint16_t>(nanosecond)); |
| return ConstructRustWrappingType<JSTemporalPlainTime>( |
| isolate, target, new_target, std::move(rust_object)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaintime.compare |
| MaybeDirectHandle<Smi> JSTemporalPlainTime::Compare( |
| Isolate* isolate, DirectHandle<Object> one_obj, |
| DirectHandle<Object> two_obj) { |
| static const char method_name[] = "Temporal.PlainTime.compare"; |
| DirectHandle<JSTemporalPlainTime> one; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, one, |
| temporal::ToTemporalTime(isolate, one_obj, {}, method_name)); |
| DirectHandle<JSTemporalPlainTime> two; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, two, |
| temporal::ToTemporalTime(isolate, two_obj, {}, method_name)); |
| |
| return direct_handle(Smi::FromInt(temporal_rs::PlainTime::compare( |
| *one->wrapped_rust(), *two->wrapped_rust())), |
| isolate); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.equals |
| MaybeDirectHandle<Oddball> JSTemporalPlainTime::Equals( |
| Isolate* isolate, DirectHandle<JSTemporalPlainTime> temporal_time, |
| DirectHandle<Object> other_obj) { |
| static const char method_name[] = "Temporal.PlainTime.prototype.equals"; |
| |
| // 3. Set other to ? ToTemporalTime(other). |
| DirectHandle<JSTemporalPlainTime> other; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, other, |
| temporal::ToTemporalTime(isolate, other_obj, {}, method_name)); |
| |
| // Rest of the steps handled in Rust |
| auto equals = temporal_time->wrapped_rust()->equals(*other->wrapped_rust()); |
| |
| return isolate->factory()->ToBoolean(equals); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.round |
| MaybeDirectHandle<JSTemporalPlainTime> JSTemporalPlainTime::Round( |
| Isolate* isolate, DirectHandle<JSTemporalPlainTime> temporal_time, |
| DirectHandle<Object> round_to_obj) { |
| static const char method_name[] = "Temporal.PlainDateTime.prototype.round"; |
| // 1. Let temporalTime be the this value. |
| // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalTime]]). |
| |
| // (handled by type system) |
| |
| // 3. If roundTo is undefined, then |
| if (IsUndefined(*round_to_obj)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kRoundToMissing)); |
| } |
| |
| DirectHandle<JSReceiver> round_to; |
| auto factory = isolate->factory(); |
| |
| // 4. If roundTo is a String, then |
| if (IsString(*round_to_obj)) { |
| // a. Let paramString be roundTo. |
| DirectHandle<String> param_string = Cast<String>(round_to_obj); |
| // b. Set roundTo to ! OrdinaryObjectCreate(null). |
| round_to = factory->NewJSObjectWithNullProto(); |
| // TODO(manishearth) Ideally we don't have to perform this allocation |
| // c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", |
| // paramString). |
| CHECK(JSReceiver::CreateDataProperty(isolate, round_to, |
| factory->smallestUnit_string(), |
| param_string, Just(kThrowOnError)) |
| .FromJust()); |
| // 5. Else, |
| } else { |
| // a. Set roundTo to ? GetOptionsObject(roundTo). |
| // We have already checked for undefined, we can hoist the JSReceiver |
| // check out and just cast |
| if (!IsJSReceiver(*round_to_obj)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kRoundToMustBeObject)); |
| } |
| round_to = Cast<JSReceiver>(round_to_obj); |
| } |
| |
| // 6. NOTE: (...) |
| |
| // 7. Let roundingIncrement be ? GetRoundingIncrementOption(roundTo). |
| |
| uint32_t rounding_increment; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_increment, |
| temporal::GetRoundingIncrementOption(isolate, round_to)); |
| |
| // 8. Let roundingMode be ? GetRoundingModeOption(roundTo, half-expand). |
| RoundingMode rounding_mode; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_mode, |
| temporal::GetRoundingModeOption(isolate, round_to, |
| RoundingMode::HalfExpand, method_name)); |
| |
| // 9. Let smallestUnit be ? GetTemporalUnitValuedOption(roundTo, |
| // "smallestUnit", required). |
| std::optional<Unit> smallest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, smallest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, round_to, factory->smallestUnit_string(), |
| DefaultValue::kRequired, method_name)); |
| // 10. Perform ? ValidateTemporalUnitValue(smallestUnit, time). |
| RETURN_ON_EXCEPTION(isolate, temporal::ValidateTemporalUnitValue( |
| isolate, smallest_unit, UnitGroup::kTime)); |
| |
| // Rest of the steps handled in Rust |
| auto options = temporal_rs::RoundingOptions{.largest_unit = std::nullopt, |
| .smallest_unit = smallest_unit, |
| .rounding_mode = rounding_mode, |
| .increment = rounding_increment}; |
| auto rounded = temporal_time->wrapped_rust()->round(options); |
| return ConstructRustWrappingType<JSTemporalPlainTime>(isolate, |
| std::move(rounded)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.with |
| MaybeDirectHandle<JSTemporalPlainTime> JSTemporalPlainTime::With( |
| Isolate* isolate, DirectHandle<JSTemporalPlainTime> temporal_time, |
| DirectHandle<Object> temporal_time_like_obj, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainTime.prototype.with"; |
| |
| // 3. If ? IsPartialTemporalObject(temporalTimeLike) is false, throw a |
| // TypeError exception. |
| bool is_partial = false; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, is_partial, |
| temporal::IsPartialTemporalObject(isolate, temporal_time_like_obj)); |
| |
| if (!is_partial) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kWithNoPartial)); |
| } |
| |
| // 4. Let partialTime be ? ToTemporalTimeRecord(temporalTimeLike, partial). |
| temporal::TimeRecord partial_time; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, partial_time, |
| temporal::ToTemporalTimeRecord(isolate, |
| Cast<JSReceiver>(temporal_time_like_obj), |
| method_name, kPartial)); |
| |
| // Intervening steps handled by Rust, but are not externally observable |
| |
| // 17. Let resolvedOptions be ? GetOptionsObject(options). |
| // 18. Let overflow be ? GetTemporalOverflowOption(resolvedOptions). |
| |
| temporal_rs::ArithmeticOverflow overflow; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, overflow, |
| temporal::ToTemporalOverflowHandleUndefined( |
| isolate, options_obj, method_name)); |
| |
| // 19. Let result be ? RegulateTime(hour, minute, second, millisecond, |
| // microsecond, nanosecond, overflow). |
| // *technically* this wants to use a full TimeRecord object with |
| // all None fields filled from the PlainTime. However, we don't |
| // actually need to do this: RegulateTime will ignore the None |
| // fields and the Rust code below will handle the rest. |
| temporal_rs::PartialTime result; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, result, |
| partial_time.Regulate(isolate, overflow)); |
| // 20. Return ! CreateTemporalTime(result). |
| return ConstructRustWrappingType<JSTemporalPlainTime>( |
| isolate, temporal_time->wrapped_rust()->with(result, overflow)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.now.plaintimeiso |
| MaybeDirectHandle<JSTemporalPlainTime> JSTemporalPlainTime::NowISO( |
| Isolate* isolate, DirectHandle<Object> temporal_time_zone_like) { |
| std::unique_ptr<temporal_rs::ZonedDateTime> zdt; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, zdt, |
| temporal::GenericTemporalNowISO(isolate, temporal_time_zone_like)); |
| return ConstructRustWrappingType<JSTemporalPlainTime>(isolate, |
| zdt->to_plain_time()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaintime.from |
| MaybeDirectHandle<JSTemporalPlainTime> JSTemporalPlainTime::From( |
| Isolate* isolate, DirectHandle<Object> item_obj, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainTime.from"; |
| DirectHandle<JSTemporalPlainTime> item; |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, item, |
| temporal::ToTemporalTime(isolate, item_obj, options_obj, method_name)); |
| |
| return item; |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.add |
| MaybeDirectHandle<JSTemporalPlainTime> JSTemporalPlainTime::Add( |
| Isolate* isolate, DirectHandle<JSTemporalPlainTime> temporal_time, |
| DirectHandle<Object> temporal_duration_like) { |
| static const char method_name[] = "Temporal.PlainTime.prototype.add"; |
| |
| DirectHandle<JSTemporalDuration> other_duration; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, other_duration, |
| temporal::ToTemporalDuration( |
| isolate, temporal_duration_like, method_name)); |
| |
| auto added = |
| temporal_time->wrapped_rust()->add(*other_duration->wrapped_rust()); |
| |
| return ConstructRustWrappingType<JSTemporalPlainTime>(isolate, |
| std::move(added)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.subtract |
| MaybeDirectHandle<JSTemporalPlainTime> JSTemporalPlainTime::Subtract( |
| Isolate* isolate, DirectHandle<JSTemporalPlainTime> temporal_time, |
| DirectHandle<Object> temporal_duration_like) { |
| static const char method_name[] = "Temporal.PlainTime.prototype.subtract"; |
| |
| DirectHandle<JSTemporalDuration> other_duration; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, other_duration, |
| temporal::ToTemporalDuration( |
| isolate, temporal_duration_like, method_name)); |
| |
| auto subtracted = |
| temporal_time->wrapped_rust()->subtract(*other_duration->wrapped_rust()); |
| |
| return ConstructRustWrappingType<JSTemporalPlainTime>(isolate, |
| std::move(subtracted)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.until |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalPlainTime::Until( |
| Isolate* isolate, DirectHandle<JSTemporalPlainTime> handle, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.PlainTime.prototype.until"; |
| |
| return temporal::GenericDifferenceTemporal( |
| isolate, &temporal_rs::PlainTime::until, UnitGroup::kTime, |
| Unit::Nanosecond, handle, other, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.since |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalPlainTime::Since( |
| Isolate* isolate, DirectHandle<JSTemporalPlainTime> handle, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.PlainTime.prototype.since"; |
| |
| return temporal::GenericDifferenceTemporal( |
| isolate, &temporal_rs::PlainTime::since, UnitGroup::kTime, |
| Unit::Nanosecond, handle, other, options, method_name); |
| } |
| |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.tostring |
| MaybeDirectHandle<String> JSTemporalPlainTime::ToString( |
| Isolate* isolate, DirectHandle<JSTemporalPlainTime> temporal_time, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.PlainTime.prototype.toString"; |
| // 3. Let resolvedOptions be ?GetOptionsObject(options). |
| DirectHandle<JSReceiver> options; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetOptionsObject(isolate, options_obj, method_name)); |
| |
| // 5. Let digits be ?GetTemporalFractionalSecondDigitsOption(resolvedOptions). |
| |
| temporal_rs::Precision digits; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, digits, |
| temporal::GetTemporalFractionalSecondDigitsOption( |
| isolate, options, method_name)); |
| |
| // 6. Let roundingMode be ? GetRoundingModeOption(resolvedOptions, trunc). |
| |
| RoundingMode rounding_mode; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_mode, |
| temporal::GetRoundingModeOption(isolate, options, RoundingMode::Trunc, |
| method_name)); |
| |
| // 8. Let smallestUnit be ? GetTemporalUnitValuedOption(resolvedOptions, |
| // "smallestUnit", unset). |
| std::optional<Unit> smallest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, smallest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, options, isolate->factory()->smallestUnit_string(), |
| DefaultValue::kUnset, method_name)); |
| // 9. Perform ? ValidateTemporalUnitValue(smallestUnit, time). |
| RETURN_ON_EXCEPTION(isolate, temporal::ValidateTemporalUnitValue( |
| isolate, smallest_unit, UnitGroup::kTime)); |
| |
| // 8-10 performed by Rust |
| auto rust_options = temporal_rs::ToStringRoundingOptions{ |
| .precision = digits, |
| .smallest_unit = smallest_unit, |
| .rounding_mode = rounding_mode, |
| }; |
| |
| return temporal::GenericTemporalToString( |
| isolate, temporal_time, &temporal_rs::PlainTime::to_ixdtf_string, |
| std::move(rust_options)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.tojson |
| MaybeDirectHandle<String> JSTemporalPlainTime::ToJSON( |
| Isolate* isolate, DirectHandle<JSTemporalPlainTime> temporal_time) { |
| return temporal::GenericTemporalToString( |
| isolate, temporal_time, &temporal_rs::PlainTime::to_ixdtf_string, |
| std::move(temporal::kToStringAuto)); |
| } |
| |
| MaybeDirectHandle<String> JSTemporalPlainTime::ToLocaleString( |
| Isolate* isolate, DirectHandle<JSTemporalPlainTime> temporal_time, |
| DirectHandle<Object> locales, DirectHandle<Object> options) { |
| #ifdef V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sup-temporal.plaintime.prototype.tolocalestring |
| return JSDateTimeFormat::TemporalToLocaleString( |
| isolate, temporal_time, locales, options, |
| JSDateTimeFormat::RequiredOption::kTime, |
| JSDateTimeFormat::DefaultsOption::kTime, |
| "Temporal.PlainTime.prototype.toLocaleString"); |
| #else // V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.tolocalestring |
| return temporal::GenericTemporalToString( |
| isolate, temporal_time, &temporal_rs::PlainTime::to_ixdtf_string, |
| std::move(temporal::kToStringAuto)); |
| #endif // V8_INTL_SUPPORT |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-handledatetimetemporaltime |
| Maybe<int64_t> JSTemporalPlainTime::GetEpochMillisecondsForUtc( |
| Isolate* isolate) { |
| return ExtractRustResult(isolate, this->wrapped_rust()->epoch_ms_for_utc()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime |
| MaybeDirectHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::Constructor( |
| Isolate* isolate, DirectHandle<JSFunction> target, |
| DirectHandle<HeapObject> new_target, |
| DirectHandle<Object> epoch_nanoseconds_obj, |
| DirectHandle<Object> time_zone_like, DirectHandle<Object> calendar_like) { |
| // 1. If NewTarget is undefined, throw a TypeError exception. |
| if (IsUndefined(*new_target)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, |
| isolate->factory()->NewStringFromAsciiChecked( |
| "Temporal.ZonedDateTime"))); |
| } |
| |
| // 2. Set epochNanoseconds to ? ToBigInt(epochNanoseconds). |
| DirectHandle<BigInt> epoch_nanoseconds; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, epoch_nanoseconds, |
| BigInt::FromObject(isolate, epoch_nanoseconds_obj)); |
| |
| // 3. If IsValidEpochNanoseconds(epochNanoseconds) is false, throw a |
| // RangeError exception. |
| temporal_rs::I128Nanoseconds ns; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, ns, temporal::GetI128FromBigInt(isolate, epoch_nanoseconds)); |
| |
| // 4. If timeZone is not a String, throw a TypeError exception. |
| if (!IsString(*time_zone_like)) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_TYPE_ERROR("Time zone must be string")); |
| } |
| |
| auto tz_str = Cast<String>(time_zone_like); |
| |
| auto tz_stdstr = tz_str->ToStdString(); |
| |
| // 5. Let timeZoneParse be ? ParseTimeZoneIdentifier(timeZone). |
| // 6. If timeZoneParse.[[OffsetMinutes]] is empty, then |
| // a. Let identifierRecord be |
| // GetAvailableNamedTimeZoneIdentifier(timeZoneParse.[[Name]]). b. If |
| // identifierRecord is empty, throw a RangeError exception. c. Set timeZone |
| // to identifierRecord.[[Identifier]]. |
| // 7. Else, |
| // a. Set timeZone to |
| // FormatOffsetTimeZoneIdentifier(timeZoneParse.[[OffsetMinutes]]). |
| temporal_rs::TimeZone time_zone; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, time_zone, |
| ExtractRustResult( |
| isolate, temporal_rs::TimeZone::try_from_identifier_str_with_provider( |
| tz_stdstr, TimeZoneProvider()))); |
| |
| // 8. If calendar is undefined, set calendar to "iso8601". |
| temporal_rs::AnyCalendarKind calendar = temporal_rs::AnyCalendarKind::Iso; |
| |
| // We pre-parsed "iso8601" so we can do steps 9 and 10 in a branch |
| if (!IsUndefined(*calendar_like)) { |
| // 9. If calendar is not a String, throw a TypeError exception. |
| if (!IsString(*calendar_like)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kCalendarMustBeString)); |
| } |
| |
| // 10. Set calendar to ? CanonicalizeCalendar(calendar). |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, calendar, |
| temporal::CanonicalizeCalendar(isolate, Cast<String>(calendar_like)), |
| kNullMaybeHandle); |
| } |
| |
| // 11. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, |
| // calendar, NewTarget). |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>( |
| isolate, target, new_target, |
| temporal_rs::ZonedDateTime::try_new_with_provider(ns, calendar, time_zone, |
| TimeZoneProvider())); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.hoursinday |
| MaybeDirectHandle<Number> JSTemporalZonedDateTime::HoursInDay( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| double hours; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, hours, |
| ExtractRustResult( |
| isolate, zoned_date_time->wrapped_rust()->hours_in_day_with_provider( |
| TimeZoneProvider()))); |
| return isolate->factory()->NewNumber(hours); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.from |
| MaybeDirectHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::From( |
| Isolate* isolate, DirectHandle<Object> item_obj, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.ZonedDateTime.from"; |
| DirectHandle<JSTemporalPlainDateTime> item; |
| |
| return temporal::ToTemporalZonedDateTime(isolate, item_obj, options_obj, |
| method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.compare |
| MaybeDirectHandle<Smi> JSTemporalZonedDateTime::Compare( |
| Isolate* isolate, DirectHandle<Object> one_obj, |
| DirectHandle<Object> two_obj) { |
| static const char method_name[] = "Temporal.ZonedDateTime.compare"; |
| DirectHandle<JSTemporalZonedDateTime> one; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, one, |
| temporal::ToTemporalZonedDateTime(isolate, one_obj, {}, method_name)); |
| DirectHandle<JSTemporalZonedDateTime> two; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, two, |
| temporal::ToTemporalZonedDateTime(isolate, two_obj, {}, method_name)); |
| |
| return direct_handle( |
| Smi::FromInt(one->wrapped_rust()->compare_instant(*two->wrapped_rust())), |
| isolate); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.equals |
| MaybeDirectHandle<Oddball> JSTemporalZonedDateTime::Equals( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time, |
| DirectHandle<Object> other_obj) { |
| static const char method_name[] = "Temporal.ZonedDateTime.prototype.equals"; |
| |
| // 3. Set other to ? ToTemporalZonedDateTime(other). |
| DirectHandle<JSTemporalZonedDateTime> other; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, other, |
| temporal::ToTemporalZonedDateTime(isolate, other_obj, {}, method_name)); |
| |
| // Rest of the steps handled in Rust. |
| auto result = zoned_date_time->wrapped_rust()->equals_with_provider( |
| *other->wrapped_rust(), TimeZoneProvider()); |
| bool equals; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, equals, |
| ExtractRustResult(isolate, std::move(result))); |
| |
| return isolate->factory()->ToBoolean(equals); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.with |
| MaybeDirectHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::With( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time, |
| DirectHandle<Object> temporal_zoned_date_time_like_obj, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.ZonedDateTime.prototype.with"; |
| |
| using enum temporal::CalendarFieldsFlag; |
| |
| return temporal::GenericWith<JSTemporalZonedDateTime, |
| temporal_rs::PartialZonedDateTime>( |
| isolate, zoned_date_time, temporal_zoned_date_time_like_obj, options_obj, |
| temporal::kAllDateFlags | kTimeFields | kOffset, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.withcalendar |
| MaybeDirectHandle<JSTemporalZonedDateTime> |
| JSTemporalZonedDateTime::WithCalendar( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> temporal_date, |
| DirectHandle<Object> calendar_id) { |
| // 3. Let calendar be ? ToTemporalCalendarIdentifier(calendarLike). |
| temporal_rs::AnyCalendarKind calendar_kind; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar_kind, |
| temporal::ToTemporalCalendarIdentifier(isolate, calendar_id)); |
| |
| // Rest of the steps handled in Rust |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>( |
| isolate, temporal_date->wrapped_rust()->with_calendar(calendar_kind)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.withplaintime |
| MaybeDirectHandle<JSTemporalZonedDateTime> |
| JSTemporalZonedDateTime::WithPlainTime( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time, |
| DirectHandle<Object> plain_time_like) { |
| static const char method_name[] = |
| "Temporal.ZonedDateTime.prototype.withPlainTime"; |
| // 1. Let zonedDateTime be the this value. |
| // |
| // 2. Perform ? RequireInternalSlot(zonedDateTime, |
| // [[InitializedTemporalZonedDateTime]]). |
| // |
| // 3. Let timeZone be zonedDateTime.[[TimeZone]]. |
| // |
| // 4. Let calendar be zonedDateTime.[[Calendar]]. |
| |
| // (handled by type system) |
| |
| // 5. Let isoDateTime be GetISODateTimeFor(timeZone, |
| // zonedDateTime.[[EpochNanoseconds]]). |
| |
| // (handled later in Rust, idempotent) |
| |
| DirectHandle<JSTemporalPlainTime> plain_time_obj; |
| CppGCManaged<temporal_rs::PlainTime>::Ptr plain_time; |
| |
| // 6. If plainTimeLike is undefined, then |
| if (IsUndefined(*plain_time_like)) { |
| // a. Let epochNs be ? GetStartOfDay(timeZone, isoDateTime.[[ISODate]]). |
| |
| // (handled later in Rust, there are no steps between this and the Rust |
| // call. Rust handles this by detecting a null time argument) |
| |
| // 7. Else, |
| } else { |
| // a. Let plainTime be ? ToTemporalTime(plainTimeLike). |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, plain_time_obj, |
| temporal::ToTemporalTime(isolate, plain_time_like, {}, method_name)); |
| |
| plain_time = plain_time_obj->wrapped_rust(); |
| // b. Let resultISODateTime be |
| // CombineISODateAndTimeRecord(isoDateTime.[[ISODate]], plainTime.[[Time]]). |
| // c. Let epochNs be ? GetEpochNanosecondsFor(timeZone, resultISODateTime, |
| // compatible). |
| |
| // (handled later in Rust, there are no steps between this and the Rust |
| // call) |
| } |
| |
| // Rest of the steps handled in Rust. |
| |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>( |
| isolate, zoned_date_time->wrapped_rust()->with_plain_time_and_provider( |
| plain_time.raw(), TimeZoneProvider())); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.withtimezone |
| MaybeDirectHandle<JSTemporalZonedDateTime> |
| JSTemporalZonedDateTime::WithTimeZone( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time, |
| DirectHandle<Object> time_zone_like) { |
| // 3. Let timeZone be ? ToTemporalTimeZoneIdentifier(timeZoneLike). |
| temporal_rs::TimeZone time_zone; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, time_zone, |
| temporal::ToTemporalTimeZoneIdentifier(isolate, time_zone_like)); |
| |
| // 4. Return ! CreateTemporalZonedDateTime(zonedDateTime.[[EpochNanoseconds]], |
| // timeZone, zonedDateTime.[[Calendar]]). |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>( |
| isolate, zoned_date_time->wrapped_rust()->with_timezone_with_provider( |
| time_zone, TimeZoneProvider())); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.tostring |
| MaybeDirectHandle<String> JSTemporalZonedDateTime::ToString( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.ZonedDateTime.prototype.toString"; |
| |
| // 3. Let resolvedOptions be ?GetOptionsObject(options). |
| DirectHandle<JSReceiver> options; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetOptionsObject(isolate, options_obj, method_name)); |
| |
| // 5. Let showCalendar be ?GetTemporalShowCalendarNameOption(resolvedOptions). |
| temporal_rs::DisplayCalendar show_calendar; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, show_calendar, |
| temporal::GetTemporalShowCalendarNameOption( |
| isolate, options, method_name)); |
| |
| // 6. Let digits be ?GetTemporalFractionalSecondDigitsOption(resolvedOptions). |
| temporal_rs::Precision digits; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, digits, |
| temporal::GetTemporalFractionalSecondDigitsOption( |
| isolate, options, method_name)); |
| |
| // 7. Let showOffset be ? GetTemporalShowOffsetOption(resolvedOptions).. |
| temporal_rs::DisplayOffset show_offset; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, show_offset, |
| temporal::GetTemporalShowOffsetOption(isolate, options, method_name)); |
| |
| // 8. Let roundingMode be ? GetRoundingModeOption(resolvedOptions, trunc). |
| RoundingMode rounding_mode; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_mode, |
| temporal::GetRoundingModeOption(isolate, options, RoundingMode::Trunc, |
| method_name)); |
| |
| // 9. Let smallestUnit be ? GetTemporalUnitValuedOption(resolvedOptions, |
| // "smallestUnit", unset). |
| std::optional<Unit> smallest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, smallest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, options, isolate->factory()->smallestUnit_string(), |
| DefaultValue::kUnset, method_name)); |
| |
| // 10. Let showTimeZone be |
| // ? GetTemporalShowTimeZoneNameOption(resolvedOptions). |
| temporal_rs::DisplayTimeZone show_tz; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, show_tz, |
| temporal::GetTemporalShowTimeZoneNameOption( |
| isolate, options, method_name)); |
| |
| // 11. Perform ? ValidateTemporalUnitValue(smallestUnit, time). |
| RETURN_ON_EXCEPTION(isolate, temporal::ValidateTemporalUnitValue( |
| isolate, smallest_unit, UnitGroup::kTime)); |
| |
| // 12. If smallestUnit is hour, throw a RangeError exception. |
| if (smallest_unit == Unit::Hour) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_RANGE_ERROR("smallestUnit cannot be Hour.")); |
| } |
| |
| // Rest of the steps handled in Rust |
| auto rust_options = temporal_rs::ToStringRoundingOptions{ |
| .precision = digits, |
| .smallest_unit = smallest_unit, |
| .rounding_mode = rounding_mode, |
| }; |
| return temporal::GenericTemporalToString( |
| isolate, zoned_date_time, |
| &temporal_rs::ZonedDateTime::to_ixdtf_string_with_provider, show_offset, |
| show_tz, show_calendar, std::move(rust_options), |
| std::ref(TimeZoneProvider())); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.tojson |
| MaybeDirectHandle<String> JSTemporalZonedDateTime::ToJSON( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| return temporal::GenericTemporalToString( |
| isolate, zoned_date_time, |
| &temporal_rs::ZonedDateTime::to_ixdtf_string_with_provider, |
| temporal_rs::DisplayOffset::Auto, temporal_rs::DisplayTimeZone::Auto, |
| temporal_rs::DisplayCalendar::Auto, std::move(temporal::kToStringAuto), |
| std::ref(TimeZoneProvider())); |
| } |
| |
| MaybeDirectHandle<String> JSTemporalZonedDateTime::ToLocaleString( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time, |
| DirectHandle<Object> locales, DirectHandle<Object> options) { |
| #ifdef V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sub-temporal.zoneddatetime.prototype.tolocalestring |
| return JSDateTimeFormat::TemporalZonedDateTimeToLocaleString( |
| isolate, zoned_date_time, locales, options, |
| "Temporal.ZonedDateTime.prototype.toLocaleString"); |
| #else // V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.tolocalestring |
| return temporal::GenericTemporalToString( |
| isolate, zoned_date_time, |
| &temporal_rs::ZonedDateTime::to_ixdtf_string_with_provider, |
| temporal_rs::DisplayOffset::Auto, temporal_rs::DisplayTimeZone::Auto, |
| temporal_rs::DisplayCalendar::Auto, std::move(temporal::kToStringAuto), |
| std::ref(TimeZoneProvider())); |
| #endif // V8_INTL_SUPPORT |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.now.zoneddatetimeiso |
| MaybeDirectHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::NowISO( |
| Isolate* isolate, DirectHandle<Object> temporal_time_zone_like) { |
| std::unique_ptr<temporal_rs::ZonedDateTime> zdt; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, zdt, |
| temporal::GenericTemporalNowISO(isolate, temporal_time_zone_like)); |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>(isolate, |
| std::move(zdt)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.round |
| MaybeDirectHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::Round( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time, |
| DirectHandle<Object> round_to_obj) { |
| static const char method_name[] = "Temporal.PlainDateTime.prototype.round"; |
| // 1. Let zonedDateTime be the this value. |
| // 2. Perform ? RequireInternalSlot(zonedDateTime, |
| // [[InitializedTemporalZonedDateTime]]). |
| |
| // (handled by type system) |
| |
| // 3. If roundTo is undefined, then |
| if (IsUndefined(*round_to_obj)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kRoundToMissing)); |
| } |
| |
| DirectHandle<JSReceiver> round_to; |
| auto factory = isolate->factory(); |
| |
| // 4. If roundTo is a String, then |
| if (IsString(*round_to_obj)) { |
| // a. Let paramString be roundTo. |
| DirectHandle<String> param_string = Cast<String>(round_to_obj); |
| // b. Set roundTo to ! OrdinaryObjectCreate(null). |
| round_to = factory->NewJSObjectWithNullProto(); |
| // c. c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", |
| // paramString). |
| CHECK(JSReceiver::CreateDataProperty(isolate, round_to, |
| factory->smallestUnit_string(), |
| param_string, Just(kThrowOnError)) |
| .FromJust()); |
| // 5. Else, |
| } else { |
| // a. Set roundTo to ? GetOptionsObject(roundTo). |
| // We have already checked for undefined, we can hoist the JSReceiver |
| // check out and just cast |
| if (!IsJSReceiver(*round_to_obj)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kRoundToMustBeObject)); |
| } |
| round_to = Cast<JSReceiver>(round_to_obj); |
| } |
| |
| // 6. NOTE: (...) |
| |
| // 7. Let roundingIncrement be ? GetRoundingIncrementOption(roundTo). |
| |
| uint32_t rounding_increment; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_increment, |
| temporal::GetRoundingIncrementOption(isolate, round_to)); |
| |
| // 8. Let roundingMode be ? GetRoundingModeOption(roundTo, half-expand). |
| RoundingMode rounding_mode; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_mode, |
| temporal::GetRoundingModeOption(isolate, round_to, |
| RoundingMode::HalfExpand, method_name)); |
| |
| // 9. Let smallestUnit be ? GetTemporalUnitValuedOption(roundTo, |
| // "smallestUnit", required). |
| std::optional<Unit> smallest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, smallest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, round_to, isolate->factory()->smallestUnit_string(), |
| DefaultValue::kRequired, method_name)); |
| |
| // 10. Perform ? ValidateTemporalUnitValue(smallestUnit, time, « day »). |
| RETURN_ON_EXCEPTION(isolate, |
| temporal::ValidateTemporalUnitValue( |
| isolate, smallest_unit, UnitGroup::kTime, Unit::Day)); |
| |
| // Rest of the steps handled in Rust |
| |
| auto options = temporal_rs::RoundingOptions{.largest_unit = std::nullopt, |
| .smallest_unit = smallest_unit, |
| .rounding_mode = rounding_mode, |
| .increment = rounding_increment}; |
| |
| auto rounded = zoned_date_time->wrapped_rust()->round_with_provider( |
| options, TimeZoneProvider()); |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>(isolate, |
| std::move(rounded)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.add |
| MaybeDirectHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::Add( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time, |
| DirectHandle<Object> temporal_duration_like, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.ZonedDateTime.prototype.add"; |
| // 3. Return ? AddDurationToZonedDateTime(add, temporalDate, |
| // temporalDurationLike, options). |
| return temporal::AddDurationToGeneric( |
| isolate, &temporal_rs::ZonedDateTime::add_with_provider, zoned_date_time, |
| temporal_duration_like, options, method_name, TimeZoneProvider()); |
| } |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.subtract |
| MaybeDirectHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::Subtract( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time, |
| DirectHandle<Object> temporal_duration_like, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.ZonedDateTime.prototype.subtract"; |
| // 3. Return ? AddDurationToZonedDateTime(subtract, temporalDate, |
| // temporalDurationLike, options). |
| return temporal::AddDurationToGeneric( |
| isolate, &temporal_rs::ZonedDateTime::subtract_with_provider, |
| zoned_date_time, temporal_duration_like, options, method_name, |
| TimeZoneProvider()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.until |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalZonedDateTime::Until( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> handle, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.ZonedDateTime.prototype.since"; |
| return temporal::GenericDifferenceTemporal( |
| isolate, &temporal_rs::ZonedDateTime::until_with_provider, |
| UnitGroup::kDateTime, Unit::Nanosecond, handle, other, options, |
| method_name, TimeZoneProvider()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.since |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalZonedDateTime::Since( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> handle, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.ZonedDateTime.prototype.since"; |
| return temporal::GenericDifferenceTemporal( |
| isolate, &temporal_rs::ZonedDateTime::since_with_provider, |
| UnitGroup::kDateTime, Unit::Nanosecond, handle, other, options, |
| method_name, TimeZoneProvider()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.now.instant |
| MaybeDirectHandle<JSTemporalInstant> JSTemporalInstant::Now(Isolate* isolate) { |
| auto ns = temporal::SystemUTCEpochNanoseconds(isolate); |
| return ConstructRustWrappingType<JSTemporalInstant>( |
| isolate, temporal_rs::Instant::try_new(ns)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds |
| MaybeDirectHandle<Object> JSTemporalZonedDateTime::OffsetNanoseconds( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| int64_t offset_ns = zoned_date_time->wrapped_rust()->offset_nanoseconds(); |
| return isolate->factory()->NewNumberFromInt64(offset_ns); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.epochnanoseconds |
| MaybeDirectHandle<BigInt> JSTemporalZonedDateTime::EpochNanoseconds( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| return temporal::I128ToBigInt( |
| isolate, zoned_date_time->wrapped_rust()->epoch_nanoseconds()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.timezoneid |
| MaybeDirectHandle<String> JSTemporalZonedDateTime::TimeZoneId( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| std::string id; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, id, |
| ExtractRustResult( |
| isolate, |
| zoned_date_time->wrapped_rust()->timezone().identifier_with_provider( |
| TimeZoneProvider()))); |
| |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendString(id); |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.offset |
| MaybeDirectHandle<String> JSTemporalZonedDateTime::Offset( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| |
| std::string offset; |
| |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, offset, |
| ExtractRustResult(isolate, zoned_date_time->wrapped_rust()->offset())); |
| |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendString(offset); |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.startofday |
| MaybeDirectHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::StartOfDay( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>( |
| isolate, zoned_date_time->wrapped_rust()->start_of_day_with_provider( |
| TimeZoneProvider())); |
| } |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.gettimezonetransition |
| MaybeDirectHandle<UnionOf<JSTemporalZonedDateTime, Null>> |
| JSTemporalZonedDateTime::GetTimeZoneTransition( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time, |
| DirectHandle<Object> direction_param_obj) { |
| static const char method_name[] = |
| "Temporal.ZonedDateTime.prototype.getTimeZoneTransition"; |
| |
| // 4. If directionParam is undefined, throw a TypeError exception. |
| if (IsUndefined(*direction_param_obj)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR( |
| "Must specify a direction parameter.")); |
| } |
| |
| // 3. If roundTo is undefined, then |
| |
| DirectHandle<JSReceiver> direction_param; |
| auto factory = isolate->factory(); |
| |
| // 5. If directionParam is a String, then |
| if (IsString(*direction_param_obj)) { |
| // a. a. Let paramString be directionParam. |
| DirectHandle<String> param_string = Cast<String>(direction_param_obj); |
| // b. Set directionParam to OrdinaryObjectCreate(null). |
| direction_param = factory->NewJSObjectWithNullProto(); |
| // TODO(manishearth) Ideally we don't have to perform this allocation |
| // c. Perform ! CreateDataPropertyOrThrow(directionParam, "direction", |
| // paramString). |
| CHECK(JSReceiver::CreateDataProperty(isolate, direction_param, |
| factory->direction_string(), |
| param_string, Just(kThrowOnError)) |
| .FromJust()); |
| // 6. Else, |
| } else { |
| // a. Set directionParam to ? GetOptionsObject(directionParam). |
| // We have already checked for undefined, we can hoist the JSReceiver |
| // check out and just cast |
| if (!IsJSReceiver(*direction_param_obj)) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR( |
| "directionParam must be object or string.")); |
| } |
| direction_param = Cast<JSReceiver>(direction_param_obj); |
| } |
| |
| // 7. Let direction be ? GetDirectionOption(directionParam). |
| temporal_rs::TransitionDirection dir; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, dir, |
| temporal::GetDirectionOption(isolate, direction_param, method_name)); |
| |
| // Steps 8-10 handled in Rust. |
| std::unique_ptr<temporal_rs::ZonedDateTime> zdt; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, zdt, |
| ExtractRustResult(isolate, zoned_date_time->wrapped_rust() |
| ->get_time_zone_transition_with_provider( |
| dir, TimeZoneProvider()))); |
| |
| // 11. If transition is null, return null. |
| if (!zdt) { |
| return isolate->factory()->null_value(); |
| } |
| |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>(isolate, |
| std::move(zdt)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.toinstant |
| MaybeDirectHandle<JSTemporalInstant> JSTemporalZonedDateTime::ToInstant( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| return temporal::GenericToTemporalMethod<JSTemporalInstant>( |
| isolate, zoned_date_time, &temporal_rs::ZonedDateTime::to_instant); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.toplaindate |
| MaybeDirectHandle<JSTemporalPlainDate> JSTemporalZonedDateTime::ToPlainDate( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| return temporal::GenericToTemporalMethod<JSTemporalPlainDate>( |
| isolate, zoned_date_time, &temporal_rs::ZonedDateTime::to_plain_date); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.toplaintime |
| MaybeDirectHandle<JSTemporalPlainTime> JSTemporalZonedDateTime::ToPlainTime( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| return temporal::GenericToTemporalMethod<JSTemporalPlainTime>( |
| isolate, zoned_date_time, &temporal_rs::ZonedDateTime::to_plain_time); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.toplaindatetime |
| MaybeDirectHandle<JSTemporalPlainDateTime> |
| JSTemporalZonedDateTime::ToPlainDateTime( |
| Isolate* isolate, DirectHandle<JSTemporalZonedDateTime> zoned_date_time) { |
| return temporal::GenericToTemporalMethod<JSTemporalPlainDateTime>( |
| isolate, zoned_date_time, &temporal_rs::ZonedDateTime::to_plain_datetime); |
| } |
| |
| namespace temporal { |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal-createtemporalinstant, but |
| // this also performs the validity check |
| MaybeDirectHandle<JSTemporalInstant> CreateTemporalInstantWithValidityCheck( |
| Isolate* isolate, DirectHandle<JSFunction> target, |
| DirectHandle<HeapObject> new_target, |
| DirectHandle<BigInt> epoch_nanoseconds) { |
| |
| temporal_rs::I128Nanoseconds ns; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, ns, |
| GetI128FromBigInt(isolate, epoch_nanoseconds)); |
| |
| return ConstructRustWrappingType<JSTemporalInstant>( |
| isolate, target, new_target, temporal_rs::Instant::try_new(ns)); |
| } |
| |
| MaybeDirectHandle<JSTemporalInstant> CreateTemporalInstantWithValidityCheck( |
| Isolate* isolate, DirectHandle<BigInt> epoch_nanoseconds) { |
| auto ctor = JSTemporalInstant::GetConstructorTarget(isolate); |
| return CreateTemporalInstantWithValidityCheck(isolate, ctor, ctor, |
| epoch_nanoseconds); |
| } |
| |
| } // namespace temporal |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant |
| MaybeDirectHandle<JSTemporalInstant> JSTemporalInstant::Constructor( |
| Isolate* isolate, DirectHandle<JSFunction> target, |
| DirectHandle<HeapObject> new_target, |
| DirectHandle<Object> epoch_nanoseconds_obj) { |
| // 1. If NewTarget is undefined, then |
| if (IsUndefined(*new_target)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, |
| isolate->factory()->NewStringFromAsciiChecked( |
| "Temporal.Instant"))); |
| } |
| // 2. Let epochNanoseconds be ? ToBigInt(epochNanoseconds). |
| DirectHandle<BigInt> epoch_nanoseconds; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, epoch_nanoseconds, |
| BigInt::FromObject(isolate, epoch_nanoseconds_obj)); |
| |
| return temporal::CreateTemporalInstantWithValidityCheck( |
| isolate, target, new_target, epoch_nanoseconds); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.from |
| MaybeDirectHandle<JSTemporalInstant> JSTemporalInstant::From( |
| Isolate* isolate, DirectHandle<Object> item) { |
| static const char method_name[] = "Temporal.Instant.from"; |
| DirectHandle<JSTemporalInstant> item_instant; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, item_instant, |
| temporal::ToTemporalInstant(isolate, item, method_name)); |
| |
| return item_instant; |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.fromepochmilliseconds |
| MaybeDirectHandle<JSTemporalInstant> JSTemporalInstant::FromEpochMilliseconds( |
| Isolate* isolate, DirectHandle<Object> epoch_milliseconds) { |
| // 1. Let number be ? ToNumber(epochMilliseconds). |
| DirectHandle<Number> number; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, number, |
| Object::ToNumber(isolate, epoch_milliseconds)); |
| double ms = Object::NumberValue(*number); |
| |
| // 2. Set epochMilliseconds to ?NumberToBigInt(epochMilliseconds). |
| // |
| // Instead of allocating a BigInt, we perform the necessary check from |
| // NumberToBigInt and cast: |
| // |
| // (NumberToBigInt) 1. If number is not an integral Number, throw a RangeError |
| // exception. |
| if (!std::isfinite(ms) || |
| !base::IsValueInRangeForNumericType<int64_t, double>(ms) || |
| nearbyint(ms) != ms) { |
| THROW_NEW_ERROR(isolate, |
| NEW_TEMPORAL_RANGE_ERROR("Expected finite integer.")); |
| } |
| auto ms_int = static_cast<int64_t>(ms); |
| |
| // 3. Let epochNanoseconds be epochMilliseconds × ℤ(10**6). |
| // 4. If IsValidEpochNanoseconds(epochNanoseconds) is false, throw a |
| // RangeError exception. |
| // |
| // (Rest of the steps handled by Rust) |
| |
| return ConstructRustWrappingType<JSTemporalInstant>( |
| isolate, temporal_rs::Instant::from_epoch_milliseconds(ms_int)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.fromepochnanoseconds |
| MaybeDirectHandle<JSTemporalInstant> JSTemporalInstant::FromEpochNanoseconds( |
| Isolate* isolate, DirectHandle<Object> epoch_nanoseconds_obj) { |
| // 1. Set epochNanoseconds to ? ToBigInt(epochNanoseconds). |
| DirectHandle<BigInt> epoch_nanoseconds; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, epoch_nanoseconds, |
| BigInt::FromObject(isolate, epoch_nanoseconds_obj)); |
| |
| // Rest of the steps handled by CreateTemporalInstantWithValidityCheck |
| |
| return temporal::CreateTemporalInstantWithValidityCheck(isolate, |
| epoch_nanoseconds); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.compare |
| MaybeDirectHandle<Smi> JSTemporalInstant::Compare( |
| Isolate* isolate, DirectHandle<Object> one_obj, |
| DirectHandle<Object> two_obj) { |
| static const char method_name[] = "Temporal.Instant.compare"; |
| DirectHandle<JSTemporalInstant> one; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, one, temporal::ToTemporalInstant(isolate, one_obj, method_name)); |
| DirectHandle<JSTemporalInstant> two; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, two, temporal::ToTemporalInstant(isolate, two_obj, method_name)); |
| |
| return direct_handle( |
| Smi::FromInt(one->wrapped_rust()->compare(*two->wrapped_rust())), |
| isolate); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.equals |
| MaybeDirectHandle<Oddball> JSTemporalInstant::Equals( |
| Isolate* isolate, DirectHandle<JSTemporalInstant> handle, |
| DirectHandle<Object> other_obj) { |
| static const char method_name[] = "Temporal.Instant.prototype.equals"; |
| |
| DirectHandle<JSTemporalInstant> other; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, other, |
| temporal::ToTemporalInstant(isolate, other_obj, method_name)); |
| |
| auto this_ns = handle->wrapped_rust()->epoch_nanoseconds(); |
| auto other_ns = other->wrapped_rust()->epoch_nanoseconds(); |
| |
| // equals() isn't exposed over FFI, but it's easy enough to do here |
| // in the future we can use https://github.com/boa-dev/temporal/pull/311 |
| return isolate->factory()->ToBoolean(this_ns.high == other_ns.high && |
| this_ns.low == other_ns.low); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.round |
| MaybeDirectHandle<JSTemporalInstant> JSTemporalInstant::Round( |
| Isolate* isolate, DirectHandle<JSTemporalInstant> handle, |
| DirectHandle<Object> round_to_obj) { |
| static const char method_name[] = "Temporal.Instant.prototype.round"; |
| Factory* factory = isolate->factory(); |
| // 1. Let instant be the this value. |
| // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]). |
| // 3. If roundTo is undefined, then |
| if (IsUndefined(*round_to_obj)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR(kRoundToMissing)); |
| } |
| DirectHandle<JSReceiver> round_to; |
| // 4. If Type(roundTo) is String, then |
| if (IsString(*round_to_obj)) { |
| // TODO(415359720) This could be done more efficiently, if we had better |
| // GetStringOption APIs |
| // a. Let paramString be roundTo. |
| DirectHandle<String> param_string = Cast<String>(round_to_obj); |
| // b. Set roundTo to ! OrdinaryObjectCreate(null). |
| round_to = factory->NewJSObjectWithNullProto(); |
| // c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", |
| // paramString). |
| CHECK(JSReceiver::CreateDataProperty(isolate, round_to, |
| factory->smallestUnit_string(), |
| param_string, Just(kThrowOnError)) |
| .FromJust()); |
| } else { |
| // a. Set roundTo to ? GetOptionsObject(roundTo). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, round_to, |
| GetOptionsObject(isolate, round_to_obj, method_name)); |
| } |
| |
| // 7. Let roundingIncrement be ? GetRoundingIncrementOption(roundTo). |
| uint32_t rounding_increment; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_increment, |
| temporal::GetRoundingIncrementOption(isolate, round_to)); |
| |
| // 8. Let roundingMode be ? GetRoundingModeOption(roundTo, half-expand). |
| RoundingMode rounding_mode; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_mode, |
| temporal::GetRoundingModeOption(isolate, round_to, |
| RoundingMode::HalfExpand, method_name)); |
| |
| // 9. Let smallestUnit be ? GetTemporalUnitValuedOption(roundTo, |
| // "smallestUnit", required). |
| std::optional<Unit> smallest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, smallest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, round_to, isolate->factory()->smallestUnit_string(), |
| DefaultValue::kRequired, method_name)); |
| // 10. Perform ? ValidateTemporalUnitValue(smallestUnit, time). |
| RETURN_ON_EXCEPTION(isolate, temporal::ValidateTemporalUnitValue( |
| isolate, smallest_unit, UnitGroup::kTime)); |
| |
| auto options = temporal_rs::RoundingOptions{.largest_unit = std::nullopt, |
| .smallest_unit = smallest_unit, |
| .rounding_mode = rounding_mode, |
| .increment = rounding_increment}; |
| |
| auto rounded = handle->wrapped_rust()->round(options); |
| return ConstructRustWrappingType<JSTemporalInstant>(isolate, |
| std::move(rounded)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.epochmilliseconds |
| MaybeDirectHandle<Number> JSTemporalInstant::EpochMilliseconds( |
| Isolate* isolate, DirectHandle<JSTemporalInstant> handle) { |
| int64_t ms = handle->wrapped_rust()->epoch_milliseconds(); |
| |
| return isolate->factory()->NewNumberFromInt64(ms); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.epochnanoseconds |
| MaybeDirectHandle<BigInt> JSTemporalInstant::EpochNanoseconds( |
| Isolate* isolate, DirectHandle<JSTemporalInstant> handle) { |
| return temporal::I128ToBigInt(isolate, |
| handle->wrapped_rust()->epoch_nanoseconds()); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tozoneddatetime |
| MaybeDirectHandle<JSTemporalZonedDateTime> |
| JSTemporalInstant::ToZonedDateTimeISO(Isolate* isolate, |
| DirectHandle<JSTemporalInstant> instant, |
| DirectHandle<Object> time_zone_obj) { |
| // 3. Let timeZone be ? ToTemporalTimeZoneIdentifier(temporalTimeZoneLike). |
| temporal_rs::TimeZone time_zone; |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, time_zone, |
| temporal::ToTemporalTimeZoneIdentifier(isolate, time_zone_obj)); |
| |
| return ConstructRustWrappingType<JSTemporalZonedDateTime>( |
| isolate, instant->wrapped_rust()->to_zoned_date_time_iso_with_provider( |
| time_zone, TimeZoneProvider())); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tostring |
| MaybeDirectHandle<String> JSTemporalInstant::ToString( |
| Isolate* isolate, DirectHandle<JSTemporalInstant> instant, |
| DirectHandle<Object> options_obj) { |
| static const char method_name[] = "Temporal.Instant.prototype.toString"; |
| |
| // 3. Set options to ? GetOptionsObject(options). |
| DirectHandle<JSReceiver> options; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetOptionsObject(isolate, options_obj, method_name)); |
| |
| // 5. Let digits be ? |
| // GetTemporalFractionalSecondDigitsOption(resolvedOptions). |
| |
| temporal_rs::Precision digits; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, digits, |
| temporal::GetTemporalFractionalSecondDigitsOption( |
| isolate, options, method_name)); |
| |
| // 6. Let roundingMode be ? GetRoundingModeOption(resolvedOptions, trunc). |
| |
| RoundingMode rounding_mode; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, rounding_mode, |
| temporal::GetRoundingModeOption(isolate, options, RoundingMode::Trunc, |
| method_name)); |
| |
| // 7. Let smallestUnit be ? GetTemporalUnitValuedOption(resolvedOptions, |
| // "smallestUnit", unset). |
| std::optional<Unit> smallest_unit; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, smallest_unit, |
| temporal::GetTemporalUnitValuedOption( |
| isolate, options, isolate->factory()->smallestUnit_string(), |
| DefaultValue::kUnset, method_name)); |
| |
| // 8. Let timeZone be ? Get(resolvedOptions, "timeZone"). |
| DirectHandle<Object> time_zone; |
| // Let val be ? Get(temporalDurationLike, fieldName). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, time_zone, |
| JSReceiver::GetProperty(isolate, options, |
| isolate->factory()->timeZone_string())); |
| |
| // 9. Perform ? ValidateTemporalUnitValue(smallestUnit, time). |
| RETURN_ON_EXCEPTION(isolate, temporal::ValidateTemporalUnitValue( |
| isolate, smallest_unit, UnitGroup::kTime)); |
| |
| // 10. If smallestUnit is hour, throw a RangeError exception. |
| if (smallest_unit == Unit::Hour) { |
| THROW_NEW_ERROR(isolate, |
| NewRangeError(MessageTemplate::kPropertyValueOutOfRange, |
| isolate->factory()->smallestUnit_string())); |
| } |
| |
| std::optional<temporal_rs::TimeZone> tz_for_passing; |
| // 11. If timeZone is not undefined, then |
| if (!IsUndefined(*time_zone)) { |
| temporal_rs::TimeZone rust_time_zone; |
| // a. Set timeZone to ? ToTemporalTimeZoneIdentifier(timeZone). |
| MOVE_RETURN_ON_EXCEPTION( |
| isolate, rust_time_zone, |
| temporal::ToTemporalTimeZoneIdentifier(isolate, time_zone)); |
| tz_for_passing = rust_time_zone; |
| } |
| |
| auto rust_options = temporal_rs::ToStringRoundingOptions{ |
| .precision = digits, |
| .smallest_unit = smallest_unit, |
| .rounding_mode = rounding_mode, |
| }; |
| |
| return temporal::GenericTemporalToString( |
| isolate, instant, &temporal_rs::Instant::to_ixdtf_string_with_provider, |
| tz_for_passing, std::move(rust_options), std::ref(TimeZoneProvider())); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tojson |
| MaybeDirectHandle<String> JSTemporalInstant::ToJSON( |
| Isolate* isolate, DirectHandle<JSTemporalInstant> instant) { |
| return temporal::GenericTemporalToString( |
| isolate, instant, &temporal_rs::Instant::to_ixdtf_string_with_provider, |
| std::nullopt, std::move(temporal::kToStringAuto), |
| std::ref(TimeZoneProvider())); |
| } |
| |
| MaybeDirectHandle<String> JSTemporalInstant::ToLocaleString( |
| Isolate* isolate, DirectHandle<JSTemporalInstant> instant, |
| DirectHandle<Object> locales, DirectHandle<Object> options) { |
| #ifdef V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sub-temporal.instant.prototype.tolocalestring |
| return JSDateTimeFormat::TemporalToLocaleString( |
| isolate, instant, locales, options, |
| JSDateTimeFormat::RequiredOption::kAny, |
| JSDateTimeFormat::DefaultsOption::kAll, |
| "Temporal.Instant.prototype.toLocaleString"); |
| #else // V8_INTL_SUPPORT |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tolocalestring |
| return temporal::GenericTemporalToString( |
| isolate, instant, &temporal_rs::Instant::to_ixdtf_string_with_provider, |
| std::nullopt, std::move(temporal::kToStringAuto), |
| std::ref(TimeZoneProvider())); |
| #endif // V8_INTL_SUPPORT |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.add |
| MaybeDirectHandle<JSTemporalInstant> JSTemporalInstant::Add( |
| Isolate* isolate, DirectHandle<JSTemporalInstant> handle, |
| DirectHandle<Object> temporal_duration_like) { |
| static const char method_name[] = "Temporal.Duration.prototype.add"; |
| |
| DirectHandle<JSTemporalDuration> other_duration; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, other_duration, |
| temporal::ToTemporalDuration( |
| isolate, temporal_duration_like, method_name)); |
| |
| auto added = handle->wrapped_rust()->add(*other_duration->wrapped_rust()); |
| |
| return ConstructRustWrappingType<JSTemporalInstant>(isolate, |
| std::move(added)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.subtract |
| MaybeDirectHandle<JSTemporalInstant> JSTemporalInstant::Subtract( |
| Isolate* isolate, DirectHandle<JSTemporalInstant> handle, |
| DirectHandle<Object> temporal_duration_like) { |
| static const char method_name[] = "Temporal.Duration.prototype.subtract"; |
| |
| DirectHandle<JSTemporalDuration> other_duration; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, other_duration, |
| temporal::ToTemporalDuration( |
| isolate, temporal_duration_like, method_name)); |
| |
| auto subtracted = |
| handle->wrapped_rust()->subtract(*other_duration->wrapped_rust()); |
| |
| return ConstructRustWrappingType<JSTemporalInstant>(isolate, |
| std::move(subtracted)); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.until |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalInstant::Until( |
| Isolate* isolate, DirectHandle<JSTemporalInstant> handle, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.Instant.prototype.until"; |
| |
| return temporal::GenericDifferenceTemporal( |
| isolate, &temporal_rs::Instant::until, UnitGroup::kTime, Unit::Nanosecond, |
| handle, other, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.since |
| MaybeDirectHandle<JSTemporalDuration> JSTemporalInstant::Since( |
| Isolate* isolate, DirectHandle<JSTemporalInstant> handle, |
| DirectHandle<Object> other, DirectHandle<Object> options) { |
| static const char method_name[] = "Temporal.Instant.prototype.since"; |
| |
| return temporal::GenericDifferenceTemporal( |
| isolate, &temporal_rs::Instant::since, UnitGroup::kTime, Unit::Nanosecond, |
| handle, other, options, method_name); |
| } |
| |
| // https://tc39.es/proposal-temporal/#sec-temporal.now.timezoneid |
| V8_WARN_UNUSED_RESULT MaybeDirectHandle<String> JSTemporalNowTimeZoneId( |
| Isolate* isolate) { |
| // 1. Return SystemTimeZoneIdentifier(). |
| #ifdef V8_INTL_SUPPORT |
| auto tz_str = Intl::DefaultTimeZone(); |
| |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendString(tz_str); |
| return builder.Finish().ToHandleChecked(); |
| #else |
| return isolate->factory()->UTC_string(); |
| #endif |
| } |
| |
| } // namespace v8::internal |