%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/objects/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/objects/js-temporal-objects.cc |
// 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 <set> #include "src/base/optional.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" #ifdef V8_INTL_SUPPORT #include "src/objects/intl-objects.h" #include "src/objects/js-date-time-format.h" #endif // V8_INTL_SUPPORT #include "src/objects/js-objects-inl.h" #include "src/objects/js-objects.h" #include "src/objects/js-temporal-objects-inl.h" #ifdef V8_INTL_SUPPORT #include "src/objects/managed-inl.h" #endif // V8_INTL_SUPPORT #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 "src/temporal/temporal-parser.h" #ifdef V8_INTL_SUPPORT #include "unicode/calendar.h" #include "unicode/unistr.h" #endif // V8_INTL_SUPPORT namespace v8 { namespace internal { namespace { enum class Unit { kNotPresent, kAuto, kYear, kMonth, kWeek, kDay, kHour, kMinute, kSecond, kMillisecond, kMicrosecond, kNanosecond }; /** * This header declare the Abstract Operations defined in the * Temporal spec with the enum and struct for them. */ // Struct // only for BalanceTime struct UnbalancedTimeRecord { double hour; double minute; double second; double millisecond; double microsecond; double nanosecond; }; using temporal::DateRecord; using temporal::DateTimeRecord; using temporal::TimeRecord; struct DateRecordWithCalendar { DateRecord date; Handle<Object> calendar; // String or Undefined }; struct TimeRecordWithCalendar { TimeRecord time; Handle<Object> calendar; // String or Undefined }; struct TimeZoneRecord { bool z; Handle<Object> offset_string; // String or Undefined Handle<Object> name; // String or Undefined }; struct DateTimeRecordWithCalendar { DateRecord date; TimeRecord time; TimeZoneRecord time_zone; Handle<Object> calendar; // String or Undefined }; struct InstantRecord { DateRecord date; TimeRecord time; Handle<Object> offset_string; // String or Undefined }; using temporal::DurationRecord; using temporal::IsValidDuration; using temporal::TimeDurationRecord; struct DurationRecordWithRemainder { DurationRecord record; double remainder; }; // #sec-temporal-date-duration-records struct DateDurationRecord { double years; double months; double weeks; double days; // #sec-temporal-createdatedurationrecord static Maybe<DateDurationRecord> Create(Isolate* isolate, double years, double months, double weeks, double days); }; // Options V8_WARN_UNUSED_RESULT Handle<String> UnitToString(Isolate* isolate, Unit unit); // #sec-temporal-totemporaldisambiguation enum class Disambiguation { kCompatible, kEarlier, kLater, kReject }; // #sec-temporal-totemporaloverflow enum class ShowOverflow { kConstrain, kReject }; // #sec-temporal-toshowcalendaroption enum class ShowCalendar { kAuto, kAlways, kNever }; // #sec-temporal-toshowtimezonenameoption enum class ShowTimeZone { kAuto, kNever }; Maybe<ShowTimeZone> ToShowTimeZoneNameOption(Isolate* isolate, Handle<JSReceiver> options, const char* method_name) { // 1. Return ? GetOption(normalizedOptions, "timeZoneName", "string", « // "auto", "never" », "auto"). return GetStringOption<ShowTimeZone>( isolate, options, "timeZoneName", method_name, {"auto", "never"}, {ShowTimeZone::kAuto, ShowTimeZone::kNever}, ShowTimeZone::kAuto); } // #sec-temporal-toshowoffsetoption enum class ShowOffset { kAuto, kNever }; Maybe<ShowOffset> ToShowOffsetOption(Isolate* isolate, Handle<JSReceiver> options, const char* method_name) { // 1. Return ? GetOption(normalizedOptions, "offset", "string", « "auto", // "never" », "auto"). return GetStringOption<ShowOffset>( isolate, options, "offset", method_name, {"auto", "never"}, {ShowOffset::kAuto, ShowOffset::kNever}, ShowOffset::kAuto); } enum class Precision { k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, kAuto, kMinute }; // Enum for add/subtract enum class Arithmetic { kAdd, kSubtract }; // Enum for since/until enum class TimePreposition { kSince, kUntil }; enum class Offset { kPrefer, kUse, kIgnore, kReject }; V8_WARN_UNUSED_RESULT Maybe<Offset> ToTemporalOffset(Isolate* isolate, Handle<Object> options, Offset fallback, const char* method_name); // sec-temporal-totemporalroundingmode enum class RoundingMode { kCeil, kFloor, kExpand, kTrunc, kHalfCeil, kHalfFloor, kHalfExpand, kHalfTrunc, kHalfEven }; // #table-temporal-unsigned-rounding-modes enum class UnsignedRoundingMode { kInfinity, kZero, kHalfInfinity, kHalfZero, kHalfEven }; enum class MatchBehaviour { kMatchExactly, kMatchMinutes }; // #sec-temporal-gettemporalunit enum class UnitGroup { kDate, kTime, kDateTime, }; struct DifferenceSettings { Unit smallest_unit; Unit largest_unit; RoundingMode rounding_mode; double rounding_increment; Handle<JSReceiver> options; }; enum class DisallowedUnitsInDifferenceSettings { kNone, kWeekAndDay, }; Maybe<DifferenceSettings> GetDifferenceSettings( Isolate* isolate, TimePreposition operation, Handle<Object> options, UnitGroup unit_group, DisallowedUnitsInDifferenceSettings disallowed_units, Unit fallback_smallest_unit, Unit smallest_largest_default_unit, const char* method_name); // #sec-temporal-totemporaloffset // ISO8601 String Parsing // #sec-temporal-parsetemporalcalendarstring V8_WARN_UNUSED_RESULT MaybeHandle<String> ParseTemporalCalendarString( Isolate* isolate, Handle<String> iso_string); // #sec-temporal-parsetemporaldatetimestring V8_WARN_UNUSED_RESULT Maybe<DateTimeRecordWithCalendar> ParseTemporalDateTimeString(Isolate* isolate, Handle<String> iso_string); // #sec-temporal-parsetemporaldatestring V8_WARN_UNUSED_RESULT Maybe<DateRecordWithCalendar> ParseTemporalDateString( Isolate* isolate, Handle<String> iso_string); // #sec-temporal-parsetemporaltimestring Maybe<TimeRecordWithCalendar> ParseTemporalTimeString( Isolate* isolate, Handle<String> iso_string); // #sec-temporal-parsetemporaldurationstring V8_WARN_UNUSED_RESULT Maybe<DurationRecord> ParseTemporalDurationString( Isolate* isolate, Handle<String> iso_string); // #sec-temporal-parsetemporaltimezonestring V8_WARN_UNUSED_RESULT Maybe<TimeZoneRecord> ParseTemporalTimeZoneString( Isolate* isolate, Handle<String> iso_string); // #sec-temporal-parsetimezoneoffsetstring V8_WARN_UNUSED_RESULT Maybe<int64_t> ParseTimeZoneOffsetString( Isolate* isolate, Handle<String> offset_string); // #sec-temporal-parsetemporalinstant V8_WARN_UNUSED_RESULT MaybeHandle<BigInt> ParseTemporalInstant( Isolate* isolate, Handle<String> iso_string); V8_WARN_UNUSED_RESULT MaybeHandle<BigInt> ParseTemporalInstant( Isolate* isolate, Handle<String> iso_string); DateRecord BalanceISODate(Isolate* isolate, const DateRecord& date); // Math and Misc V8_WARN_UNUSED_RESULT MaybeHandle<BigInt> AddInstant( Isolate* isolate, Handle<BigInt> epoch_nanoseconds, const TimeDurationRecord& addend); // #sec-temporal-balanceduration V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> BalanceDuration( Isolate* isolate, Unit largest_unit, Handle<Object> relative_to, const TimeDurationRecord& duration, const char* method_name); // The special case of BalanceDuration while the nanosecond is a large value // and the rest are 0. V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> BalanceDuration( Isolate* isolate, Unit largest_unit, Handle<BigInt> nanoseconds, const char* method_name); // A special version of BalanceDuration which add two TimeDurationRecord // internally as BigInt to avoid overflow double. V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> BalanceDuration( Isolate* isolate, Unit largest_unit, const TimeDurationRecord& dur1, const TimeDurationRecord& dur2, const char* method_name); // sec-temporal-balancepossiblyinfiniteduration enum BalanceOverflow { kNone, kPositive, kNegative, }; struct BalancePossiblyInfiniteDurationResult { TimeDurationRecord value; BalanceOverflow overflow; }; V8_WARN_UNUSED_RESULT Maybe<BalancePossiblyInfiniteDurationResult> BalancePossiblyInfiniteDuration(Isolate* isolate, Unit largest_unit, Handle<Object> relative_to, const TimeDurationRecord& duration, const char* method_name); // The special case of BalancePossiblyInfiniteDuration while the nanosecond is a // large value and days contains non-zero values but the rest are 0. // This version has no relative_to. V8_WARN_UNUSED_RESULT Maybe<BalancePossiblyInfiniteDurationResult> BalancePossiblyInfiniteDuration(Isolate* isolate, Unit largest_unit, Handle<Object> relative_to, double days, Handle<BigInt> nanoseconds, const char* method_name); V8_WARN_UNUSED_RESULT Maybe<BalancePossiblyInfiniteDurationResult> BalancePossiblyInfiniteDuration(Isolate* isolate, Unit largest_unit, double days, Handle<BigInt> nanoseconds, const char* method_name) { return BalancePossiblyInfiniteDuration(isolate, largest_unit, isolate->factory()->undefined_value(), days, nanoseconds, method_name); } V8_WARN_UNUSED_RESULT Maybe<DurationRecord> DifferenceISODateTime( Isolate* isolate, const DateTimeRecord& date_time1, const DateTimeRecord& date_time2, Handle<JSReceiver> calendar, Unit largest_unit, Handle<JSReceiver> relative_to, const char* method_name); // #sec-temporal-adddatetime V8_WARN_UNUSED_RESULT Maybe<DateTimeRecord> AddDateTime( Isolate* isolate, const DateTimeRecord& date_time, Handle<JSReceiver> calendar, const DurationRecord& addend, Handle<Object> options); // #sec-temporal-addzoneddatetime V8_WARN_UNUSED_RESULT MaybeHandle<BigInt> AddZonedDateTime( Isolate* isolate, Handle<BigInt> eopch_nanoseconds, Handle<JSReceiver> time_zone, Handle<JSReceiver> calendar, const DurationRecord& addend, const char* method_name); V8_WARN_UNUSED_RESULT MaybeHandle<BigInt> AddZonedDateTime( Isolate* isolate, Handle<BigInt> eopch_nanoseconds, Handle<JSReceiver> time_zone, Handle<JSReceiver> calendar, const DurationRecord& addend, Handle<Object> options, const char* method_name); // #sec-temporal-isvalidepochnanoseconds bool IsValidEpochNanoseconds(Isolate* isolate, Handle<BigInt> epoch_nanoseconds); struct NanosecondsToDaysResult { double days; double nanoseconds; int64_t day_length; }; // #sec-temporal-nanosecondstodays V8_WARN_UNUSED_RESULT Maybe<NanosecondsToDaysResult> NanosecondsToDays( Isolate* isolate, Handle<BigInt> nanoseconds, Handle<Object> relative_to_obj, const char* method_name); // #sec-temporal-interpretisodatetimeoffset enum class OffsetBehaviour { kOption, kExact, kWall }; // sec-temporal-totemporalroundingmode Maybe<RoundingMode> ToTemporalRoundingMode(Isolate* isolate, Handle<JSReceiver> options, RoundingMode fallback, const char* method_name) { // 1. Return ? GetOption(normalizedOptions, "roundingMode", "string", « // "ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", // "halfTrunc", "halfEven" », fallback). return GetStringOption<RoundingMode>( isolate, options, "roundingMode", method_name, {"ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", "halfEven"}, {RoundingMode::kCeil, RoundingMode::kFloor, RoundingMode::kExpand, RoundingMode::kTrunc, RoundingMode::kHalfCeil, RoundingMode::kHalfFloor, RoundingMode::kHalfExpand, RoundingMode::kHalfTrunc, RoundingMode::kHalfEven}, fallback); } V8_WARN_UNUSED_RESULT Handle<BigInt> GetEpochFromISOParts(Isolate* isolate, const DateTimeRecord& date_time); int32_t DurationSign(Isolate* isolaet, const DurationRecord& dur); // #sec-temporal-isodaysinmonth int32_t ISODaysInMonth(Isolate* isolate, int32_t year, int32_t month); // #sec-temporal-isodaysinyear int32_t ISODaysInYear(Isolate* isolate, int32_t year); bool IsValidTime(Isolate* isolate, const TimeRecord& time); // #sec-temporal-isvalidisodate bool IsValidISODate(Isolate* isolate, const DateRecord& date); // #sec-temporal-compareisodate int32_t CompareISODate(const DateRecord& date1, const DateRecord& date2); // #sec-temporal-balanceisoyearmonth void BalanceISOYearMonth(Isolate* isolate, int32_t* year, int32_t* month); // #sec-temporal-balancetime V8_WARN_UNUSED_RESULT DateTimeRecord BalanceTime(const UnbalancedTimeRecord& time); // #sec-temporal-differencetime V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> DifferenceTime( Isolate* isolate, const TimeRecord& time1, const TimeRecord& time2); // #sec-temporal-addtime V8_WARN_UNUSED_RESULT DateTimeRecord AddTime(Isolate* isolate, const TimeRecord& time, const TimeDurationRecord& addend); // #sec-temporal-totaldurationnanoseconds Handle<BigInt> TotalDurationNanoseconds(Isolate* isolate, const TimeDurationRecord& duration, double offset_shift); // #sec-temporal-totemporaltimerecord Maybe<TimeRecord> ToTemporalTimeRecord(Isolate* isolate, Handle<JSReceiver> temporal_time_like, const char* method_name); // Calendar Operations // #sec-temporal-calendardateadd V8_WARN_UNUSED_RESULT MaybeHandle<JSTemporalPlainDate> CalendarDateAdd( Isolate* isolate, Handle<JSReceiver> calendar, Handle<Object> date, Handle<Object> durations, Handle<Object> options, Handle<Object> date_add); V8_WARN_UNUSED_RESULT MaybeHandle<JSTemporalPlainDate> CalendarDateAdd( Isolate* isolate, Handle<JSReceiver> calendar, Handle<Object> date, Handle<Object> durations, Handle<Object> options); V8_WARN_UNUSED_RESULT MaybeHandle<JSTemporalPlainDate> CalendarDateAdd( Isolate* isolate, Handle<JSReceiver> calendar, Handle<Object> date, Handle<Object> durations); // #sec-temporal-calendardateuntil V8_WARN_UNUSED_RESULT MaybeHandle<JSTemporalDuration> CalendarDateUntil( Isolate* isolate, Handle<JSReceiver> calendar, Handle<Object> one, Handle<Object> two, Handle<Object> options, Handle<Object> date_until); // #sec-temporal-calendarfields MaybeHandle<FixedArray> CalendarFields(Isolate* isolate, Handle<JSReceiver> calendar, Handle<FixedArray> field_names); // #sec-temporal-getoffsetnanosecondsfor V8_WARN_UNUSED_RESULT Maybe<int64_t> GetOffsetNanosecondsFor( Isolate* isolate, Handle<JSReceiver> time_zone, Handle<Object> instant, const char* method_name); // #sec-temporal-totemporalcalendarwithisodefault MaybeHandle<JSReceiver> ToTemporalCalendarWithISODefault( Isolate* isolate, Handle<Object> temporal_calendar_like, const char* method_name); // #sec-temporal-isbuiltincalendar bool IsBuiltinCalendar(Isolate* isolate, Handle<String> id); // Internal Helper Function int32_t CalendarIndex(Isolate* isolate, Handle<String> id); // #sec-isvalidtimezonename bool IsValidTimeZoneName(Isolate* isolate, Handle<String> time_zone); // #sec-canonicalizetimezonename Handle<String> CanonicalizeTimeZoneName(Isolate* isolate, Handle<String> identifier); // #sec-temporal-tointegerthrowoninfinity MaybeHandle<Object> ToIntegerThrowOnInfinity(Isolate* isolate, Handle<Object> argument); // #sec-temporal-topositiveinteger MaybeHandle<Object> ToPositiveInteger(Isolate* isolate, Handle<Object> argument); inline double modulo(double a, int32_t b) { return a - std::floor(a / b) * b; } #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) #define AT __FILE__ ":" TOSTRING(__LINE__) #ifdef DEBUG #define TEMPORAL_DEBUG_INFO AT #define TEMPORAL_ENTER_FUNC() // #define TEMPORAL_ENTER_FUNC() do { PrintF("Start: %s\n", __func__); } while // (false) #else // #define TEMPORAL_DEBUG_INFO "" #define TEMPORAL_DEBUG_INFO AT #define TEMPORAL_ENTER_FUNC() // #define TEMPORAL_ENTER_FUNC() do { PrintF("Start: %s\n", __func__); } while // (false) #endif // DEBUG #define NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR() \ NewTypeError( \ MessageTemplate::kInvalidArgumentForTemporal, \ isolate->factory()->NewStringFromStaticChars(TEMPORAL_DEBUG_INFO)) #define NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR() \ NewRangeError( \ MessageTemplate::kInvalidTimeValueForTemporal, \ isolate->factory()->NewStringFromStaticChars(TEMPORAL_DEBUG_INFO)) // #sec-defaulttimezone #ifdef V8_INTL_SUPPORT Handle<String> DefaultTimeZone(Isolate* isolate) { TEMPORAL_ENTER_FUNC(); return Intl::DefaultTimeZone(isolate); } #else // V8_INTL_SUPPORT Handle<String> DefaultTimeZone(Isolate* isolate) { TEMPORAL_ENTER_FUNC(); return isolate->factory()->UTC_string(); } #endif // V8_INTL_SUPPORT // #sec-temporal-isodatetimewithinlimits bool ISODateTimeWithinLimits(Isolate* isolate, const DateTimeRecord& date_time) { TEMPORAL_ENTER_FUNC(); /** * Note: It is really overkill to decide within the limit by following the * specified algorithm literally, which require the conversion to BigInt. * Take a short cut and use pre-calculated year/month/day boundary instead. * * Math: * (-8.64 x 10^21- 8.64 x 10^13, 8.64 x 10^21 + 8.64 x 10^13) ns * = (-8.64 x 100000001 x 10^13, 8.64 x 100000001 x 10^13) ns * = (-8.64 x 100000001 x 10^10, 8.64 x 100000001 x 10^10) microsecond * = (-8.64 x 100000001 x 10^7, 8.64 x 100000001 x 10^7) millisecond * = (-8.64 x 100000001 x 10^4, 8.64 x 100000001 x 10^4) second * = (-86400 x 100000001 , 86400 x 100000001 ) second * = (-100000001, 100000001) days => Because 60*60*24 = 86400 * 100000001 days is about 273790 years, 11 months and 4 days. * Therefore 100000001 days before Jan 1 1970 is around Apr 19, -271821 and * 100000001 days after Jan 1 1970 is around Sept 13, 275760. */ if (date_time.date.year > -271821 && date_time.date.year < 275760) return true; if (date_time.date.year < -271821 || date_time.date.year > 275760) return false; if (date_time.date.year == -271821) { if (date_time.date.month > 4) return true; if (date_time.date.month < 4) return false; if (date_time.date.day > 19) return true; if (date_time.date.day < 19) return false; if (date_time.time.hour > 0) return true; if (date_time.time.minute > 0) return true; if (date_time.time.second > 0) return true; if (date_time.time.millisecond > 0) return true; if (date_time.time.microsecond > 0) return true; return date_time.time.nanosecond > 0; } else { DCHECK_EQ(date_time.date.year, 275760); if (date_time.date.month > 9) return false; if (date_time.date.month < 9) return true; return date_time.date.day < 14; } // 1. Assert: year, month, day, hour, minute, second, millisecond, // microsecond, and nanosecond are integers. // 2. Let ns be ! GetEpochFromISOParts(year, month, day, hour, minute, // second, millisecond, microsecond, nanosecond). // 3. If ns ≤ -8.64 × 10^21 - 8.64 × 10^13, then // 4. If ns ≥ 8.64 × 10^21 + 8.64 × 10^13, then // 5. Return true. } // #sec-temporal-isoyearmonthwithinlimits bool ISOYearMonthWithinLimits(int32_t year, int32_t month) { TEMPORAL_ENTER_FUNC(); // 1. Assert: year and month are integers. // 2. If year < −271821 or year > 275760, then // a. Return false. if (year < -271821 || year > 275760) return false; // 3. If year is −271821 and month < 4, then // a. Return false. if (year == -271821 && month < 4) return false; // 4. If year is 275760 and month > 9, then // a. Return false. if (year == 275760 && month > 9) return false; // 5. Return true. return true; } #define ORDINARY_CREATE_FROM_CONSTRUCTOR(obj, target, new_target, T) \ Handle<JSReceiver> new_target_receiver = \ Handle<JSReceiver>::cast(new_target); \ Handle<Map> map; \ ASSIGN_RETURN_ON_EXCEPTION( \ isolate, map, \ JSFunction::GetDerivedMap(isolate, target, new_target_receiver), T); \ Handle<T> object = \ Handle<T>::cast(isolate->factory()->NewFastOrSlowJSObjectFromMap(map)); #define THROW_INVALID_RANGE(T) \ THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), T); #define CONSTRUCTOR(name) \ Handle<JSFunction>( \ JSFunction::cast( \ isolate->context()->native_context()->temporal_##name##_function()), \ isolate) // #sec-temporal-systemutcepochnanoseconds Handle<BigInt> SystemUTCEpochNanoseconds(Isolate* isolate) { TEMPORAL_ENTER_FUNC(); // 1. Let ns be the approximate current UTC date and time, in nanoseconds // since the epoch. double ms = V8::GetCurrentPlatform()->CurrentClockTimeMillisecondsHighResolution(); // 2. Set ns to the result of clamping ns between −8.64 × 10^21 and 8.64 × // 10^21. // 3. Return ℤ(ns). double ns = ms * 1000000.0; ns = std::floor(std::max(-8.64e21, std::min(ns, 8.64e21))); return BigInt::FromNumber(isolate, isolate->factory()->NewNumber(ns)) .ToHandleChecked(); } // #sec-temporal-createtemporalcalendar MaybeHandle<JSTemporalCalendar> CreateTemporalCalendar( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<String> identifier) { TEMPORAL_ENTER_FUNC(); // 1. Assert: ! IsBuiltinCalendar(identifier) is true. // 2. If newTarget is not provided, set newTarget to %Temporal.Calendar%. // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, // "%Temporal.Calendar.prototype%", « [[InitializedTemporalCalendar]], // [[Identifier]] »). int32_t index = CalendarIndex(isolate, identifier); ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, JSTemporalCalendar) object->set_flags(0); // 4. Set object.[[Identifier]] to identifier. object->set_calendar_index(index); // 5. Return object. return object; } MaybeHandle<JSTemporalCalendar> CreateTemporalCalendar( Isolate* isolate, Handle<String> identifier) { TEMPORAL_ENTER_FUNC(); return CreateTemporalCalendar(isolate, CONSTRUCTOR(calendar), CONSTRUCTOR(calendar), identifier); } // #sec-temporal-createtemporaldate MaybeHandle<JSTemporalPlainDate> CreateTemporalDate( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, const DateRecord& date, Handle<JSReceiver> calendar) { TEMPORAL_ENTER_FUNC(); // 1. Assert: isoYear is an integer. // 2. Assert: isoMonth is an integer. // 3. Assert: isoDay is an integer. // 4. Assert: Type(calendar) is Object. // 5. If ! IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a // RangeError exception. if (!IsValidISODate(isolate, date)) { THROW_INVALID_RANGE(JSTemporalPlainDate); } // 6. If ! ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, // 0) is false, throw a RangeError exception. if (!ISODateTimeWithinLimits(isolate, {date, {12, 0, 0, 0, 0, 0}})) { THROW_INVALID_RANGE(JSTemporalPlainDate); } // 7. If newTarget is not present, set it to %Temporal.PlainDate%. // 8. Let object be ? OrdinaryCreateFromConstructor(newTarget, // "%Temporal.PlainDate.prototype%", « [[InitializedTemporalDate]], // [[ISOYear]], [[ISOMonth]], [[ISODay]], [[Calendar]] »). ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, JSTemporalPlainDate) object->set_year_month_day(0); // 9. Set object.[[ISOYear]] to isoYear. object->set_iso_year(date.year); // 10. Set object.[[ISOMonth]] to isoMonth. object->set_iso_month(date.month); // 11. Set object.[[ISODay]] to isoDay. object->set_iso_day(date.day); // 12. Set object.[[Calendar]] to calendar. object->set_calendar(*calendar); // 13. Return object. return object; } MaybeHandle<JSTemporalPlainDate> CreateTemporalDate( Isolate* isolate, const DateRecord& date, Handle<JSReceiver> calendar) { TEMPORAL_ENTER_FUNC(); return CreateTemporalDate(isolate, CONSTRUCTOR(plain_date), CONSTRUCTOR(plain_date), date, calendar); } // #sec-temporal-createtemporaldatetime MaybeHandle<JSTemporalPlainDateTime> CreateTemporalDateTime( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, const DateTimeRecord& date_time, Handle<JSReceiver> calendar) { TEMPORAL_ENTER_FUNC(); // 1. Assert: isoYear, isoMonth, isoDay, hour, minute, second, millisecond, // microsecond, and nanosecond are integers. // 2. Assert: Type(calendar) is Object. // 3. If ! IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a // RangeError exception. if (!IsValidISODate(isolate, date_time.date)) { THROW_INVALID_RANGE(JSTemporalPlainDateTime); } // 4. If ! IsValidTime(hour, minute, second, millisecond, microsecond, // nanosecond) is false, throw a RangeError exception. if (!IsValidTime(isolate, date_time.time)) { THROW_INVALID_RANGE(JSTemporalPlainDateTime); } // 5. If ! ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, hour, minute, // second, millisecond, microsecond, nanosecond) is false, then if (!ISODateTimeWithinLimits(isolate, date_time)) { // a. Throw a RangeError exception. THROW_INVALID_RANGE(JSTemporalPlainDateTime); } // 6. If newTarget is not present, set it to %Temporal.PlainDateTime%. // 7. Let object be ? OrdinaryCreateFromConstructor(newTarget, // "%Temporal.PlainDateTime.prototype%", « [[InitializedTemporalDateTime]], // [[ISOYear]], [[ISOMonth]], [[ISODay]], [[ISOHour]], [[ISOMinute]], // [[ISOSecond]], [[ISOMillisecond]], [[ISOMicrosecond]], [[ISONanosecond]], // [[Calendar]] »). ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, JSTemporalPlainDateTime) object->set_year_month_day(0); object->set_hour_minute_second(0); object->set_second_parts(0); // 8. Set object.[[ISOYear]] to isoYear. object->set_iso_year(date_time.date.year); // 9. Set object.[[ISOMonth]] to isoMonth. object->set_iso_month(date_time.date.month); // 10. Set object.[[ISODay]] to isoDay. object->set_iso_day(date_time.date.day); // 11. Set object.[[ISOHour]] to hour. object->set_iso_hour(date_time.time.hour); // 12. Set object.[[ISOMinute]] to minute. object->set_iso_minute(date_time.time.minute); // 13. Set object.[[ISOSecond]] to second. object->set_iso_second(date_time.time.second); // 14. Set object.[[ISOMillisecond]] to millisecond. object->set_iso_millisecond(date_time.time.millisecond); // 15. Set object.[[ISOMicrosecond]] to microsecond. object->set_iso_microsecond(date_time.time.microsecond); // 16. Set object.[[ISONanosecond]] to nanosecond. object->set_iso_nanosecond(date_time.time.nanosecond); // 17. Set object.[[Calendar]] to calendar. object->set_calendar(*calendar); // 18. Return object. return object; } MaybeHandle<JSTemporalPlainDateTime> CreateTemporalDateTimeDefaultTarget( Isolate* isolate, const DateTimeRecord& date_time, Handle<JSReceiver> calendar) { TEMPORAL_ENTER_FUNC(); return CreateTemporalDateTime(isolate, CONSTRUCTOR(plain_date_time), CONSTRUCTOR(plain_date_time), date_time, calendar); } } // namespace namespace temporal { MaybeHandle<JSTemporalPlainDateTime> CreateTemporalDateTime( Isolate* isolate, const DateTimeRecord& date_time, Handle<JSReceiver> calendar) { return CreateTemporalDateTimeDefaultTarget(isolate, date_time, calendar); } } // namespace temporal namespace { // #sec-temporal-createtemporaltime MaybeHandle<JSTemporalPlainTime> CreateTemporalTime( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, const TimeRecord& time) { TEMPORAL_ENTER_FUNC(); // 2. If ! IsValidTime(hour, minute, second, millisecond, microsecond, // nanosecond) is false, throw a RangeError exception. if (!IsValidTime(isolate, time)) { THROW_INVALID_RANGE(JSTemporalPlainTime); } Handle<JSTemporalCalendar> calendar = temporal::GetISO8601Calendar(isolate); // 4. Let object be ? OrdinaryCreateFromConstructor(newTarget, // "%Temporal.PlainTime.prototype%", « [[InitializedTemporalTime]], // [[ISOHour]], [[ISOMinute]], [[ISOSecond]], [[ISOMillisecond]], // [[ISOMicrosecond]], [[ISONanosecond]], [[Calendar]] »). ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, JSTemporalPlainTime) object->set_hour_minute_second(0); object->set_second_parts(0); // 5. Set object.[[ISOHour]] to hour. object->set_iso_hour(time.hour); // 6. Set object.[[ISOMinute]] to minute. object->set_iso_minute(time.minute); // 7. Set object.[[ISOSecond]] to second. object->set_iso_second(time.second); // 8. Set object.[[ISOMillisecond]] to millisecond. object->set_iso_millisecond(time.millisecond); // 9. Set object.[[ISOMicrosecond]] to microsecond. object->set_iso_microsecond(time.microsecond); // 10. Set object.[[ISONanosecond]] to nanosecond. object->set_iso_nanosecond(time.nanosecond); // 11. Set object.[[Calendar]] to ? GetISO8601Calendar(). object->set_calendar(*calendar); // 12. Return object. return object; } MaybeHandle<JSTemporalPlainTime> CreateTemporalTime(Isolate* isolate, const TimeRecord& time) { TEMPORAL_ENTER_FUNC(); return CreateTemporalTime(isolate, CONSTRUCTOR(plain_time), CONSTRUCTOR(plain_time), time); } // #sec-temporal-createtemporalmonthday MaybeHandle<JSTemporalPlainMonthDay> CreateTemporalMonthDay( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, int32_t iso_month, int32_t iso_day, Handle<JSReceiver> calendar, int32_t reference_iso_year) { TEMPORAL_ENTER_FUNC(); // 1. Assert: isoMonth, isoDay, and referenceISOYear are integers. // 2. Assert: Type(calendar) is Object. // 3. If ! IsValidISODate(referenceISOYear, isoMonth, isoDay) is false, throw if (!IsValidISODate(isolate, {reference_iso_year, iso_month, iso_day})) { // a RangeError exception. THROW_INVALID_RANGE(JSTemporalPlainMonthDay); } // 4. If ISODateTimeWithinLimits(referenceISOYear, isoMonth, isoDay, 12, 0, 0, // 0, 0, 0) is false, throw a RangeError exception. if (!ISODateTimeWithinLimits( isolate, {{reference_iso_year, iso_month, iso_day}, {12, 0, 0, 0, 0, 0}})) { THROW_INVALID_RANGE(JSTemporalPlainMonthDay); } // 5. If newTarget is not present, set it to %Temporal.PlainMonthDay%. // 6. Let object be ? OrdinaryCreateFromConstructor(newTarget, // "%Temporal.PlainMonthDay.prototype%", « [[InitializedTemporalMonthDay]], // [[ISOMonth]], [[ISODay]], [[ISOYear]], [[Calendar]] »). ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, JSTemporalPlainMonthDay) object->set_year_month_day(0); // 7. Set object.[[ISOMonth]] to isoMonth. object->set_iso_month(iso_month); // 8. Set object.[[ISODay]] to isoDay. object->set_iso_day(iso_day); // 9. Set object.[[Calendar]] to calendar. object->set_calendar(*calendar); // 10. Set object.[[ISOYear]] to referenceISOYear. object->set_iso_year(reference_iso_year); // 11. Return object. return object; } MaybeHandle<JSTemporalPlainMonthDay> CreateTemporalMonthDay( Isolate* isolate, int32_t iso_month, int32_t iso_day, Handle<JSReceiver> calendar, int32_t reference_iso_year) { return CreateTemporalMonthDay(isolate, CONSTRUCTOR(plain_month_day), CONSTRUCTOR(plain_month_day), iso_month, iso_day, calendar, reference_iso_year); } // #sec-temporal-createtemporalyearmonth MaybeHandle<JSTemporalPlainYearMonth> CreateTemporalYearMonth( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, int32_t iso_year, int32_t iso_month, Handle<JSReceiver> calendar, int32_t reference_iso_day) { TEMPORAL_ENTER_FUNC(); // 1. Assert: isoYear, isoMonth, and referenceISODay are integers. // 2. Assert: Type(calendar) is Object. // 3. If ! IsValidISODate(isoYear, isoMonth, referenceISODay) is false, throw // a RangeError exception. if (!IsValidISODate(isolate, {iso_year, iso_month, reference_iso_day})) { THROW_INVALID_RANGE(JSTemporalPlainYearMonth); } // 4. If ! ISOYearMonthWithinLimits(isoYear, isoMonth) is false, throw a // RangeError exception. if (!ISOYearMonthWithinLimits(iso_year, iso_month)) { THROW_INVALID_RANGE(JSTemporalPlainYearMonth); } // 5. If newTarget is not present, set it to %Temporal.PlainYearMonth%. // 6. Let object be ? OrdinaryCreateFromConstructor(newTarget, // "%Temporal.PlainYearMonth.prototype%", « [[InitializedTemporalYearMonth]], // [[ISOYear]], [[ISOMonth]], [[ISODay]], [[Calendar]] »). ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, JSTemporalPlainYearMonth) object->set_year_month_day(0); // 7. Set object.[[ISOYear]] to isoYear. object->set_iso_year(iso_year); // 8. Set object.[[ISOMonth]] to isoMonth. object->set_iso_month(iso_month); // 9. Set object.[[Calendar]] to calendar. object->set_calendar(*calendar); // 10. Set object.[[ISODay]] to referenceISODay. object->set_iso_day(reference_iso_day); // 11. Return object. return object; } MaybeHandle<JSTemporalPlainYearMonth> CreateTemporalYearMonth( Isolate* isolate, int32_t iso_year, int32_t iso_month, Handle<JSReceiver> calendar, int32_t reference_iso_day) { TEMPORAL_ENTER_FUNC(); return CreateTemporalYearMonth(isolate, CONSTRUCTOR(plain_year_month), CONSTRUCTOR(plain_year_month), iso_year, iso_month, calendar, reference_iso_day); } // #sec-temporal-createtemporalzoneddatetime MaybeHandle<JSTemporalZonedDateTime> CreateTemporalZonedDateTime( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<BigInt> epoch_nanoseconds, Handle<JSReceiver> time_zone, Handle<JSReceiver> calendar) { TEMPORAL_ENTER_FUNC(); // 1. Assert: Type(epochNanoseconds) is BigInt. // 2. Assert: ! IsValidEpochNanoseconds(epochNanoseconds) is true. DCHECK(IsValidEpochNanoseconds(isolate, epoch_nanoseconds)); // 3. Assert: Type(timeZone) is Object. // 4. Assert: Type(calendar) is Object. // 5. If newTarget is not present, set it to %Temporal.ZonedDateTime%. // 6. Let object be ? OrdinaryCreateFromConstructor(newTarget, // "%Temporal.ZonedDateTime.prototype%", « // [[InitializedTemporalZonedDateTime]], [[Nanoseconds]], [[TimeZone]], // [[Calendar]] »). ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, JSTemporalZonedDateTime) // 7. Set object.[[Nanoseconds]] to epochNanoseconds. object->set_nanoseconds(*epoch_nanoseconds); // 8. Set object.[[TimeZone]] to timeZone. object->set_time_zone(*time_zone); // 9. Set object.[[Calendar]] to calendar. object->set_calendar(*calendar); // 10. Return object. return object; } MaybeHandle<JSTemporalZonedDateTime> CreateTemporalZonedDateTime( Isolate* isolate, Handle<BigInt> epoch_nanoseconds, Handle<JSReceiver> time_zone, Handle<JSReceiver> calendar) { TEMPORAL_ENTER_FUNC(); return CreateTemporalZonedDateTime(isolate, CONSTRUCTOR(zoned_date_time), CONSTRUCTOR(zoned_date_time), epoch_nanoseconds, time_zone, calendar); } inline double NormalizeMinusZero(double v) { return IsMinusZero(v) ? 0 : v; } // #sec-temporal-createdatedurationrecord Maybe<DateDurationRecord> DateDurationRecord::Create( Isolate* isolate, double years, double months, double weeks, double days) { // 1. If ! IsValidDuration(years, months, weeks, days, 0, 0, 0, 0, 0, 0) is // false, throw a RangeError exception. if (!IsValidDuration(isolate, {years, months, weeks, {days, 0, 0, 0, 0, 0, 0}})) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateDurationRecord>()); } // 2. Return the Record { [[Years]]: ℝ(𝔽(years)), [[Months]]: ℝ(𝔽(months)), // [[Weeks]]: ℝ(𝔽(weeks)), [[Days]]: ℝ(𝔽(days)) }. DateDurationRecord record = {years, months, weeks, days}; return Just(record); } } // namespace namespace temporal { // #sec-temporal-createtimedurationrecord Maybe<TimeDurationRecord> TimeDurationRecord::Create( Isolate* isolate, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds) { // 1. If ! IsValidDuration(0, 0, 0, days, hours, minutes, seconds, // milliseconds, microseconds, nanoseconds) is false, throw a RangeError // exception. TimeDurationRecord record = {days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds}; if (!IsValidDuration(isolate, {0, 0, 0, record})) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<TimeDurationRecord>()); } // 2. Return the Record { [[Days]]: ℝ(𝔽(days)), [[Hours]]: ℝ(𝔽(hours)), // [[Minutes]]: ℝ(𝔽(minutes)), [[Seconds]]: ℝ(𝔽(seconds)), [[Milliseconds]]: // ℝ(𝔽(milliseconds)), [[Microseconds]]: ℝ(𝔽(microseconds)), [[Nanoseconds]]: // ℝ(𝔽(nanoseconds)) }. return Just(record); } // #sec-temporal-createdurationrecord Maybe<DurationRecord> DurationRecord::Create( Isolate* isolate, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds) { // 1. If ! IsValidDuration(years, months, weeks, days, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds) is false, throw a // RangeError exception. DurationRecord record = { years, months, weeks, {days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds}}; if (!IsValidDuration(isolate, record)) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DurationRecord>()); } // 2. Return the Record { [[Years]]: ℝ(𝔽(years)), [[Months]]: ℝ(𝔽(months)), // [[Weeks]]: ℝ(𝔽(weeks)), [[Days]]: ℝ(𝔽(days)), [[Hours]]: ℝ(𝔽(hours)), // [[Minutes]]: ℝ(𝔽(minutes)), [[Seconds]]: ℝ(𝔽(seconds)), [[Milliseconds]]: // ℝ(𝔽(milliseconds)), [[Microseconds]]: ℝ(𝔽(microseconds)), [[Nanoseconds]]: // ℝ(𝔽(nanoseconds)) }. return Just(record); } } // namespace temporal namespace { // #sec-temporal-createtemporalduration MaybeHandle<JSTemporalDuration> CreateTemporalDuration( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, const DurationRecord& duration) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 1. If ! IsValidDuration(years, months, weeks, days, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds) is false, throw a // RangeError exception. if (!IsValidDuration(isolate, duration)) { THROW_INVALID_RANGE(JSTemporalDuration); } // 2. If newTarget is not present, set it to %Temporal.Duration%. // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, // "%Temporal.Duration.prototype%", « [[InitializedTemporalDuration]], // [[Years]], [[Months]], [[Weeks]], [[Days]], [[Hours]], [[Minutes]], // [[Seconds]], [[Milliseconds]], [[Microseconds]], [[Nanoseconds]] »). const TimeDurationRecord& time_duration = duration.time_duration; Handle<Object> years = factory->NewNumber(NormalizeMinusZero(duration.years)); Handle<Object> months = factory->NewNumber(NormalizeMinusZero(duration.months)); Handle<Object> weeks = factory->NewNumber(NormalizeMinusZero(duration.weeks)); Handle<Object> days = factory->NewNumber(NormalizeMinusZero(time_duration.days)); Handle<Object> hours = factory->NewNumber(NormalizeMinusZero(time_duration.hours)); Handle<Object> minutes = factory->NewNumber(NormalizeMinusZero(time_duration.minutes)); Handle<Object> seconds = factory->NewNumber(NormalizeMinusZero(time_duration.seconds)); Handle<Object> milliseconds = factory->NewNumber(NormalizeMinusZero(time_duration.milliseconds)); Handle<Object> microseconds = factory->NewNumber(NormalizeMinusZero(time_duration.microseconds)); Handle<Object> nanoseconds = factory->NewNumber(NormalizeMinusZero(time_duration.nanoseconds)); ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, JSTemporalDuration) // 4. Set object.[[Years]] to ℝ(𝔽(years)). object->set_years(*years); // 5. Set object.[[Months]] to ℝ(𝔽(months)). object->set_months(*months); // 6. Set object.[[Weeks]] to ℝ(𝔽(weeks)). object->set_weeks(*weeks); // 7. Set object.[[Days]] to ℝ(𝔽(days)). object->set_days(*days); // 8. Set object.[[Hours]] to ℝ(𝔽(hours)). object->set_hours(*hours); // 9. Set object.[[Minutes]] to ℝ(𝔽(minutes)). object->set_minutes(*minutes); // 10. Set object.[[Seconds]] to ℝ(𝔽(seconds)). object->set_seconds(*seconds); // 11. Set object.[[Milliseconds]] to ℝ(𝔽(milliseconds)). object->set_milliseconds(*milliseconds); // 12. Set object.[[Microseconds]] to ℝ(𝔽(microseconds)). object->set_microseconds(*microseconds); // 13. Set object.[[Nanoseconds]] to ℝ(𝔽(nanoseconds)). object->set_nanoseconds(*nanoseconds); // 14. Return object. return object; } MaybeHandle<JSTemporalDuration> CreateTemporalDuration( Isolate* isolate, const DurationRecord& duration) { TEMPORAL_ENTER_FUNC(); return CreateTemporalDuration(isolate, CONSTRUCTOR(duration), CONSTRUCTOR(duration), duration); } } // namespace namespace temporal { // #sec-temporal-createtemporalinstant MaybeHandle<JSTemporalInstant> CreateTemporalInstant( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<BigInt> epoch_nanoseconds) { TEMPORAL_ENTER_FUNC(); // 1. Assert: Type(epochNanoseconds) is BigInt. // 2. Assert: ! IsValidEpochNanoseconds(epochNanoseconds) is true. DCHECK(IsValidEpochNanoseconds(isolate, epoch_nanoseconds)); // 4. Let object be ? OrdinaryCreateFromConstructor(newTarget, // "%Temporal.Instant.prototype%", « [[InitializedTemporalInstant]], // [[Nanoseconds]] »). ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, JSTemporalInstant) // 5. Set object.[[Nanoseconds]] to ns. object->set_nanoseconds(*epoch_nanoseconds); return object; } MaybeHandle<JSTemporalInstant> CreateTemporalInstant( Isolate* isolate, Handle<BigInt> epoch_nanoseconds) { TEMPORAL_ENTER_FUNC(); return CreateTemporalInstant(isolate, CONSTRUCTOR(instant), CONSTRUCTOR(instant), epoch_nanoseconds); } } // namespace temporal namespace { MaybeHandle<JSTemporalTimeZone> CreateTemporalTimeZoneFromIndex( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, int32_t index) { TEMPORAL_ENTER_FUNC(); ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, JSTemporalTimeZone) object->set_flags(0); object->set_details(0); object->set_is_offset(false); object->set_offset_milliseconds_or_time_zone_index(index); return object; } Handle<JSTemporalTimeZone> CreateTemporalTimeZoneUTC( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target) { TEMPORAL_ENTER_FUNC(); return CreateTemporalTimeZoneFromIndex(isolate, target, new_target, 0) .ToHandleChecked(); } Handle<JSTemporalTimeZone> CreateTemporalTimeZoneUTC(Isolate* isolate) { TEMPORAL_ENTER_FUNC(); return CreateTemporalTimeZoneUTC(isolate, CONSTRUCTOR(time_zone), CONSTRUCTOR(time_zone)); } bool IsUTC(Isolate* isolate, Handle<String> time_zone); // #sec-temporal-createtemporaltimezone MaybeHandle<JSTemporalTimeZone> CreateTemporalTimeZone( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<String> identifier) { TEMPORAL_ENTER_FUNC(); // 1. If newTarget is not present, set it to %Temporal.TimeZone%. // 2. Let object be ? OrdinaryCreateFromConstructor(newTarget, // "%Temporal.TimeZone.prototype%", « [[InitializedTemporalTimeZone]], // [[Identifier]], [[OffsetNanoseconds]] »). // 3. Let offsetNanosecondsResult be ParseTimeZoneOffsetString(identifier). Maybe<int64_t> maybe_offset_nanoseconds = ParseTimeZoneOffsetString(isolate, identifier); // 4. If offsetNanosecondsResult is an abrupt completion, then if (maybe_offset_nanoseconds.IsNothing()) { DCHECK(isolate->has_pending_exception()); isolate->clear_pending_exception(); // a. Assert: ! CanonicalizeTimeZoneName(identifier) is identifier. DCHECK(String::Equals(isolate, identifier, CanonicalizeTimeZoneName(isolate, identifier))); // b. Set object.[[Identifier]] to identifier. // c. Set object.[[OffsetNanoseconds]] to undefined. if (IsUTC(isolate, identifier)) { return CreateTemporalTimeZoneUTC(isolate, target, new_target); } #ifdef V8_INTL_SUPPORT int32_t time_zone_index = Intl::GetTimeZoneIndex(isolate, identifier); DCHECK_GE(time_zone_index, 0); return CreateTemporalTimeZoneFromIndex(isolate, target, new_target, time_zone_index); #else UNREACHABLE(); #endif // V8_INTL_SUPPORT // 5. Else, } else { // a. Set object.[[Identifier]] to ! // FormatTimeZoneOffsetString(offsetNanosecondsResult.[[Value]]). b. Set // object.[[OffsetNanoseconds]] to offsetNanosecondsResult.[[Value]]. ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, JSTemporalTimeZone) object->set_flags(0); object->set_details(0); object->set_is_offset(true); object->set_offset_nanoseconds(maybe_offset_nanoseconds.FromJust()); return object; } // 6. Return object. } MaybeHandle<JSTemporalTimeZone> CreateTemporalTimeZoneDefaultTarget( Isolate* isolate, Handle<String> identifier) { TEMPORAL_ENTER_FUNC(); return CreateTemporalTimeZone(isolate, CONSTRUCTOR(time_zone), CONSTRUCTOR(time_zone), identifier); } } // namespace namespace temporal { MaybeHandle<JSTemporalTimeZone> CreateTemporalTimeZone( Isolate* isolate, Handle<String> identifier) { return CreateTemporalTimeZoneDefaultTarget(isolate, identifier); } } // namespace temporal namespace { // #sec-temporal-systeminstant Handle<JSTemporalInstant> SystemInstant(Isolate* isolate) { TEMPORAL_ENTER_FUNC(); // 1. Let ns be ! SystemUTCEpochNanoseconds(). Handle<BigInt> ns = SystemUTCEpochNanoseconds(isolate); // 2. Return ? CreateTemporalInstant(ns). return temporal::CreateTemporalInstant(isolate, ns).ToHandleChecked(); } // #sec-temporal-systemtimezone Handle<JSTemporalTimeZone> SystemTimeZone(Isolate* isolate) { TEMPORAL_ENTER_FUNC(); Handle<String> default_time_zone = DefaultTimeZone(isolate); return temporal::CreateTemporalTimeZone(isolate, default_time_zone) .ToHandleChecked(); } DateTimeRecord GetISOPartsFromEpoch(Isolate* isolate, Handle<BigInt> epoch_nanoseconds) { TEMPORAL_ENTER_FUNC(); DateTimeRecord result; // 1. Assert: ! IsValidEpochNanoseconds(ℤ(epochNanoseconds)) is true. DCHECK(IsValidEpochNanoseconds(isolate, epoch_nanoseconds)); // 2. Let remainderNs be epochNanoseconds modulo 10^6. Handle<BigInt> million = BigInt::FromUint64(isolate, 1000000); Handle<BigInt> remainder_ns = BigInt::Remainder(isolate, epoch_nanoseconds, million).ToHandleChecked(); // Need to do some remainder magic to negative remainder. if (remainder_ns->IsNegative()) { remainder_ns = BigInt::Add(isolate, remainder_ns, million).ToHandleChecked(); } // 3. Let epochMilliseconds be (epochNanoseconds − remainderNs) / 10^6. int64_t epoch_milliseconds = BigInt::Divide(isolate, BigInt::Subtract(isolate, epoch_nanoseconds, remainder_ns) .ToHandleChecked(), million) .ToHandleChecked() ->AsInt64(); int year = 0; int month = 0; int day = 0; int wday = 0; int hour = 0; int min = 0; int sec = 0; int ms = 0; isolate->date_cache()->BreakDownTime(epoch_milliseconds, &year, &month, &day, &wday, &hour, &min, &sec, &ms); // 4. Let year be ! YearFromTime(epochMilliseconds). result.date.year = year; // 5. Let month be ! MonthFromTime(epochMilliseconds) + 1. result.date.month = month + 1; DCHECK_GE(result.date.month, 1); DCHECK_LE(result.date.month, 12); // 6. Let day be ! DateFromTime(epochMilliseconds). result.date.day = day; DCHECK_GE(result.date.day, 1); DCHECK_LE(result.date.day, 31); // 7. Let hour be ! HourFromTime(epochMilliseconds). result.time.hour = hour; DCHECK_GE(result.time.hour, 0); DCHECK_LE(result.time.hour, 23); // 8. Let minute be ! MinFromTime(epochMilliseconds). result.time.minute = min; DCHECK_GE(result.time.minute, 0); DCHECK_LE(result.time.minute, 59); // 9. Let second be ! SecFromTime(epochMilliseconds). result.time.second = sec; DCHECK_GE(result.time.second, 0); DCHECK_LE(result.time.second, 59); // 10. Let millisecond be ! msFromTime(epochMilliseconds). result.time.millisecond = ms; DCHECK_GE(result.time.millisecond, 0); DCHECK_LE(result.time.millisecond, 999); // 11. Let microsecond be floor(remainderNs / 1000) modulo 1000. int64_t remainder = remainder_ns->AsInt64(); result.time.microsecond = (remainder / 1000) % 1000; DCHECK_GE(result.time.microsecond, 0); // 12. 12. Assert: microsecond < 1000. DCHECK_LE(result.time.microsecond, 999); // 13. Let nanosecond be remainderNs modulo 1000. result.time.nanosecond = remainder % 1000; DCHECK_GE(result.time.nanosecond, 0); DCHECK_LE(result.time.nanosecond, 999); // 14. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day, // [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: // millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond }. return result; } // #sec-temporal-balanceisodatetime DateTimeRecord BalanceISODateTime(Isolate* isolate, const DateTimeRecord& date_time) { TEMPORAL_ENTER_FUNC(); // 1. Assert: year, month, day, hour, minute, second, millisecond, // microsecond, and nanosecond are integers. // 2. Let balancedTime be ! BalanceTime(hour, minute, second, millisecond, // microsecond, nanosecond). DateTimeRecord balanced_time = BalanceTime({static_cast<double>(date_time.time.hour), static_cast<double>(date_time.time.minute), static_cast<double>(date_time.time.second), static_cast<double>(date_time.time.millisecond), static_cast<double>(date_time.time.microsecond), static_cast<double>(date_time.time.nanosecond)}); // 3. Let balancedDate be ! BalanceISODate(year, month, day + // balancedTime.[[Days]]). DateRecord added_date = date_time.date; added_date.day += balanced_time.date.day; DateRecord balanced_date = BalanceISODate(isolate, added_date); // 4. Return the Record { [[Year]]: balancedDate.[[Year]], [[Month]]: // balancedDate.[[Month]], [[Day]]: balancedDate.[[Day]], [[Hour]]: // balancedTime.[[Hour]], [[Minute]]: balancedTime.[[Minute]], [[Second]]: // balancedTime.[[Second]], [[Millisecond]]: balancedTime.[[Millisecond]], // [[Microsecond]]: balancedTime.[[Microsecond]], [[Nanosecond]]: // balancedTime.[[Nanosecond]] }. return {balanced_date, balanced_time.time}; } // #sec-temporal-roundtowardszero double RoundTowardsZero(double x) { // 1. Return the mathematical value that is the same sign as x and whose // magnitude is floor(abs(x)). if (x < 0) { return -std::floor(std::abs(x)); } else { return std::floor(std::abs(x)); } } // #sec-temporal-temporaldurationtostring Handle<String> TemporalDurationToString(Isolate* isolate, const DurationRecord& duration, Precision precision) { IncrementalStringBuilder builder(isolate); DCHECK(precision != Precision::kMinute); // 1. Let sign be ! DurationSign(years, months, weeks, days, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds). DurationRecord dur = duration; int32_t sign = DurationSign(isolate, dur); // Note: for the operation below, to avoid microseconds .. seconds lost // precision while the resulting value may exceed the precision limit, we use // extra double xx_add to hold the additional temp value. // 2. Set microseconds to microseconds + RoundTowardsZero(nanoseconds / 1000). double microseconds_add = RoundTowardsZero(dur.time_duration.nanoseconds / 1000); // 3. Set nanoseconds to remainder(nanoseconds, 1000). dur.time_duration.nanoseconds = std::fmod(dur.time_duration.nanoseconds, 1000); // 4. Set milliseconds to milliseconds + RoundTowardsZero(microseconds / // 1000). double milliseconds_add = RoundTowardsZero( dur.time_duration.microseconds / 1000 + microseconds_add / 1000); // 5. Set microseconds to remainder(microseconds, 1000). dur.time_duration.microseconds = std::fmod(std::fmod(dur.time_duration.microseconds, 1000) + std::fmod(microseconds_add, 1000), 1000); // 6. Set seconds to seconds + RoundTowardsZero(milliseconds / 1000). double seconds_add = RoundTowardsZero(dur.time_duration.milliseconds / 1000 + milliseconds_add / 1000); // 7. Set milliseconds to remainder(milliseconds, 1000). dur.time_duration.milliseconds = std::fmod(std::fmod(dur.time_duration.milliseconds, 1000) + std::fmod(milliseconds_add, 1000), 1000); // 8. Let datePart be "". IncrementalStringBuilder date_part(isolate); // Number.MAX_VALUE.toString() is "1.7976931348623157e+308" // We add several more spaces to 320. base::ScopedVector<char> buf(320); // 9. If years is not 0, then if (dur.years != 0) { // a. Set datePart to the string concatenation of abs(years) formatted as a // decimal number and the code unit 0x0059 (LATIN CAPITAL LETTER Y). SNPrintF(buf, "%.0f", std::abs(dur.years)); date_part.AppendCString(buf.data()); date_part.AppendCharacter('Y'); } // 10. If months is not 0, then if (dur.months != 0) { // a. Set datePart to the string concatenation of datePart, // abs(months) formatted as a decimal number, and the code unit // 0x004D (LATIN CAPITAL LETTER M). SNPrintF(buf, "%.0f", std::abs(dur.months)); date_part.AppendCString(buf.data()); date_part.AppendCharacter('M'); } // 11. If weeks is not 0, then if (dur.weeks != 0) { // a. Set datePart to the string concatenation of datePart, // abs(weeks) formatted as a decimal number, and the code unit // 0x0057 (LATIN CAPITAL LETTER W). SNPrintF(buf, "%.0f", std::abs(dur.weeks)); date_part.AppendCString(buf.data()); date_part.AppendCharacter('W'); } // 12. If days is not 0, then if (dur.time_duration.days != 0) { // a. Set datePart to the string concatenation of datePart, // abs(days) formatted as a decimal number, and the code unit 0x0044 // (LATIN CAPITAL LETTER D). SNPrintF(buf, "%.0f", std::abs(dur.time_duration.days)); date_part.AppendCString(buf.data()); date_part.AppendCharacter('D'); } // 13. Let timePart be "". IncrementalStringBuilder time_part(isolate); // 14. If hours is not 0, then if (dur.time_duration.hours != 0) { // a. Set timePart to the string concatenation of abs(hours) formatted as a // decimal number and the code unit 0x0048 (LATIN CAPITAL LETTER H). SNPrintF(buf, "%.0f", std::abs(dur.time_duration.hours)); time_part.AppendCString(buf.data()); time_part.AppendCharacter('H'); } // 15. If minutes is not 0, then if (dur.time_duration.minutes != 0) { // a. Set timePart to the string concatenation of timePart, // abs(minutes) formatted as a decimal number, and the code unit // 0x004D (LATIN CAPITAL LETTER M). SNPrintF(buf, "%.0f", std::abs(dur.time_duration.minutes)); time_part.AppendCString(buf.data()); time_part.AppendCharacter('M'); } IncrementalStringBuilder seconds_part(isolate); IncrementalStringBuilder decimal_part(isolate); // 16. If any of seconds, milliseconds, microseconds, and nanoseconds are not // 0; or years, months, weeks, days, hours, and minutes are all 0, or // precision is not "auto" then if ((dur.time_duration.seconds != 0 || seconds_add != 0 || dur.time_duration.milliseconds != 0 || dur.time_duration.microseconds != 0 || dur.time_duration.nanoseconds != 0) || (dur.years == 0 && dur.months == 0 && dur.weeks == 0 && dur.time_duration.days == 0 && dur.time_duration.hours == 0 && dur.time_duration.minutes == 0) || precision != Precision::kAuto) { // a. Let fraction be abs(milliseconds) × 10^6 + abs(microseconds) × 10^3 + // abs(nanoseconds). int64_t fraction = std::abs(dur.time_duration.milliseconds) * 1e6 + std::abs(dur.time_duration.microseconds) * 1e3 + std::abs(dur.time_duration.nanoseconds); // b. Let decimalPart be fraction formatted as a nine-digit decimal number, // padded to the left with zeroes if necessary. int64_t divisor = 100000000; // c. If precision is "auto", then if (precision == Precision::kAuto) { // i. Set decimalPart to the longest possible substring of decimalPart // starting at position 0 and not ending with the code unit 0x0030 (DIGIT // ZERO). while (fraction > 0) { decimal_part.AppendInt(static_cast<int32_t>(fraction / divisor)); fraction %= divisor; divisor /= 10; } // d. Else if precision = 0, then } else if (precision == Precision::k0) { // i. Set decimalPart to "". // e. Else, } else { // i. Set decimalPart to the substring of decimalPart from 0 to precision. int32_t precision_len = static_cast<int32_t>(precision); DCHECK_LE(0, precision_len); DCHECK_GE(9, precision_len); for (int32_t len = 0; len < precision_len; len++) { decimal_part.AppendInt(static_cast<int32_t>(fraction / divisor)); fraction %= divisor; divisor /= 10; } } // f. Let secondsPart be abs(seconds) formatted as a decimal number. if (std::abs(seconds_add + dur.time_duration.seconds) < kMaxSafeInteger) { // Fast path: The seconds_add + dur.time_duration.seconds is in the range // the double could keep the precision. dur.time_duration.seconds += seconds_add; SNPrintF(buf, "%.0f", std::abs(dur.time_duration.seconds)); seconds_part.AppendCString(buf.data()); } else { // Slow path: The seconds_add + dur.time_duration.seconds is out of the // range which the double could keep the precision. Format by math via // BigInt. seconds_part.AppendString( BigInt::ToString( isolate, BigInt::Add( isolate, BigInt::FromNumber(isolate, isolate->factory()->NewNumber( std::abs(seconds_add))) .ToHandleChecked(), BigInt::FromNumber(isolate, isolate->factory()->NewNumber( std::abs(dur.time_duration.seconds))) .ToHandleChecked()) .ToHandleChecked()) .ToHandleChecked()); } // g. If decimalPart is not "", then if (decimal_part.Length() != 0) { // i. Set secondsPart to the string-concatenation of secondsPart, the code // unit 0x002E (FULL STOP), and decimalPart. seconds_part.AppendCharacter('.'); seconds_part.AppendString(decimal_part.Finish().ToHandleChecked()); } // h. Set timePart to the string concatenation of timePart, secondsPart, and // the code unit 0x0053 (LATIN CAPITAL LETTER S). time_part.AppendString(seconds_part.Finish().ToHandleChecked()); time_part.AppendCharacter('S'); } // 17. Let signPart be the code unit 0x002D (HYPHEN-MINUS) if sign < 0, and // otherwise the empty String. if (sign < 0) { builder.AppendCharacter('-'); } // 18. Let result be the string concatenation of signPart, the code unit // 0x0050 (LATIN CAPITAL LETTER P) and datePart. builder.AppendCharacter('P'); builder.AppendString(date_part.Finish().ToHandleChecked()); // 19. If timePart is not "", then if (time_part.Length() > 0) { // a. Set result to the string concatenation of result, the code unit 0x0054 // (LATIN CAPITAL LETTER T), and timePart. builder.AppendCharacter('T'); builder.AppendString(time_part.Finish().ToHandleChecked()); } return builder.Finish().ToHandleChecked(); } void ToZeroPaddedDecimalString(IncrementalStringBuilder* builder, int32_t n, int32_t min_length); // #sec-temporal-formatsecondsstringpart void FormatSecondsStringPart(IncrementalStringBuilder* builder, int32_t second, int32_t millisecond, int32_t microsecond, int32_t nanosecond, Precision precision) { // 1. Assert: second, millisecond, microsecond and nanosecond are integers. // 2. If precision is "minute", return "". if (precision == Precision::kMinute) { return; } // 3. Let secondsString be the string-concatenation of the code unit 0x003A // (COLON) and second formatted as a two-digit decimal number, padded to the // left with zeroes if necessary. builder->AppendCharacter(':'); ToZeroPaddedDecimalString(builder, second, 2); // 4. Let fraction be millisecond × 10^6 + microsecond × 10^3 + nanosecond. int64_t fraction = millisecond * 1000000 + microsecond * 1000 + nanosecond; int64_t divisor = 100000000; // 5. If precision is "auto", then if (precision == Precision::kAuto) { // a. If fraction is 0, return secondsString. if (fraction == 0) { return; } builder->AppendCharacter('.'); // b. Set fraction to ToZeroPaddedDecimalString(fraction, 9). // c. Set fraction to the longest possible substring of fraction starting at // position 0 and not ending with the code unit 0x0030 (DIGIT ZERO). while (fraction > 0) { builder->AppendInt(static_cast<int32_t>(fraction / divisor)); fraction %= divisor; divisor /= 10; } // 6. Else, } else { // a. If precision is 0, return secondsString. if (precision == Precision::k0) { return; } builder->AppendCharacter('.'); // b. Set fraction to ToZeroPaddedDecimalString(fraction, 9). // c. Set fraction to the substring of fraction from 0 to precision. int32_t precision_len = static_cast<int32_t>(precision); DCHECK_LE(0, precision_len); DCHECK_GE(9, precision_len); for (int32_t len = 0; len < precision_len; len++) { builder->AppendInt(static_cast<int32_t>(fraction / divisor)); fraction %= divisor; divisor /= 10; } } // 7. Return the string-concatenation of secondsString, the code unit 0x002E // (FULL STOP), and fraction. } // #sec-temporal-temporaltimetostring Handle<String> TemporalTimeToString(Isolate* isolate, const TimeRecord& time, Precision precision) { // 1. Assert: hour, minute, second, millisecond, microsecond and nanosecond // are integers. IncrementalStringBuilder builder(isolate); // 2. Let hour be ToZeroPaddedDecimalString(hour, 2). ToZeroPaddedDecimalString(&builder, time.hour, 2); builder.AppendCharacter(':'); // 3. Let minute be ToZeroPaddedDecimalString(minute, 2). ToZeroPaddedDecimalString(&builder, time.minute, 2); // 4. Let seconds be ! FormatSecondsStringPart(second, millisecond, // microsecond, nanosecond, precision). FormatSecondsStringPart(&builder, time.second, time.millisecond, time.microsecond, time.nanosecond, precision); // 5. Return the string-concatenation of hour, the code unit 0x003A (COLON), // minute, and seconds. return builder.Finish().ToHandleChecked(); } Handle<String> TemporalTimeToString(Isolate* isolate, Handle<JSTemporalPlainTime> temporal_time, Precision precision) { return TemporalTimeToString( isolate, {temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond()}, precision); } } // namespace namespace temporal { MaybeHandle<JSTemporalPlainDateTime> BuiltinTimeZoneGetPlainDateTimeFor( Isolate* isolate, Handle<JSReceiver> time_zone, Handle<JSTemporalInstant> instant, Handle<JSReceiver> calendar, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant). int64_t offset_nanoseconds; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_nanoseconds, GetOffsetNanosecondsFor(isolate, time_zone, instant, method_name), Handle<JSTemporalPlainDateTime>()); // 2. Let result be ! GetISOPartsFromEpoch(instant.[[Nanoseconds]]). DateTimeRecord result = GetISOPartsFromEpoch(isolate, handle(instant->nanoseconds(), isolate)); // 3. Set result to ! BalanceISODateTime(result.[[Year]], result.[[Month]], // result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], // result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]] + // offsetNanoseconds). // Note: Since offsetNanoseconds is bounded 86400x 10^9, the // result of result.[[Nanosecond]] + offsetNanoseconds may overflow int32_t // Therefore we distribute the sum to other fields below to make sure it won't // overflow each of the int32_t fields. But it will leave each field to be // balanced by BalanceISODateTime result.time.nanosecond += offset_nanoseconds % 1000; result.time.microsecond += (offset_nanoseconds / 1000) % 1000; result.time.millisecond += (offset_nanoseconds / 1000000L) % 1000; result.time.second += (offset_nanoseconds / 1000000000L) % 60; result.time.minute += (offset_nanoseconds / 60000000000L) % 60; result.time.hour += (offset_nanoseconds / 3600000000000L) % 24; result.date.day += (offset_nanoseconds / 86400000000000L); result = BalanceISODateTime(isolate, result); // 4. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], // result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], // result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], // calendar). return temporal::CreateTemporalDateTime(isolate, result, calendar); } } // namespace temporal namespace { // #sec-temporal-getpossibleinstantsfor MaybeHandle<FixedArray> GetPossibleInstantsFor(Isolate* isolate, Handle<JSReceiver> time_zone, Handle<Object> date_time) { TEMPORAL_ENTER_FUNC(); // 1. Let possibleInstants be ? Invoke(timeZone, "getPossibleInstantsFor", « // dateTime »). Handle<Object> function; ASSIGN_RETURN_ON_EXCEPTION( isolate, function, Object::GetProperty(isolate, time_zone, isolate->factory()->getPossibleInstantsFor_string()), FixedArray); if (!IsCallable(*function)) { THROW_NEW_ERROR( isolate, NewTypeError(MessageTemplate::kCalledNonCallable, isolate->factory()->getPossibleInstantsFor_string()), FixedArray); } Handle<Object> possible_instants; { Handle<Object> argv[] = {date_time}; ASSIGN_RETURN_ON_EXCEPTION( isolate, possible_instants, Execution::Call(isolate, function, time_zone, arraysize(argv), argv), FixedArray); } // Step 4-6 of GetPossibleInstantsFor is implemented inside // temporal_instant_fixed_array_from_iterable. { Handle<Object> argv[] = {possible_instants}; ASSIGN_RETURN_ON_EXCEPTION( isolate, possible_instants, Execution::CallBuiltin( isolate, isolate->temporal_instant_fixed_array_from_iterable(), possible_instants, arraysize(argv), argv), FixedArray); } DCHECK(IsFixedArray(*possible_instants)); // 7. Return list. return Handle<FixedArray>::cast(possible_instants); } // #sec-temporal-disambiguatepossibleinstants MaybeHandle<JSTemporalInstant> DisambiguatePossibleInstants( Isolate* isolate, Handle<FixedArray> possible_instants, Handle<JSReceiver> time_zone, Handle<Object> date_time_obj, Disambiguation disambiguation, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. Assert: dateTime has an [[InitializedTemporalDateTime]] internal slot. DCHECK(IsJSTemporalPlainDateTime(*date_time_obj)); Handle<JSTemporalPlainDateTime> date_time = Handle<JSTemporalPlainDateTime>::cast(date_time_obj); // 2. Let n be possibleInstants's length. int32_t n = possible_instants->length(); // 3. If n = 1, then if (n == 1) { // a. Return possibleInstants[0]. Handle<Object> ret_obj = FixedArray::get(*possible_instants, 0, isolate); DCHECK(IsJSTemporalInstant(*ret_obj)); return Handle<JSTemporalInstant>::cast(ret_obj); } // 4. If n ≠ 0, then if (n != 0) { // a. If disambiguation is "earlier" or "compatible", then if (disambiguation == Disambiguation::kEarlier || disambiguation == Disambiguation::kCompatible) { // i. Return possibleInstants[0]. Handle<Object> ret_obj = FixedArray::get(*possible_instants, 0, isolate); DCHECK(IsJSTemporalInstant(*ret_obj)); return Handle<JSTemporalInstant>::cast(ret_obj); } // b. If disambiguation is "later", then if (disambiguation == Disambiguation::kLater) { // i. Return possibleInstants[n − 1]. Handle<Object> ret_obj = FixedArray::get(*possible_instants, n - 1, isolate); DCHECK(IsJSTemporalInstant(*ret_obj)); return Handle<JSTemporalInstant>::cast(ret_obj); } // c. Assert: disambiguation is "reject". DCHECK_EQ(disambiguation, Disambiguation::kReject); // d. Throw a RangeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalInstant); } // 5. Assert: n = 0. DCHECK_EQ(n, 0); // 6. If disambiguation is "reject", then if (disambiguation == Disambiguation::kReject) { // a. Throw a RangeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalInstant); } // 7. Let epochNanoseconds be ! GetEpochFromISOParts(dateTime.[[ISOYear]], // dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], // dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], // dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], // dateTime.[[ISONanosecond]]). Handle<BigInt> epoch_nanoseconds = GetEpochFromISOParts( isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}); // 8. Let dayBeforeNs be epochNanoseconds - ℤ(nsPerDay). Handle<BigInt> one_day_in_ns = BigInt::FromUint64(isolate, 86400000000000ULL); Handle<BigInt> day_before_ns = BigInt::Subtract(isolate, epoch_nanoseconds, one_day_in_ns) .ToHandleChecked(); // 9. If ! IsValidEpochNanoseconds(dayBeforeNs) is false, throw a RangeError // exception. if (!IsValidEpochNanoseconds(isolate, day_before_ns)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalInstant); } // 10. Let dayBefore be ! CreateTemporalInstant(dayBeforeNs). Handle<JSTemporalInstant> day_before = temporal::CreateTemporalInstant(isolate, day_before_ns).ToHandleChecked(); // 11. Let dayAfterNs be epochNanoseconds + ℤ(nsPerDay). Handle<BigInt> day_after_ns = BigInt::Add(isolate, epoch_nanoseconds, one_day_in_ns).ToHandleChecked(); // 12. If ! IsValidEpochNanoseconds(dayAfterNs) is false, throw a RangeError // exception. if (!IsValidEpochNanoseconds(isolate, day_after_ns)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalInstant); } // 13. Let dayAfter be ! CreateTemporalInstant(dayAfterNs). Handle<JSTemporalInstant> day_after = temporal::CreateTemporalInstant(isolate, day_after_ns).ToHandleChecked(); // 10. Let offsetBefore be ? GetOffsetNanosecondsFor(timeZone, dayBefore). int64_t offset_before; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_before, GetOffsetNanosecondsFor(isolate, time_zone, day_before, method_name), Handle<JSTemporalInstant>()); // 11. Let offsetAfter be ? GetOffsetNanosecondsFor(timeZone, dayAfter). int64_t offset_after; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_after, GetOffsetNanosecondsFor(isolate, time_zone, day_after, method_name), Handle<JSTemporalInstant>()); // 12. Let nanoseconds be offsetAfter − offsetBefore. double nanoseconds = offset_after - offset_before; // 13. If disambiguation is "earlier", then if (disambiguation == Disambiguation::kEarlier) { // a. Let earlier be ? AddDateTime(dateTime.[[ISOYear]], // dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], // dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], // dateTime.[[ISOMillisecond]], // dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], // dateTime.[[Calendar]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −nanoseconds, // undefined). DateTimeRecord earlier; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, earlier, AddDateTime( isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}, handle(date_time->calendar(), isolate), {0, 0, 0, {0, 0, 0, 0, 0, 0, -nanoseconds}}, isolate->factory()->undefined_value()), Handle<JSTemporalInstant>()); // See https://github.com/tc39/proposal-temporal/issues/1816 // b. Let earlierDateTime be ? CreateTemporalDateTime(earlier.[[Year]], // earlier.[[Month]], earlier.[[Day]], earlier.[[Hour]], earlier.[[Minute]], // earlier.[[Second]], earlier.[[Millisecond]], earlier.[[Microsecond]], // earlier.[[Nanosecond]], dateTime.[[Calendar]]). Handle<JSTemporalPlainDateTime> earlier_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, earlier_date_time, temporal::CreateTemporalDateTime( isolate, earlier, handle(date_time->calendar(), isolate)), JSTemporalInstant); // c. Set possibleInstants to ? GetPossibleInstantsFor(timeZone, // earlierDateTime). ASSIGN_RETURN_ON_EXCEPTION( isolate, possible_instants, GetPossibleInstantsFor(isolate, time_zone, earlier_date_time), JSTemporalInstant); // d. If possibleInstants is empty, throw a RangeError exception. if (possible_instants->length() == 0) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalInstant); } // e. Return possibleInstants[0]. Handle<Object> ret_obj = FixedArray::get(*possible_instants, 0, isolate); DCHECK(IsJSTemporalInstant(*ret_obj)); return Handle<JSTemporalInstant>::cast(ret_obj); } // 14. Assert: disambiguation is "compatible" or "later". DCHECK(disambiguation == Disambiguation::kCompatible || disambiguation == Disambiguation::kLater); // 15. Let later be ? AddDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], // dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], // dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], // dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], // dateTime.[[Calendar]], 0, 0, 0, 0, 0, 0, 0, 0, 0, nanoseconds, undefined). DateTimeRecord later; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, later, AddDateTime(isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}, handle(date_time->calendar(), isolate), {0, 0, 0, {0, 0, 0, 0, 0, 0, nanoseconds}}, isolate->factory()->undefined_value()), Handle<JSTemporalInstant>()); // See https://github.com/tc39/proposal-temporal/issues/1816 // 16. Let laterDateTime be ? CreateTemporalDateTime(later.[[Year]], // later.[[Month]], later.[[Day]], later.[[Hour]], later.[[Minute]], // later.[[Second]], later.[[Millisecond]], later.[[Microsecond]], // later.[[Nanosecond]], dateTime.[[Calendar]]). Handle<JSTemporalPlainDateTime> later_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, later_date_time, temporal::CreateTemporalDateTime(isolate, later, handle(date_time->calendar(), isolate)), JSTemporalInstant); // 17. Set possibleInstants to ? GetPossibleInstantsFor(timeZone, // laterDateTime). ASSIGN_RETURN_ON_EXCEPTION( isolate, possible_instants, GetPossibleInstantsFor(isolate, time_zone, later_date_time), JSTemporalInstant); // 18. Set n to possibleInstants's length. n = possible_instants->length(); // 19. If n = 0, throw a RangeError exception. if (n == 0) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalInstant); } // 20. Return possibleInstants[n − 1]. Handle<Object> ret_obj = FixedArray::get(*possible_instants, n - 1, isolate); DCHECK(IsJSTemporalInstant(*ret_obj)); return Handle<JSTemporalInstant>::cast(ret_obj); } // #sec-temporal-gettemporalcalendarwithisodefault MaybeHandle<JSReceiver> GetTemporalCalendarWithISODefault( Isolate* isolate, Handle<JSReceiver> item, const char* method_name) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 1. If item has an [[InitializedTemporalDate]], // [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], // [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or // [[InitializedTemporalZonedDateTime]] internal slot, then a. Return // item.[[Calendar]]. if (IsJSTemporalPlainDate(*item)) { return handle(Handle<JSTemporalPlainDate>::cast(item)->calendar(), isolate); } if (IsJSTemporalPlainDateTime(*item)) { return handle(Handle<JSTemporalPlainDateTime>::cast(item)->calendar(), isolate); } if (IsJSTemporalPlainMonthDay(*item)) { return handle(Handle<JSTemporalPlainMonthDay>::cast(item)->calendar(), isolate); } if (IsJSTemporalPlainTime(*item)) { return handle(Handle<JSTemporalPlainTime>::cast(item)->calendar(), isolate); } if (IsJSTemporalPlainYearMonth(*item)) { return handle(Handle<JSTemporalPlainYearMonth>::cast(item)->calendar(), isolate); } if (IsJSTemporalZonedDateTime(*item)) { return handle(Handle<JSTemporalZonedDateTime>::cast(item)->calendar(), isolate); } // 2. Let calendar be ? Get(item, "calendar"). Handle<Object> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, JSReceiver::GetProperty(isolate, item, factory->calendar_string()), JSReceiver); // 3. Return ? ToTemporalCalendarWithISODefault(calendar). return ToTemporalCalendarWithISODefault(isolate, calendar, method_name); } enum class RequiredFields { kNone, kTimeZone, kTimeZoneAndOffset, kDay, kYearAndDay }; // The common part of PrepareTemporalFields and PreparePartialTemporalFields // #sec-temporal-preparetemporalfields // #sec-temporal-preparepartialtemporalfields V8_WARN_UNUSED_RESULT MaybeHandle<JSReceiver> PrepareTemporalFieldsOrPartial( Isolate* isolate, Handle<JSReceiver> fields, Handle<FixedArray> field_names, RequiredFields required, bool partial) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 1. Let result be OrdinaryObjectCreate(null). Handle<JSReceiver> result = isolate->factory()->NewJSObjectWithNullProto(); // 2. Let any be false. bool any = false; // 3. For each value property of fieldNames, do int length = field_names->length(); for (int i = 0; i < length; i++) { Handle<Object> property_obj = Handle<Object>(field_names->get(i), isolate); Handle<String> property = Handle<String>::cast(property_obj); // a. Let value be ? Get(fields, property). Handle<Object> value; ASSIGN_RETURN_ON_EXCEPTION( isolate, value, JSReceiver::GetProperty(isolate, fields, property), JSObject); // b. If value is undefined, then if (IsUndefined(*value)) { // This part is only for PrepareTemporalFields // Skip for the case of PreparePartialTemporalFields. if (partial) continue; // i. If requiredFields contains property, then if (((required == RequiredFields::kDay || required == RequiredFields::kYearAndDay) && String::Equals(isolate, property, factory->day_string())) || ((required == RequiredFields::kTimeZone || required == RequiredFields::kTimeZoneAndOffset) && String::Equals(isolate, property, factory->timeZone_string())) || (required == RequiredFields::kTimeZoneAndOffset && String::Equals(isolate, property, factory->offset_string())) || (required == RequiredFields::kYearAndDay && String::Equals(isolate, property, factory->year_string()))) { // 1. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSObject); } // ii. Else, // 1. If property is in the Property column of Table 13, then // a. Set value to the corresponding Default value of the same row. if (String::Equals(isolate, property, factory->hour_string()) || String::Equals(isolate, property, factory->minute_string()) || String::Equals(isolate, property, factory->second_string()) || String::Equals(isolate, property, factory->millisecond_string()) || String::Equals(isolate, property, factory->microsecond_string()) || String::Equals(isolate, property, factory->nanosecond_string())) { value = Handle<Object>(Smi::zero(), isolate); } } else { // For both PrepareTemporalFields and PreparePartialTemporalFields any = partial; // c. Else, // i. If property is in the Property column of Table 13 and there is a // Conversion value in the same row, then // 1. Let Conversion represent the abstract operation named by the // Conversion value of the same row. // 2. Set value to ? Conversion(value). if (String::Equals(isolate, property, factory->month_string()) || String::Equals(isolate, property, factory->day_string())) { ASSIGN_RETURN_ON_EXCEPTION(isolate, value, ToPositiveInteger(isolate, value), JSObject); } else if (String::Equals(isolate, property, factory->year_string()) || String::Equals(isolate, property, factory->hour_string()) || String::Equals(isolate, property, factory->minute_string()) || String::Equals(isolate, property, factory->second_string()) || String::Equals(isolate, property, factory->millisecond_string()) || String::Equals(isolate, property, factory->microsecond_string()) || String::Equals(isolate, property, factory->nanosecond_string()) || String::Equals(isolate, property, factory->eraYear_string())) { ASSIGN_RETURN_ON_EXCEPTION( isolate, value, ToIntegerThrowOnInfinity(isolate, value), JSObject); } else if (String::Equals(isolate, property, factory->monthCode_string()) || String::Equals(isolate, property, factory->offset_string()) || String::Equals(isolate, property, factory->era_string())) { ASSIGN_RETURN_ON_EXCEPTION(isolate, value, Object::ToString(isolate, value), JSObject); } } // d. Perform ! CreateDataPropertyOrThrow(result, property, value). CHECK(JSReceiver::CreateDataProperty(isolate, result, property, value, Just(kThrowOnError)) .FromJust()); } // Only for PreparePartialTemporalFields if (partial) { // 5. If any is false, then if (!any) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSObject); } } // 4. Return result. return result; } // #sec-temporal-preparetemporalfields V8_WARN_UNUSED_RESULT MaybeHandle<JSReceiver> PrepareTemporalFields( Isolate* isolate, Handle<JSReceiver> fields, Handle<FixedArray> field_names, RequiredFields required) { TEMPORAL_ENTER_FUNC(); return PrepareTemporalFieldsOrPartial(isolate, fields, field_names, required, false); } // #sec-temporal-preparepartialtemporalfields V8_WARN_UNUSED_RESULT MaybeHandle<JSReceiver> PreparePartialTemporalFields( Isolate* isolate, Handle<JSReceiver> fields, Handle<FixedArray> field_names) { TEMPORAL_ENTER_FUNC(); return PrepareTemporalFieldsOrPartial(isolate, fields, field_names, RequiredFields::kNone, true); } // Template for DateFromFields, YearMonthFromFields, and MonthDayFromFields template <typename T> MaybeHandle<T> FromFields(Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSReceiver> fields, Handle<Object> options, Handle<String> property, InstanceType type) { Handle<Object> function; ASSIGN_RETURN_ON_EXCEPTION( isolate, function, Object::GetProperty(isolate, calendar, property), T); if (!IsCallable(*function)) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCalledNonCallable, property), T); } Handle<Object> argv[] = {fields, options}; Handle<Object> result; ASSIGN_RETURN_ON_EXCEPTION( isolate, result, Execution::Call(isolate, function, calendar, 2, argv), T); if ((!IsHeapObject(*result)) || HeapObject::cast(*result)->map()->instance_type() != type) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), T); } return Handle<T>::cast(result); } // #sec-temporal-datefromfields MaybeHandle<JSTemporalPlainDate> DateFromFields(Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSReceiver> fields, Handle<Object> options) { return FromFields<JSTemporalPlainDate>( isolate, calendar, fields, options, isolate->factory()->dateFromFields_string(), JS_TEMPORAL_PLAIN_DATE_TYPE); } // #sec-temporal-yearmonthfromfields MaybeHandle<JSTemporalPlainYearMonth> YearMonthFromFields( Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSReceiver> fields, Handle<Object> options) { return FromFields<JSTemporalPlainYearMonth>( isolate, calendar, fields, options, isolate->factory()->yearMonthFromFields_string(), JS_TEMPORAL_PLAIN_YEAR_MONTH_TYPE); } MaybeHandle<JSTemporalPlainYearMonth> YearMonthFromFields( Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSReceiver> fields) { // 1. If options is not present, set options to undefined. return YearMonthFromFields(isolate, calendar, fields, isolate->factory()->undefined_value()); } // #sec-temporal-monthdayfromfields MaybeHandle<JSTemporalPlainMonthDay> MonthDayFromFields( Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSReceiver> fields, Handle<Object> options) { return FromFields<JSTemporalPlainMonthDay>( isolate, calendar, fields, options, isolate->factory()->monthDayFromFields_string(), JS_TEMPORAL_PLAIN_MONTH_DAY_TYPE); } MaybeHandle<JSTemporalPlainMonthDay> MonthDayFromFields( Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSReceiver> fields) { // 1. If options is not present, set options to undefined. return MonthDayFromFields(isolate, calendar, fields, isolate->factory()->undefined_value()); } // #sec-temporal-totemporaloverflow Maybe<ShowOverflow> ToTemporalOverflow(Isolate* isolate, Handle<Object> options, const char* method_name) { // 1. If options is undefined, return "constrain". if (IsUndefined(*options)) return Just(ShowOverflow::kConstrain); DCHECK(IsJSReceiver(*options)); // 2. Return ? GetOption(options, "overflow", « String », « "constrain", // "reject" », "constrain"). return GetStringOption<ShowOverflow>( isolate, Handle<JSReceiver>::cast(options), "overflow", method_name, {"constrain", "reject"}, {ShowOverflow::kConstrain, ShowOverflow::kReject}, ShowOverflow::kConstrain); } // #sec-temporal-totemporaloffset Maybe<Offset> ToTemporalOffset(Isolate* isolate, Handle<Object> options, Offset fallback, const char* method_name) { // 1. If options is undefined, return fallback. if (IsUndefined(*options)) return Just(fallback); DCHECK(IsJSReceiver(*options)); // 2. Return ? GetOption(options, "offset", « String », « "prefer", "use", // "ignore", "reject" », fallback). return GetStringOption<Offset>( isolate, Handle<JSReceiver>::cast(options), "offset", method_name, {"prefer", "use", "ignore", "reject"}, {Offset::kPrefer, Offset::kUse, Offset::kIgnore, Offset::kReject}, fallback); } // #sec-temporal-totemporaldisambiguation Maybe<Disambiguation> ToTemporalDisambiguation(Isolate* isolate, Handle<Object> options, const char* method_name) { // 1. If options is undefined, return "compatible". if (IsUndefined(*options)) return Just(Disambiguation::kCompatible); DCHECK(IsJSReceiver(*options)); // 2. Return ? GetOption(options, "disambiguation", « String », « // "compatible", "earlier", "later", "reject" », "compatible"). return GetStringOption<Disambiguation>( isolate, Handle<JSReceiver>::cast(options), "disambiguation", method_name, {"compatible", "earlier", "later", "reject"}, {Disambiguation::kCompatible, Disambiguation::kEarlier, Disambiguation::kLater, Disambiguation::kReject}, Disambiguation::kCompatible); } // #sec-temporal-builtintimezonegetinstantfor MaybeHandle<JSTemporalInstant> BuiltinTimeZoneGetInstantFor( Isolate* isolate, Handle<JSReceiver> time_zone, Handle<JSTemporalPlainDateTime> date_time, Disambiguation disambiguation, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. Assert: dateTime has an [[InitializedTemporalDateTime]] internal slot. // 2. Let possibleInstants be ? GetPossibleInstantsFor(timeZone, dateTime). Handle<FixedArray> possible_instants; ASSIGN_RETURN_ON_EXCEPTION( isolate, possible_instants, GetPossibleInstantsFor(isolate, time_zone, date_time), JSTemporalInstant); // 3. Return ? DisambiguatePossibleInstants(possibleInstants, timeZone, // dateTime, disambiguation). return DisambiguatePossibleInstants(isolate, possible_instants, time_zone, date_time, disambiguation, method_name); } // #sec-temporal-totemporalinstant MaybeHandle<JSTemporalInstant> ToTemporalInstant(Isolate* isolate, Handle<Object> item, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If Type(item) is Object, then // a. If item has an [[InitializedTemporalInstant]] internal slot, then if (IsJSTemporalInstant(*item)) { // i. Return item. return Handle<JSTemporalInstant>::cast(item); } // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then if (IsJSTemporalZonedDateTime(*item)) { // i. Return ! CreateTemporalInstant(item.[[Nanoseconds]]). Handle<BigInt> nanoseconds = handle(JSTemporalZonedDateTime::cast(*item)->nanoseconds(), isolate); return temporal::CreateTemporalInstant(isolate, nanoseconds) .ToHandleChecked(); } // 2. Let string be ? ToString(item). Handle<String> string; ASSIGN_RETURN_ON_EXCEPTION(isolate, string, Object::ToString(isolate, item), JSTemporalInstant); // 3. Let epochNanoseconds be ? ParseTemporalInstant(string). Handle<BigInt> epoch_nanoseconds; ASSIGN_RETURN_ON_EXCEPTION(isolate, epoch_nanoseconds, ParseTemporalInstant(isolate, string), JSTemporalInstant); // 4. Return ? CreateTemporalInstant(ℤ(epochNanoseconds)). return temporal::CreateTemporalInstant(isolate, epoch_nanoseconds); } } // namespace namespace temporal { // #sec-temporal-totemporalcalendar MaybeHandle<JSReceiver> ToTemporalCalendar( Isolate* isolate, Handle<Object> temporal_calendar_like, const char* method_name) { Factory* factory = isolate->factory(); // 1.If Type(temporalCalendarLike) is Object, then if (IsJSReceiver(*temporal_calendar_like)) { // a. If temporalCalendarLike has an [[InitializedTemporalDate]], // [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], // [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or // [[InitializedTemporalZonedDateTime]] internal slot, then i. Return // temporalCalendarLike.[[Calendar]]. #define EXTRACT_CALENDAR(T, obj) \ if (IsJSTemporal##T(*obj)) { \ return handle(Handle<JSTemporal##T>::cast(obj)->calendar(), isolate); \ } EXTRACT_CALENDAR(PlainDate, temporal_calendar_like) EXTRACT_CALENDAR(PlainDateTime, temporal_calendar_like) EXTRACT_CALENDAR(PlainMonthDay, temporal_calendar_like) EXTRACT_CALENDAR(PlainTime, temporal_calendar_like) EXTRACT_CALENDAR(PlainYearMonth, temporal_calendar_like) EXTRACT_CALENDAR(ZonedDateTime, temporal_calendar_like) #undef EXTRACT_CALENDAR Handle<JSReceiver> obj = Handle<JSReceiver>::cast(temporal_calendar_like); // b. If ? HasProperty(temporalCalendarLike, "calendar") is false, return // temporalCalendarLike. bool has; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, has, JSReceiver::HasProperty(isolate, obj, factory->calendar_string()), Handle<JSReceiver>()); if (!has) { return obj; } // c. Set temporalCalendarLike to ? Get(temporalCalendarLike, "calendar"). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_calendar_like, JSReceiver::GetProperty(isolate, obj, factory->calendar_string()), JSReceiver); // d. If Type(temporalCalendarLike) is Object if (IsJSReceiver(*temporal_calendar_like)) { obj = Handle<JSReceiver>::cast(temporal_calendar_like); // and ? HasProperty(temporalCalendarLike, "calendar") is false, MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, has, JSReceiver::HasProperty(isolate, obj, factory->calendar_string()), Handle<JSReceiver>()); if (!has) { // return temporalCalendarLike. return obj; } } } // 2. Let identifier be ? ToString(temporalCalendarLike). Handle<String> identifier; ASSIGN_RETURN_ON_EXCEPTION(isolate, identifier, Object::ToString(isolate, temporal_calendar_like), JSReceiver); // 3. Let identifier be ? ParseTemporalCalendarString(identifier). ASSIGN_RETURN_ON_EXCEPTION(isolate, identifier, ParseTemporalCalendarString(isolate, identifier), JSReceiver); // 4. If IsBuiltinCalendar(identifier) is false, throw a RangeError // exception. if (!IsBuiltinCalendar(isolate, identifier)) { THROW_NEW_ERROR( isolate, NewRangeError(MessageTemplate::kInvalidCalendar, identifier), JSReceiver); } // 5. Return ? CreateTemporalCalendar(identifier). return CreateTemporalCalendar(isolate, identifier); } } // namespace temporal namespace { // #sec-temporal-totemporalcalendarwithisodefault MaybeHandle<JSReceiver> ToTemporalCalendarWithISODefault( Isolate* isolate, Handle<Object> temporal_calendar_like, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If temporalCalendarLike is undefined, then if (IsUndefined(*temporal_calendar_like)) { // a. Return ? GetISO8601Calendar(). return temporal::GetISO8601Calendar(isolate); } // 2. Return ? ToTemporalCalendar(temporalCalendarLike). return temporal::ToTemporalCalendar(isolate, temporal_calendar_like, method_name); } // Create « "day", "hour", "microsecond", "millisecond", "minute", "month", // "monthCode", "nanosecond", "second", "year" » in several AOs. Handle<FixedArray> All10UnitsInFixedArray(Isolate* isolate) { Handle<FixedArray> field_names = isolate->factory()->NewFixedArray(10); field_names->set(0, ReadOnlyRoots(isolate).day_string()); field_names->set(1, ReadOnlyRoots(isolate).hour_string()); field_names->set(2, ReadOnlyRoots(isolate).microsecond_string()); field_names->set(3, ReadOnlyRoots(isolate).millisecond_string()); field_names->set(4, ReadOnlyRoots(isolate).minute_string()); field_names->set(5, ReadOnlyRoots(isolate).month_string()); field_names->set(6, ReadOnlyRoots(isolate).monthCode_string()); field_names->set(7, ReadOnlyRoots(isolate).nanosecond_string()); field_names->set(8, ReadOnlyRoots(isolate).second_string()); field_names->set(9, ReadOnlyRoots(isolate).year_string()); return field_names; } // Create « "day", "month", "monthCode", "year" » in several AOs. Handle<FixedArray> DayMonthMonthCodeYearInFixedArray(Isolate* isolate) { Handle<FixedArray> field_names = isolate->factory()->NewFixedArray(4); field_names->set(0, ReadOnlyRoots(isolate).day_string()); field_names->set(1, ReadOnlyRoots(isolate).month_string()); field_names->set(2, ReadOnlyRoots(isolate).monthCode_string()); field_names->set(3, ReadOnlyRoots(isolate).year_string()); return field_names; } // Create « "month", "monthCode", "year" » in several AOs. Handle<FixedArray> MonthMonthCodeYearInFixedArray(Isolate* isolate) { Handle<FixedArray> field_names = isolate->factory()->NewFixedArray(3); field_names->set(0, ReadOnlyRoots(isolate).month_string()); field_names->set(1, ReadOnlyRoots(isolate).monthCode_string()); field_names->set(2, ReadOnlyRoots(isolate).year_string()); return field_names; } // Create « "monthCode", "year" » in several AOs. Handle<FixedArray> MonthCodeYearInFixedArray(Isolate* isolate) { Handle<FixedArray> field_names = isolate->factory()->NewFixedArray(2); field_names->set(0, ReadOnlyRoots(isolate).monthCode_string()); field_names->set(1, ReadOnlyRoots(isolate).year_string()); return field_names; } // #sec-temporal-totemporaldate MaybeHandle<JSTemporalPlainDate> ToTemporalDate(Isolate* isolate, Handle<Object> item_obj, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 2. Assert: Type(options) is Object or Undefined. DCHECK(IsJSReceiver(*options) || IsUndefined(*options)); // 3. If Type(item) is Object, then if (IsJSReceiver(*item_obj)) { Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); // a. If item has an [[InitializedTemporalDate]] internal slot, then // i. Return item. if (IsJSTemporalPlainDate(*item)) { return Handle<JSTemporalPlainDate>::cast(item); } // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, // then if (IsJSTemporalZonedDateTime(*item)) { // i. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainDate>()); // ii. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]). Handle<JSTemporalZonedDateTime> zoned_date_time = Handle<JSTemporalZonedDateTime>::cast(item); Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); // iii. Let plainDateTime be ? // BuiltinTimeZoneGetPlainDateTimeFor(item.[[TimeZone]], // instant, item.[[Calendar]]). Handle<JSTemporalPlainDateTime> plain_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, plain_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor( isolate, Handle<JSReceiver>(zoned_date_time->time_zone(), isolate), instant, Handle<JSReceiver>(zoned_date_time->calendar(), isolate), method_name), JSTemporalPlainDate); // iv. Return ! CreateTemporalDate(plainDateTime.[[ISOYear]], // plainDateTime.[[ISOMonth]], plainDateTime.[[ISODay]], // plainDateTime.[[Calendar]]). return CreateTemporalDate( isolate, {plain_date_time->iso_year(), plain_date_time->iso_month(), plain_date_time->iso_day()}, handle(plain_date_time->calendar(), isolate)) .ToHandleChecked(); } // c. If item has an [[InitializedTemporalDateTime]] internal slot, then // item.[[ISODay]], item.[[Calendar]]). if (IsJSTemporalPlainDateTime(*item)) { // i. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainDate>()); // ii. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], Handle<JSTemporalPlainDateTime> date_time = Handle<JSTemporalPlainDateTime>::cast(item); return CreateTemporalDate(isolate, {date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, handle(date_time->calendar(), isolate)) .ToHandleChecked(); } // d. Let calendar be ? GetTemporalCalendarWithISODefault(item). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, GetTemporalCalendarWithISODefault(isolate, item, method_name), JSTemporalPlainDate); // e. Let fieldNames be ? CalendarFields(calendar, « "day", "month", // "monthCode", "year" »). Handle<FixedArray> field_names = DayMonthMonthCodeYearInFixedArray(isolate); ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, CalendarFields(isolate, calendar, field_names), JSTemporalPlainDate); // f. Let fields be ? PrepareTemporalFields(item, // fieldNames, «»). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION(isolate, fields, PrepareTemporalFields(isolate, item, field_names, RequiredFields::kNone), JSTemporalPlainDate); // g. Return ? DateFromFields(calendar, fields, options). return DateFromFields(isolate, calendar, fields, options); } // 4. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainDate>()); // 5. Let string be ? ToString(item). Handle<String> string; ASSIGN_RETURN_ON_EXCEPTION(isolate, string, Object::ToString(isolate, item_obj), JSTemporalPlainDate); // 6. Let result be ? ParseTemporalDateString(string). DateRecordWithCalendar result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ParseTemporalDateString(isolate, string), Handle<JSTemporalPlainDate>()); // 7. Assert: ! IsValidISODate(result.[[Year]], result.[[Month]], // result.[[Day]]) is true. DCHECK(IsValidISODate(isolate, result.date)); // 8. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, result.calendar, method_name), JSTemporalPlainDate); // 9. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], // result.[[Day]], calendar). return CreateTemporalDate(isolate, result.date, calendar); } MaybeHandle<JSTemporalPlainDate> ToTemporalDate(Isolate* isolate, Handle<Object> item_obj, const char* method_name) { // 1. If options is not present, set options to undefined. return ToTemporalDate(isolate, item_obj, isolate->factory()->undefined_value(), method_name); } // #sec-isintegralnumber bool IsIntegralNumber(Isolate* isolate, Handle<Object> argument) { // 1. If Type(argument) is not Number, return false. if (!IsNumber(*argument)) return false; // 2. If argument is NaN, +∞𝔽, or -∞𝔽, return false. double number = Object::Number(*argument); if (!std::isfinite(number)) return false; // 3. If floor(abs(ℝ(argument))) ≠ abs(ℝ(argument)), return false. if (std::floor(std::abs(number)) != std::abs(number)) return false; // 4. Return true. return true; } // #sec-temporal-tointegerwithoutrounding Maybe<double> ToIntegerWithoutRounding(Isolate* isolate, Handle<Object> argument) { // 1. Let number be ? ToNumber(argument). Handle<Object> number; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, number, Object::ToNumber(isolate, argument), Nothing<double>()); // 2. If number is NaN, +0𝔽, or −0𝔽 return 0. if (IsNaN(*number) || Object::Number(*number) == 0) { return Just(static_cast<double>(0)); } // 3. If IsIntegralNumber(number) is false, throw a RangeError exception. if (!IsIntegralNumber(isolate, number)) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<double>()); } // 4. Return ℝ(number). return Just(Object::Number(*number)); } } // namespace namespace temporal { // #sec-temporal-regulatetime Maybe<TimeRecord> RegulateTime(Isolate* isolate, const TimeRecord& time, ShowOverflow overflow) { TEMPORAL_ENTER_FUNC(); // 1. Assert: hour, minute, second, millisecond, microsecond and nanosecond // are integers. // 2. Assert: overflow is either "constrain" or "reject". switch (overflow) { case ShowOverflow::kConstrain: { TimeRecord result(time); // 3. If overflow is "constrain", then // a. Return ! ConstrainTime(hour, minute, second, millisecond, // microsecond, nanosecond). result.hour = std::max(std::min(result.hour, 23), 0); result.minute = std::max(std::min(result.minute, 59), 0); result.second = std::max(std::min(result.second, 59), 0); result.millisecond = std::max(std::min(result.millisecond, 999), 0); result.microsecond = std::max(std::min(result.microsecond, 999), 0); result.nanosecond = std::max(std::min(result.nanosecond, 999), 0); return Just(result); } case ShowOverflow::kReject: // 4. If overflow is "reject", then // a. If ! IsValidTime(hour, minute, second, millisecond, microsecond, // nanosecond) is false, throw a RangeError exception. if (!IsValidTime(isolate, time)) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<TimeRecord>()); } // b. Return the new Record { [[Hour]]: hour, [[Minute]]: minute, // [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: // microsecond, [[Nanosecond]]: nanosecond }. return Just(time); } } // #sec-temporal-totemporaltime MaybeHandle<JSTemporalPlainTime> ToTemporalTime( Isolate* isolate, Handle<Object> item_obj, const char* method_name, ShowOverflow overflow = ShowOverflow::kConstrain) { Factory* factory = isolate->factory(); TimeRecordWithCalendar result; // 2. Assert: overflow is either "constrain" or "reject". // 3. If Type(item) is Object, then if (IsJSReceiver(*item_obj)) { Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); // a. If item has an [[InitializedTemporalTime]] internal slot, then // i. Return item. if (IsJSTemporalPlainTime(*item)) { return Handle<JSTemporalPlainTime>::cast(item); } // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, // then if (IsJSTemporalZonedDateTime(*item)) { // i. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]). Handle<JSTemporalZonedDateTime> zoned_date_time = Handle<JSTemporalZonedDateTime>::cast(item); Handle<JSTemporalInstant> instant = CreateTemporalInstant(isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); // ii. Set plainDateTime to ? // BuiltinTimeZoneGetPlainDateTimeFor(item.[[TimeZone]], // instant, item.[[Calendar]]). Handle<JSTemporalPlainDateTime> plain_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, plain_date_time, BuiltinTimeZoneGetPlainDateTimeFor( isolate, Handle<JSReceiver>(zoned_date_time->time_zone(), isolate), instant, Handle<JSReceiver>(zoned_date_time->calendar(), isolate), method_name), JSTemporalPlainTime); // iii. Return ! // CreateTemporalTime(plainDateTime.[[ISOHour]], // plainDateTime.[[ISOMinute]], plainDateTime.[[ISOSecond]], // plainDateTime.[[ISOMillisecond]], plainDateTime.[[ISOMicrosecond]], // plainDateTime.[[ISONanosecond]]). return CreateTemporalTime(isolate, {plain_date_time->iso_hour(), plain_date_time->iso_minute(), plain_date_time->iso_second(), plain_date_time->iso_millisecond(), plain_date_time->iso_microsecond(), plain_date_time->iso_nanosecond()}) .ToHandleChecked(); } // c. If item has an [[InitializedTemporalDateTime]] internal slot, then if (IsJSTemporalPlainDateTime(*item)) { // i. Return ! CreateTemporalTime(item.[[ISOHour]], item.[[ISOMinute]], // item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]], // item.[[ISONanosecond]]). Handle<JSTemporalPlainDateTime> date_time = Handle<JSTemporalPlainDateTime>::cast(item); return CreateTemporalTime( isolate, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}) .ToHandleChecked(); } // d. Let calendar be ? GetTemporalCalendarWithISODefault(item). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, GetTemporalCalendarWithISODefault(isolate, item, method_name), JSTemporalPlainTime); // e. If ? ToString(calendar) is not "iso8601", then Handle<String> identifier; ASSIGN_RETURN_ON_EXCEPTION(isolate, identifier, Object::ToString(isolate, calendar), JSTemporalPlainTime); if (!String::Equals(isolate, factory->iso8601_string(), identifier)) { // i. Throw a RangeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalPlainTime); } // f. Let result be ? ToTemporalTimeRecord(item). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result.time, ToTemporalTimeRecord(isolate, item, method_name), Handle<JSTemporalPlainTime>()); // g. Set result to ? RegulateTime(result.[[Hour]], result.[[Minute]], // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], // result.[[Nanosecond]], overflow). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result.time, RegulateTime(isolate, result.time, overflow), Handle<JSTemporalPlainTime>()); } else { // 4. Else, // a. Let string be ? ToString(item). Handle<String> string; ASSIGN_RETURN_ON_EXCEPTION(isolate, string, Object::ToString(isolate, item_obj), JSTemporalPlainTime); // b. Let result be ? ParseTemporalTimeString(string). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ParseTemporalTimeString(isolate, string), Handle<JSTemporalPlainTime>()); // c. Assert: ! IsValidTime(result.[[Hour]], result.[[Minute]], // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], // result.[[Nanosecond]]) is true. DCHECK(IsValidTime(isolate, result.time)); // d. If result.[[Calendar]] is not one of undefined or "iso8601", then DCHECK(IsUndefined(*result.calendar) || IsString(*result.calendar)); if (!IsUndefined(*result.calendar) && !String::Equals(isolate, Handle<String>::cast(result.calendar), isolate->factory()->iso8601_string())) { // i. Throw a RangeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalPlainTime); } } // 5. Return ? CreateTemporalTime(result.[[Hour]], result.[[Minute]], // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], // result.[[Nanosecond]]). return CreateTemporalTime(isolate, result.time); } // Helper function to loop through Table 8 Duration Record Fields // This function implement // "For each row of Table 8, except the header row, in table order, do" // loop. It is designed to be used to implement the common part of // ToPartialDuration, ToTemporalDurationRecord Maybe<bool> IterateDurationRecordFieldsTable( Isolate* isolate, Handle<JSReceiver> temporal_duration_like, Maybe<bool> (*RowFunction)(Isolate*, Handle<JSReceiver> temporal_duration_like, Handle<String>, double*), DurationRecord* record) { Factory* factory = isolate->factory(); std::array<std::pair<Handle<String>, double*>, 10> table8 = { {{factory->days_string(), &record->time_duration.days}, {factory->hours_string(), &record->time_duration.hours}, {factory->microseconds_string(), &record->time_duration.microseconds}, {factory->milliseconds_string(), &record->time_duration.milliseconds}, {factory->minutes_string(), &record->time_duration.minutes}, {factory->months_string(), &record->months}, {factory->nanoseconds_string(), &record->time_duration.nanoseconds}, {factory->seconds_string(), &record->time_duration.seconds}, {factory->weeks_string(), &record->weeks}, {factory->years_string(), &record->years}}}; // x. Let any be false. bool any = false; // x+1. For each row of Table 8, except the header row, in table order, do for (const auto& row : table8) { bool result; // row.first is prop: the Property Name value of the current row // row.second is the address of result's field whose name is the Field Name // value of the current row MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, RowFunction(isolate, temporal_duration_like, row.first, row.second), Nothing<bool>()); any |= result; } return Just(any); } // #sec-temporal-totemporaldurationrecord Maybe<DurationRecord> ToTemporalDurationRecord( Isolate* isolate, Handle<Object> temporal_duration_like_obj, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If Type(temporalDurationLike) is not Object, then if (!IsJSReceiver(*temporal_duration_like_obj)) { // a. Let string be ? ToString(temporalDurationLike). Handle<String> string; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, string, Object::ToString(isolate, temporal_duration_like_obj), Nothing<DurationRecord>()); // b. Let result be ? ParseTemporalDurationString(string). return ParseTemporalDurationString(isolate, string); } Handle<JSReceiver> temporal_duration_like = Handle<JSReceiver>::cast(temporal_duration_like_obj); // 2. If temporalDurationLike has an [[InitializedTemporalDuration]] internal // slot, then if (IsJSTemporalDuration(*temporal_duration_like)) { // a. Return ! CreateDurationRecord(temporalDurationLike.[[Years]], // temporalDurationLike.[[Months]], temporalDurationLike.[[Weeks]], // temporalDurationLike.[[Days]], temporalDurationLike.[[Hours]], // temporalDurationLike.[[Minutes]], temporalDurationLike.[[Seconds]], // temporalDurationLike.[[Milliseconds]], // temporalDurationLike.[[Microseconds]], // temporalDurationLike.[[Nanoseconds]]). Handle<JSTemporalDuration> duration = Handle<JSTemporalDuration>::cast(temporal_duration_like); return DurationRecord::Create( isolate, Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), Object::Number(duration->days()), Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())); } // 3. Let result be a new Record with all the internal slots given in the // Internal Slot column in Table 8. DurationRecord result; // 4. Let any be false. bool any = false; // 5. For each row of Table 8, except the header row, in table order, do MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, any, IterateDurationRecordFieldsTable( isolate, temporal_duration_like, [](Isolate* isolate, Handle<JSReceiver> temporal_duration_like, Handle<String> prop, double* field) -> Maybe<bool> { bool not_undefined = false; // a. Let prop be the Property value of the current row. Handle<Object> val; // b. Let val be ? Get(temporalDurationLike, prop). ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, val, JSReceiver::GetProperty(isolate, temporal_duration_like, prop), Nothing<bool>()); // c. If val is undefined, then if (IsUndefined(*val)) { // i. Set result's internal slot whose name is the Internal Slot // value of the current row to 0. *field = 0; // d. Else, } else { // i. Set any to true. not_undefined = true; // ii. Let val be 𝔽(? ToIntegerWithoutRounding(val)). // iii. Set result's field whose name is the Field Name value of // the current row to val. MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, *field, ToIntegerWithoutRounding(isolate, val), Nothing<bool>()); } return Just(not_undefined); }, &result), Nothing<DurationRecord>()); // 6. If any is false, then if (!any) { // a. Throw a TypeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Nothing<DurationRecord>()); } // 7. If ! IsValidDuration(result.[[Years]], result.[[Months]], // result.[[Weeks]] result.[[Days]], result.[[Hours]], result.[[Minutes]], // result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], // result.[[Nanoseconds]]) is false, then if (!IsValidDuration(isolate, result)) { // a. Throw a RangeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DurationRecord>()); } // 8. Return result. return Just(result); } // #sec-temporal-totemporalduration MaybeHandle<JSTemporalDuration> ToTemporalDuration(Isolate* isolate, Handle<Object> item, const char* method_name) { TEMPORAL_ENTER_FUNC(); DurationRecord result; // 1. If Type(item) is Object and item has an [[InitializedTemporalDuration]] // internal slot, then if (IsJSTemporalDuration(*item)) { // a. Return item. return Handle<JSTemporalDuration>::cast(item); } // 2. Let result be ? ToTemporalDurationRecord(item). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ToTemporalDurationRecord(isolate, item, method_name), Handle<JSTemporalDuration>()); // 3. Return ? CreateTemporalDuration(result.[[Years]], result.[[Months]], // result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], // result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], // result.[[Nanoseconds]]). return CreateTemporalDuration(isolate, result); } // #sec-temporal-totemporaltimezone MaybeHandle<JSReceiver> ToTemporalTimeZone( Isolate* isolate, Handle<Object> temporal_time_zone_like, const char* method_name) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 1. If Type(temporalTimeZoneLike) is Object, then if (IsJSReceiver(*temporal_time_zone_like)) { // a. If temporalTimeZoneLike has an [[InitializedTemporalZonedDateTime]] // internal slot, then if (IsJSTemporalZonedDateTime(*temporal_time_zone_like)) { // i. Return temporalTimeZoneLike.[[TimeZone]]. Handle<JSTemporalZonedDateTime> zoned_date_time = Handle<JSTemporalZonedDateTime>::cast(temporal_time_zone_like); return handle(zoned_date_time->time_zone(), isolate); } Handle<JSReceiver> obj = Handle<JSReceiver>::cast(temporal_time_zone_like); // b. If ? HasProperty(temporalTimeZoneLike, "timeZone") is false, bool has; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, has, JSReceiver::HasProperty(isolate, obj, factory->timeZone_string()), Handle<JSReceiver>()); if (!has) { // return temporalTimeZoneLike. return obj; } // c. Set temporalTimeZoneLike to ? // Get(temporalTimeZoneLike, "timeZone"). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_time_zone_like, JSReceiver::GetProperty(isolate, obj, factory->timeZone_string()), JSReceiver); // d. If Type(temporalTimeZoneLike) if (IsJSReceiver(*temporal_time_zone_like)) { // is Object and ? HasProperty(temporalTimeZoneLike, "timeZone") is false, obj = Handle<JSReceiver>::cast(temporal_time_zone_like); MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, has, JSReceiver::HasProperty(isolate, obj, factory->timeZone_string()), Handle<JSReceiver>()); if (!has) { // return temporalTimeZoneLike. return obj; } } } Handle<String> identifier; // 2. Let identifier be ? ToString(temporalTimeZoneLike). ASSIGN_RETURN_ON_EXCEPTION(isolate, identifier, Object::ToString(isolate, temporal_time_zone_like), JSReceiver); // 3. Let parseResult be ? ParseTemporalTimeZoneString(identifier). TimeZoneRecord parse_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, parse_result, ParseTemporalTimeZoneString(isolate, identifier), Handle<JSReceiver>()); // 4. If parseResult.[[Name]] is not undefined, then if (!IsUndefined(*parse_result.name)) { DCHECK(IsString(*parse_result.name)); // a. Let name be parseResult.[[Name]]. Handle<String> name = Handle<String>::cast(parse_result.name); // b. If ParseText(StringToCodePoints(name, TimeZoneNumericUTCOffset)) is // a List of errors, then base::Optional<ParsedISO8601Result> parsed_offset = TemporalParser::ParseTimeZoneNumericUTCOffset(isolate, name); if (!parsed_offset.has_value()) { // i. If ! IsValidTimeZoneName(name) is false, throw a RangeError // exception. if (!IsValidTimeZoneName(isolate, name)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSReceiver); } // ii. Set name to ! CanonicalizeTimeZoneName(name). name = CanonicalizeTimeZoneName(isolate, name); } // c. Return ! CreateTemporalTimeZone(name). return temporal::CreateTemporalTimeZone(isolate, name); } // 5. If parseResult.[[Z]] is true, return ! CreateTemporalTimeZone("UTC"). if (parse_result.z) { return CreateTemporalTimeZoneUTC(isolate); } // 6. Return ! CreateTemporalTimeZone(parseResult.[[OffsetString]]). DCHECK(IsString(*parse_result.offset_string)); return temporal::CreateTemporalTimeZone( isolate, Handle<String>::cast(parse_result.offset_string)); } } // namespace temporal namespace { // #sec-temporal-systemdatetime MaybeHandle<JSTemporalPlainDateTime> SystemDateTime( Isolate* isolate, Handle<Object> temporal_time_zone_like, Handle<Object> calendar_like, const char* method_name) { TEMPORAL_ENTER_FUNC(); Handle<JSReceiver> time_zone; // 1. 1. If temporalTimeZoneLike is undefined, then if (IsUndefined(*temporal_time_zone_like)) { // a. Let timeZone be ! SystemTimeZone(). time_zone = SystemTimeZone(isolate); } else { // 2. Else, // a. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone, temporal::ToTemporalTimeZone(isolate, temporal_time_zone_like, method_name), JSTemporalPlainDateTime); } Handle<JSReceiver> calendar; // 3. Let calendar be ? ToTemporalCalendar(calendarLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, temporal::ToTemporalCalendar(isolate, calendar_like, method_name), JSTemporalPlainDateTime); // 4. Let instant be ! SystemInstant(). Handle<JSTemporalInstant> instant = SystemInstant(isolate); // 5. Return ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, // calendar). return temporal::BuiltinTimeZoneGetPlainDateTimeFor( isolate, time_zone, instant, calendar, method_name); } MaybeHandle<JSTemporalZonedDateTime> SystemZonedDateTime( Isolate* isolate, Handle<Object> temporal_time_zone_like, Handle<Object> calendar_like, const char* method_name) { TEMPORAL_ENTER_FUNC(); Handle<JSReceiver> time_zone; // 1. 1. If temporalTimeZoneLike is undefined, then if (IsUndefined(*temporal_time_zone_like)) { // a. Let timeZone be ! SystemTimeZone(). time_zone = SystemTimeZone(isolate); } else { // 2. Else, // a. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone, temporal::ToTemporalTimeZone(isolate, temporal_time_zone_like, method_name), JSTemporalZonedDateTime); } Handle<JSReceiver> calendar; // 3. Let calendar be ? ToTemporalCalendar(calendarLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, temporal::ToTemporalCalendar(isolate, calendar_like, method_name), JSTemporalZonedDateTime); // 4. Let ns be ! SystemUTCEpochNanoseconds(). Handle<BigInt> ns = SystemUTCEpochNanoseconds(isolate); // Return ? CreateTemporalZonedDateTime(ns, timeZone, calendar). return CreateTemporalZonedDateTime(isolate, ns, time_zone, calendar); } int CompareResultToSign(ComparisonResult r) { DCHECK_NE(r, ComparisonResult::kUndefined); return static_cast<int>(r); } // #sec-temporal-formattimezoneoffsetstring Handle<String> FormatTimeZoneOffsetString(Isolate* isolate, int64_t offset_nanoseconds) { IncrementalStringBuilder builder(isolate); // 1. Assert: offsetNanoseconds is an integer. // 2. If offsetNanoseconds ≥ 0, let sign be "+"; otherwise, let sign be "-". builder.AppendCharacter((offset_nanoseconds >= 0) ? '+' : '-'); // 3. Let offsetNanoseconds be abs(offsetNanoseconds). offset_nanoseconds = std::abs(offset_nanoseconds); // 4. Let nanoseconds be offsetNanoseconds modulo 10^9. int64_t nanoseconds = offset_nanoseconds % 1000000000; // 5. Let seconds be floor(offsetNanoseconds / 10^9) modulo 60. int32_t seconds = (offset_nanoseconds / 1000000000) % 60; // 6. Let minutes be floor(offsetNanoseconds / (6 × 10^10)) modulo 60. int32_t minutes = (offset_nanoseconds / 60000000000) % 60; // 7. Let hours be floor(offsetNanoseconds / (3.6 × 10^12)). int32_t hours = offset_nanoseconds / 3600000000000; // 8. Let h be ToZeroPaddedDecimalString(hours, 2). ToZeroPaddedDecimalString(&builder, hours, 2); // 9. Let m be ToZeroPaddedDecimalString(minutes, 2). builder.AppendCharacter(':'); ToZeroPaddedDecimalString(&builder, minutes, 2); // 10. Let s be ToZeroPaddedDecimalString(seconds, 2). // 11. If nanoseconds ≠ 0, then if (nanoseconds != 0) { // a. Let fraction be ToZeroPaddedDecimalString(nanoseconds, 9). // b. Set fraction to the longest possible substring of fraction starting at // position 0 and not ending with the code unit 0x0030 (DIGIT ZERO). c. Let // post be the string-concatenation of the code unit 0x003A (COLON), s, the // code unit 0x002E (FULL STOP), and fraction. builder.AppendCharacter(':'); ToZeroPaddedDecimalString(&builder, seconds, 2); builder.AppendCharacter('.'); int64_t divisor = 100000000; do { builder.AppendInt(static_cast<int>(nanoseconds / divisor)); nanoseconds %= divisor; divisor /= 10; } while (nanoseconds > 0); // 11. Else if seconds ≠ 0, then } else if (seconds != 0) { // a. Let post be the string-concatenation of the code unit 0x003A (COLON) // and s. builder.AppendCharacter(':'); ToZeroPaddedDecimalString(&builder, seconds, 2); } // 12. Return the string-concatenation of sign, h, the code unit 0x003A // (COLON), m, and post. return builder.Finish().ToHandleChecked(); } double RoundNumberToIncrement(Isolate* isolate, double x, double increment, RoundingMode rounding_mode); // #sec-temporal-formatisotimezoneoffsetstring Handle<String> FormatISOTimeZoneOffsetString(Isolate* isolate, int64_t offset_nanoseconds) { IncrementalStringBuilder builder(isolate); // 1. Assert: offsetNanoseconds is an integer. // 2. Set offsetNanoseconds to ! RoundNumberToIncrement(offsetNanoseconds, 60 // × 10^9, "halfExpand"). offset_nanoseconds = RoundNumberToIncrement( isolate, offset_nanoseconds, 60000000000, RoundingMode::kHalfExpand); // 3. If offsetNanoseconds ≥ 0, let sign be "+"; otherwise, let sign be "-". builder.AppendCharacter((offset_nanoseconds >= 0) ? '+' : '-'); // 4. Set offsetNanoseconds to abs(offsetNanoseconds). offset_nanoseconds = std::abs(offset_nanoseconds); // 5. Let minutes be offsetNanoseconds / (60 × 10^9) modulo 60. int32_t minutes = (offset_nanoseconds / 60000000000) % 60; // 6. Let hours be floor(offsetNanoseconds / (3600 × 10^9)). int32_t hours = offset_nanoseconds / 3600000000000; // 7. Let h be ToZeroPaddedDecimalString(hours, 2). ToZeroPaddedDecimalString(&builder, hours, 2); // 8. Let m be ToZeroPaddedDecimalString(minutes, 2). builder.AppendCharacter(':'); ToZeroPaddedDecimalString(&builder, minutes, 2); // 9. Return the string-concatenation of sign, h, the code unit 0x003A // (COLON), and m. return builder.Finish().ToHandleChecked(); } int32_t DecimalLength(int32_t n) { int32_t i = 1; while (n >= 10) { n /= 10; i++; } return i; } // #sec-tozeropaddeddecimalstring void ToZeroPaddedDecimalString(IncrementalStringBuilder* builder, int32_t n, int32_t min_length) { for (int32_t pad = min_length - DecimalLength(n); pad > 0; pad--) { builder->AppendCharacter('0'); } builder->AppendInt(n); } // #sec-temporal-padisoyear void PadISOYear(IncrementalStringBuilder* builder, int32_t y) { // 1. Assert: y is an integer. // 2. If y ≥ 0 and y ≤ 9999, then if (y >= 0 && y <= 9999) { // a. Return ToZeroPaddedDecimalString(y, 4). ToZeroPaddedDecimalString(builder, y, 4); return; } // 3. If y > 0, let yearSign be "+"; otherwise, let yearSign be "-". if (y > 0) { builder->AppendCharacter('+'); } else { builder->AppendCharacter('-'); } // 4. Let year be ToZeroPaddedDecimalString(abs(y), 6). ToZeroPaddedDecimalString(builder, std::abs(y), 6); // 5. Return the string-concatenation of yearSign and year. } // #sec-temporal-formatcalendarannotation Handle<String> FormatCalendarAnnotation(Isolate* isolate, Handle<String> id, ShowCalendar show_calendar) { // 1.Assert: showCalendar is "auto", "always", or "never". // 2. If showCalendar is "never", return the empty String. if (show_calendar == ShowCalendar::kNever) { return isolate->factory()->empty_string(); } // 3. If showCalendar is "auto" and id is "iso8601", return the empty String. if (show_calendar == ShowCalendar::kAuto && String::Equals(isolate, id, isolate->factory()->iso8601_string())) { return isolate->factory()->empty_string(); } // 4. Return the string-concatenation of "[u-ca=", id, and "]". IncrementalStringBuilder builder(isolate); builder.AppendCStringLiteral("[u-ca="); builder.AppendString(id); builder.AppendCharacter(']'); return builder.Finish().ToHandleChecked(); } // #sec-temporal-maybeformatcalendarannotation MaybeHandle<String> MaybeFormatCalendarAnnotation( Isolate* isolate, Handle<JSReceiver> calendar_object, ShowCalendar show_calendar) { // 1. If showCalendar is "never", return the empty String. if (show_calendar == ShowCalendar::kNever) { return isolate->factory()->empty_string(); } // 2. Let calendarID be ? ToString(calendarObject). Handle<String> calendar_id; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar_id, Object::ToString(isolate, calendar_object), String); // 3. Return FormatCalendarAnnotation(calendarID, showCalendar). return FormatCalendarAnnotation(isolate, calendar_id, show_calendar); } // #sec-temporal-temporaldatetostring MaybeHandle<String> TemporalDateToString( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date, ShowCalendar show_calendar) { IncrementalStringBuilder builder(isolate); // 1. Assert: Type(temporalDate) is Object. // 2. Assert: temporalDate has an [[InitializedTemporalDate]] internal slot. // 3. Let year be ! PadISOYear(temporalDate.[[ISOYear]]). PadISOYear(&builder, temporal_date->iso_year()); // 4. Let month be ToZeroPaddedDecimalString(temporalDate.[[ISOMonth]], 2). builder.AppendCharacter('-'); ToZeroPaddedDecimalString(&builder, temporal_date->iso_month(), 2); // 5. Let day be ToZeroPaddedDecimalString(temporalDate.[[ISODay]], 2). builder.AppendCharacter('-'); ToZeroPaddedDecimalString(&builder, temporal_date->iso_day(), 2); // 6. Let calendar be ? // MaybeFormatCalendarAnnotation(temporalDate.[[Calendar]], showCalendar). Handle<String> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, MaybeFormatCalendarAnnotation( isolate, handle(temporal_date->calendar(), isolate), show_calendar), String); // 7. Return the string-concatenation of year, the code unit 0x002D // (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, and // calendar. builder.AppendString(calendar); return builder.Finish().ToHandleChecked(); } // #sec-temporal-temporalmonthdaytostring MaybeHandle<String> TemporalMonthDayToString( Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day, ShowCalendar show_calendar) { // 1. Assert: Type(monthDay) is Object. // 2. Assert: monthDay has an [[InitializedTemporalMonthDay]] internal slot. IncrementalStringBuilder builder(isolate); // 6. Let calendarID be ? ToString(monthDay.[[Calendar]]). Handle<String> calendar_id; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar_id, Object::ToString(isolate, handle(month_day->calendar(), isolate)), String); // 7. If showCalendar is "always" or if calendarID is not "iso8601", then if (show_calendar == ShowCalendar::kAlways || !String::Equals(isolate, calendar_id, isolate->factory()->iso8601_string())) { // a. Let year be ! PadISOYear(monthDay.[[ISOYear]]). PadISOYear(&builder, month_day->iso_year()); // b. Set result to the string-concatenation of year, the code unit // 0x002D (HYPHEN-MINUS), and result. builder.AppendCharacter('-'); } // 3. Let month be ToZeroPaddedDecimalString(monthDay.[[ISOMonth]], 2). ToZeroPaddedDecimalString(&builder, month_day->iso_month(), 2); // 5. Let result be the string-concatenation of month, the code unit 0x002D // (HYPHEN-MINUS), and day. builder.AppendCharacter('-'); // 4. Let day be ToZeroPaddedDecimalString(monthDay.[[ISODay]], 2). ToZeroPaddedDecimalString(&builder, month_day->iso_day(), 2); // 8. Let calendarString be ! FormatCalendarAnnotation(calendarID, // showCalendar). Handle<String> calendar_string = FormatCalendarAnnotation(isolate, calendar_id, show_calendar); // 9. Set result to the string-concatenation of result and calendarString. builder.AppendString(calendar_string); // 10. Return result. return builder.Finish().ToHandleChecked(); } // #sec-temporal-temporalyearmonthtostring MaybeHandle<String> TemporalYearMonthToString( Isolate* isolate, Handle<JSTemporalPlainYearMonth> year_month, ShowCalendar show_calendar) { // 1. Assert: Type(yearMonth) is Object. // 2. Assert: yearMonth has an [[InitializedTemporalYearMonth]] internal slot. IncrementalStringBuilder builder(isolate); // 3. Let year be ! PadISOYear(yearMonth.[[ISOYear]]). PadISOYear(&builder, year_month->iso_year()); // 4. Let month be ToZeroPaddedDecimalString(yearMonth.[[ISOMonth]], 2). // 5. Let result be the string-concatenation of year, the code unit 0x002D // (HYPHEN-MINUS), and month. builder.AppendCharacter('-'); ToZeroPaddedDecimalString(&builder, year_month->iso_month(), 2); // 6. Let calendarID be ? ToString(yearMonth.[[Calendar]]). Handle<String> calendar_id; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar_id, Object::ToString(isolate, handle(year_month->calendar(), isolate)), String); // 7. If showCalendar is "always" or if *_calendarID_ is not *"iso8601", then if (show_calendar == ShowCalendar::kAlways || !String::Equals(isolate, calendar_id, isolate->factory()->iso8601_string())) { // a. Let day be ToZeroPaddedDecimalString(yearMonth.[[ISODay]], 2). // b. Set result to the string-concatenation of result, the code unit 0x002D // (HYPHEN-MINUS), and day. builder.AppendCharacter('-'); ToZeroPaddedDecimalString(&builder, year_month->iso_day(), 2); } // 8. Let calendarString be ! FormatCalendarAnnotation(calendarID, // showCalendar). Handle<String> calendar_string = FormatCalendarAnnotation(isolate, calendar_id, show_calendar); // 9. Set result to the string-concatenation of result and calendarString. builder.AppendString(calendar_string); // 10. Return result. return builder.Finish().ToHandleChecked(); } // #sec-temporal-builtintimezonegetoffsetstringfor MaybeHandle<String> BuiltinTimeZoneGetOffsetStringFor( Isolate* isolate, Handle<JSReceiver> time_zone, Handle<JSTemporalInstant> instant, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant). int64_t offset_nanoseconds; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_nanoseconds, GetOffsetNanosecondsFor(isolate, time_zone, instant, method_name), Handle<String>()); // 2. Return ! FormatTimeZoneOffsetString(offsetNanoseconds). return FormatTimeZoneOffsetString(isolate, offset_nanoseconds); } // #sec-temporal-parseisodatetime Maybe<DateTimeRecordWithCalendar> ParseISODateTime( Isolate* isolate, Handle<String> iso_string, const ParsedISO8601Result& parsed); // Note: We split ParseISODateTime to two function because the spec text // repeates some parsing unnecessary. If a function is calling ParseISODateTime // from a AO which already call ParseText() for TemporalDateTimeString, // TemporalInstantString, TemporalMonthDayString, TemporalTimeString, // TemporalYearMonthString, TemporalZonedDateTimeString. But for the usage in // ParseTemporalTimeZoneString, we use the following version. Maybe<DateTimeRecordWithCalendar> ParseISODateTime(Isolate* isolate, Handle<String> iso_string) { // 2. For each nonterminal goal of « TemporalDateTimeString, // TemporalInstantString, TemporalMonthDayString, TemporalTimeString, // TemporalYearMonthString, TemporalZonedDateTimeString », do // a. If parseResult is not a Parse Node, set parseResult to // ParseText(StringToCodePoints(isoString), goal). base::Optional<ParsedISO8601Result> parsed; if ((parsed = TemporalParser::ParseTemporalDateTimeString(isolate, iso_string)) .has_value() || (parsed = TemporalParser::ParseTemporalInstantString(isolate, iso_string)) .has_value() || (parsed = TemporalParser::ParseTemporalMonthDayString(isolate, iso_string)) .has_value() || (parsed = TemporalParser::ParseTemporalTimeString(isolate, iso_string)) .has_value() || (parsed = TemporalParser::ParseTemporalYearMonthString(isolate, iso_string)) .has_value() || (parsed = TemporalParser::ParseTemporalZonedDateTimeString(isolate, iso_string)) .has_value()) { return ParseISODateTime(isolate, iso_string, *parsed); } // 3. If parseResult is not a Parse Node, throw a RangeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateTimeRecordWithCalendar>()); } Maybe<DateTimeRecordWithCalendar> ParseISODateTime( Isolate* isolate, Handle<String> iso_string, const ParsedISO8601Result& parsed) { TEMPORAL_ENTER_FUNC(); DateTimeRecordWithCalendar result; // 6. Set yearMV to ! ToIntegerOrInfinity(year). result.date.year = parsed.date_year; // 7. If month is undefined, then if (parsed.date_month_is_undefined()) { // a. Set monthMV to 1. result.date.month = 1; // 8. Else, } else { // a. Set monthMV to ! ToIntegerOrInfinity(month). result.date.month = parsed.date_month; } // 9. If day is undefined, then if (parsed.date_day_is_undefined()) { // a. Set dayMV to 1. result.date.day = 1; // 10. Else, } else { // a. Set dayMV to ! ToIntegerOrInfinity(day). result.date.day = parsed.date_day; } // 11. Set hourMV to ! ToIntegerOrInfinity(hour). result.time.hour = parsed.time_hour_is_undefined() ? 0 : parsed.time_hour; // 12. Set minuteMV to ! ToIntegerOrInfinity(minute). result.time.minute = parsed.time_minute_is_undefined() ? 0 : parsed.time_minute; // 13. Set secondMV to ! ToIntegerOrInfinity(second). result.time.second = parsed.time_second_is_undefined() ? 0 : parsed.time_second; // 14. If secondMV is 60, then if (result.time.second == 60) { // a. Set secondMV to 59. result.time.second = 59; } // 15. If fSeconds is not empty, then if (!parsed.time_nanosecond_is_undefined()) { // a. Let fSecondsDigits be the substring of CodePointsToString(fSeconds) // from 1. // // b. Let fSecondsDigitsExtended be the string-concatenation of // fSecondsDigits and "000000000". // // c. Let millisecond be the substring of fSecondsDigitsExtended from 0 to // 3. // // d. Let microsecond be the substring of fSecondsDigitsExtended from 3 to // 6. // // e. Let nanosecond be the substring of fSecondsDigitsExtended from 6 to 9. // // f. Let millisecondMV be ! ToIntegerOrInfinity(millisecond). result.time.millisecond = parsed.time_nanosecond / 1000000; // g. Let microsecondMV be ! ToIntegerOrInfinity(microsecond). result.time.microsecond = (parsed.time_nanosecond / 1000) % 1000; // h. Let nanosecondMV be ! ToIntegerOrInfinity(nanosecond). result.time.nanosecond = (parsed.time_nanosecond % 1000); // 16. Else, } else { // a. Let millisecondMV be 0. result.time.millisecond = 0; // b. Let microsecondMV be 0. result.time.microsecond = 0; // c. Let nanosecondMV be 0. result.time.nanosecond = 0; } // 17. If ! IsValidISODate(yearMV, monthMV, dayMV) is false, throw a // RangeError exception. if (!IsValidISODate(isolate, result.date)) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateTimeRecordWithCalendar>()); } // 18. If ! IsValidTime(hourMV, minuteMV, secondMV, millisecondMV, // microsecondMV, nanosecond) is false, throw a RangeError exception. if (!IsValidTime(isolate, result.time)) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateTimeRecordWithCalendar>()); } // 19. Let timeZoneResult be the Record { [[Z]]: false, [[OffsetString]]: // undefined, [[Name]]: undefined }. result.time_zone = {false, isolate->factory()->undefined_value(), isolate->factory()->undefined_value()}; // 20. If parseResult contains a TimeZoneIdentifier Parse Node, then if (parsed.tzi_name_length != 0) { // a. Let name be the source text matched by the TimeZoneIdentifier Parse // Node contained within parseResult. // // b. Set timeZoneResult.[[Name]] to CodePointsToString(name). result.time_zone.name = isolate->factory()->NewSubString( iso_string, parsed.tzi_name_start, parsed.tzi_name_start + parsed.tzi_name_length); } // 21. If parseResult contains a UTCDesignator Parse Node, then if (parsed.utc_designator) { // a. Set timeZoneResult.[[Z]] to true. result.time_zone.z = true; // 22. Else, } else { // a. If parseResult contains a TimeZoneNumericUTCOffset Parse Node, then if (parsed.offset_string_length != 0) { // i. Let offset be the source text matched by the // TimeZoneNumericUTCOffset Parse Node contained within parseResult. // ii. Set timeZoneResult.[[OffsetString]] to CodePointsToString(offset). result.time_zone.offset_string = isolate->factory()->NewSubString( iso_string, parsed.offset_string_start, parsed.offset_string_start + parsed.offset_string_length); } } // 23. If calendar is empty, then if (parsed.calendar_name_length == 0) { // a. Let calendarVal be undefined. result.calendar = isolate->factory()->undefined_value(); // 24. Else, } else { // a. Let calendarVal be CodePointsToString(calendar). result.calendar = isolate->factory()->NewSubString( iso_string, parsed.calendar_name_start, parsed.calendar_name_start + parsed.calendar_name_length); } // 24. Return the Record { [[Year]]: yearMV, [[Month]]: monthMV, [[Day]]: // dayMV, [[Hour]]: hourMV, [[Minute]]: minuteMV, [[Second]]: secondMV, // [[Millisecond]]: millisecondMV, [[Microsecond]]: microsecondMV, // [[Nanosecond]]: nanosecondMV, [[TimeZone]]: timeZoneResult, // [[Calendar]]: calendarVal, }. return Just(result); } // #sec-temporal-parsetemporaldatestring Maybe<DateRecordWithCalendar> ParseTemporalDateString( Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); // 1. Let parts be ? ParseTemporalDateTimeString(isoString). // 2. Return the Record { [[Year]]: parts.[[Year]], [[Month]]: // parts.[[Month]], [[Day]]: parts.[[Day]], [[Calendar]]: parts.[[Calendar]] // }. DateTimeRecordWithCalendar record; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, record, ParseTemporalDateTimeString(isolate, iso_string), Nothing<DateRecordWithCalendar>()); DateRecordWithCalendar result = {record.date, record.calendar}; return Just(result); } // #sec-temporal-parsetemporaltimestring Maybe<TimeRecordWithCalendar> ParseTemporalTimeString( Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); // 1. Assert: Type(isoString) is String. // 2. If isoString does not satisfy the syntax of a TemporalTimeString // (see 13.33), then base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseTemporalTimeString(isolate, iso_string); if (!parsed.has_value()) { // a. Throw a *RangeError* exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<TimeRecordWithCalendar>()); } // 3. If _isoString_ contains a |UTCDesignator|, then if (parsed->utc_designator) { // a. Throw a *RangeError* exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<TimeRecordWithCalendar>()); } // 3. Let result be ? ParseISODateTime(isoString). DateTimeRecordWithCalendar result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ParseISODateTime(isolate, iso_string, *parsed), Nothing<TimeRecordWithCalendar>()); // 4. Return the Record { [[Hour]]: result.[[Hour]], [[Minute]]: // result.[[Minute]], [[Second]]: result.[[Second]], [[Millisecond]]: // result.[[Millisecond]], [[Microsecond]]: result.[[Microsecond]], // [[Nanosecond]]: result.[[Nanosecond]], [[Calendar]]: result.[[Calendar]] }. TimeRecordWithCalendar ret = {result.time, result.calendar}; return Just(ret); } // #sec-temporal-parsetemporalinstantstring Maybe<InstantRecord> ParseTemporalInstantString(Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); // 1. If ParseText(StringToCodePoints(isoString), TemporalInstantString) is a // List of errors, throw a RangeError exception. base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseTemporalInstantString(isolate, iso_string); if (!parsed.has_value()) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<InstantRecord>()); } // 2. Let result be ? ParseISODateTime(isoString). DateTimeRecordWithCalendar result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ParseISODateTime(isolate, iso_string, *parsed), Nothing<InstantRecord>()); // 3. Let offsetString be result.[[TimeZone]].[[OffsetString]]. Handle<Object> offset_string = result.time_zone.offset_string; // 4. If result.[[TimeZone]].[[Z]] is true, then if (result.time_zone.z) { // a. Set offsetString to "+00:00". offset_string = isolate->factory()->NewStringFromStaticChars("+00:00"); } // 5. Assert: offsetString is not undefined. DCHECK(!IsUndefined(*offset_string)); // 6. Return the new Record { [[Year]]: result.[[Year]], // [[Month]]: result.[[Month]], [[Day]]: result.[[Day]], // [[Hour]]: result.[[Hour]], [[Minute]]: result.[[Minute]], // [[Second]]: result.[[Second]], // [[Millisecond]]: result.[[Millisecond]], // [[Microsecond]]: result.[[Microsecond]], // [[Nanosecond]]: result.[[Nanosecond]], // [[TimeZoneOffsetString]]: offsetString }. InstantRecord record({result.date, result.time, offset_string}); return Just(record); } // #sec-temporal-parsetemporalrelativetostring Maybe<DateTimeRecordWithCalendar> ParseTemporalRelativeToString( Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); // 1. If ParseText(StringToCodePoints(isoString), TemporalDateTimeString) is a // List of errors, throw a RangeError exception. base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseTemporalDateTimeString(isolate, iso_string); if (!parsed.has_value()) { // a. Throw a *RangeError* exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateTimeRecordWithCalendar>()); } // 2. Returns ? ParseISODateTime(isoString). return ParseISODateTime(isolate, iso_string, *parsed); } // #sec-temporal-parsetemporalinstant MaybeHandle<BigInt> ParseTemporalInstant(Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); // 1. Assert: Type(isoString) is String. // 2. Let result be ? ParseTemporalInstantString(isoString). InstantRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ParseTemporalInstantString(isolate, iso_string), Handle<BigInt>()); // 3. Let offsetString be result.[[TimeZoneOffsetString]]. // 4. Assert: offsetString is not undefined. DCHECK(!IsUndefined(*result.offset_string)); // 5. Let utc be ? GetEpochFromISOParts(result.[[Year]], result.[[Month]], // result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], // result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]). Handle<BigInt> utc = GetEpochFromISOParts(isolate, {result.date, result.time}); // 6. Let offsetNanoseconds be ? ParseTimeZoneOffsetString(offsetString). int64_t offset_nanoseconds; DCHECK(IsString(*result.offset_string)); MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_nanoseconds, ParseTimeZoneOffsetString(isolate, Handle<String>::cast(result.offset_string)), Handle<BigInt>()); // 7. Let result be utc - ℤ(offsetNanoseconds). Handle<BigInt> result_value = BigInt::Subtract(isolate, utc, BigInt::FromInt64(isolate, offset_nanoseconds)) .ToHandleChecked(); // 8. If ! IsValidEpochNanoseconds(result) is false, then if (!IsValidEpochNanoseconds(isolate, result_value)) { // a. Throw a RangeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), BigInt); } // 9. Return result. return result_value; } // #sec-temporal-parsetemporalzoneddatetimestring Maybe<DateTimeRecordWithCalendar> ParseTemporalZonedDateTimeString( Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); // 1. If ParseText(StringToCodePoints(isoString), TemporalZonedDateTimeString) // is a List of errors, throw a RangeError exception. base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseTemporalZonedDateTimeString(isolate, iso_string); if (!parsed.has_value()) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateTimeRecordWithCalendar>()); } // 2. Return ? ParseISODateTime(isoString). return ParseISODateTime(isolate, iso_string, *parsed); } // #sec-temporal-createdurationrecord Maybe<DurationRecord> CreateDurationRecord(Isolate* isolate, const DurationRecord& duration) { // 1. If ! IsValidDuration(years, months, weeks, days, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds) is false, throw a // RangeError exception. if (!IsValidDuration(isolate, duration)) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DurationRecord>()); } // 2. Return the Record { [[Years]]: ℝ(𝔽(years)), [[Months]]: ℝ(𝔽(months)), // [[Weeks]]: ℝ(𝔽(weeks)), [[Days]]: ℝ(𝔽(days)), [[Hours]]: ℝ(𝔽(hours)), // [[Minutes]]: ℝ(𝔽(minutes)), [[Seconds]]: ℝ(𝔽(seconds)), [[Milliseconds]]: // ℝ(𝔽(milliseconds)), [[Microseconds]]: ℝ(𝔽(microseconds)), [[Nanoseconds]]: // ℝ(𝔽(nanoseconds)) }. return Just(duration); } inline double IfEmptyReturnZero(double value) { return value == ParsedISO8601Duration::kEmpty ? 0 : value; } // #sec-temporal-parsetemporaldurationstring Maybe<DurationRecord> ParseTemporalDurationString(Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); // In this funciton, we use 'double' as type for all mathematical values // because in // https://tc39.es/proposal-temporal/#sec-properties-of-temporal-duration-instances // they are "A float64-representable integer representing the number" in the // internal slots. // 1. Let duration be ParseText(StringToCodePoints(isoString), // TemporalDurationString). // 2. If duration is a List of errors, throw a RangeError exception. // 3. Let each of sign, years, months, weeks, days, hours, fHours, minutes, // fMinutes, seconds, and fSeconds be the source text matched by the // respective Sign, DurationYears, DurationMonths, DurationWeeks, // DurationDays, DurationWholeHours, DurationHoursFraction, // DurationWholeMinutes, DurationMinutesFraction, DurationWholeSeconds, and // DurationSecondsFraction Parse Node enclosed by duration, or an empty // sequence of code points if not present. base::Optional<ParsedISO8601Duration> parsed = TemporalParser::ParseTemporalDurationString(isolate, iso_string); if (!parsed.has_value()) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DurationRecord>()); } // 4. Let yearsMV be ! ToIntegerOrInfinity(CodePointsToString(years)). double years_mv = IfEmptyReturnZero(parsed->years); // 5. Let monthsMV be ! ToIntegerOrInfinity(CodePointsToString(months)). double months_mv = IfEmptyReturnZero(parsed->months); // 6. Let weeksMV be ! ToIntegerOrInfinity(CodePointsToString(weeks)). double weeks_mv = IfEmptyReturnZero(parsed->weeks); // 7. Let daysMV be ! ToIntegerOrInfinity(CodePointsToString(days)). double days_mv = IfEmptyReturnZero(parsed->days); // 8. Let hoursMV be ! ToIntegerOrInfinity(CodePointsToString(hours)). double hours_mv = IfEmptyReturnZero(parsed->whole_hours); // 9. If fHours is not empty, then double minutes_mv; if (parsed->hours_fraction != ParsedISO8601Duration::kEmpty) { // a. If any of minutes, fMinutes, seconds, fSeconds is not empty, throw a // RangeError exception. if (parsed->whole_minutes != ParsedISO8601Duration::kEmpty || parsed->minutes_fraction != ParsedISO8601Duration::kEmpty || parsed->whole_seconds != ParsedISO8601Duration::kEmpty || parsed->seconds_fraction != ParsedISO8601Duration::kEmpty) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DurationRecord>()); } // b. Let fHoursDigits be the substring of CodePointsToString(fHours) // from 1. // // c. Let fHoursScale be the length of fHoursDigits. // // d. Let // minutesMV be ! ToIntegerOrInfinity(fHoursDigits) / 10^fHoursScale × 60. minutes_mv = IfEmptyReturnZero(parsed->hours_fraction) * 60.0 / 1e9; // 10. Else, } else { // a. Let minutesMV be ! ToIntegerOrInfinity(CodePointsToString(minutes)). minutes_mv = IfEmptyReturnZero(parsed->whole_minutes); } double seconds_mv; // 11. If fMinutes is not empty, then if (parsed->minutes_fraction != ParsedISO8601Duration::kEmpty) { // a. If any of seconds, fSeconds is not empty, throw a RangeError // exception. if (parsed->whole_seconds != ParsedISO8601Duration::kEmpty || parsed->seconds_fraction != ParsedISO8601Duration::kEmpty) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DurationRecord>()); } // b. Let fMinutesDigits be the substring of CodePointsToString(fMinutes) // from 1. // // c. Let fMinutesScale be the length of fMinutesDigits. // // d. Let secondsMV be ! ToIntegerOrInfinity(fMinutesDigits) / // 10^fMinutesScale × 60. seconds_mv = IfEmptyReturnZero(parsed->minutes_fraction) * 60.0 / 1e9; // 12. Else if seconds is not empty, then } else if (parsed->whole_seconds != ParsedISO8601Duration::kEmpty) { // a. Let secondsMV be ! ToIntegerOrInfinity(CodePointsToString(seconds)). seconds_mv = parsed->whole_seconds; // 13. Else, } else { // a. Let secondsMV be remainder(minutesMV, 1) × 60. seconds_mv = (minutes_mv - std::floor(minutes_mv)) * 60.0; } double milliseconds_mv, microseconds_mv, nanoseconds_mv; // Note: In step 14-17, we calculate from nanoseconds_mv to miilliseconds_mv // in the reversee order of the spec text to avoid numerical errors would be // introduced by multiple division inside the remainder operations. If we // strickly follow the order by using double, the end result of nanoseconds_mv // will be wrong due to numerical errors. // // 14. If fSeconds is not empty, then if (parsed->seconds_fraction != ParsedISO8601Duration::kEmpty) { // a. Let fSecondsDigits be the substring of CodePointsToString(fSeconds) // from 1. // // b. Let fSecondsScale be the length of fSecondsDigits. // // c. Let millisecondsMV be ! ToIntegerOrInfinity(fSecondsDigits) / // 10^fSecondsScale × 1000. DCHECK_LE(IfEmptyReturnZero(parsed->seconds_fraction), 1e9); nanoseconds_mv = std::round(IfEmptyReturnZero(parsed->seconds_fraction)); // 15. Else, } else { // a. Let millisecondsMV be remainder(secondsMV, 1) × 1000. nanoseconds_mv = std::round((seconds_mv - std::floor(seconds_mv)) * 1e9); } milliseconds_mv = std::floor(nanoseconds_mv / 1000000); // 16. Let microsecondsMV be remainder(millisecondsMV, 1) × 1000. microseconds_mv = std::floor(nanoseconds_mv / 1000) - std::floor(nanoseconds_mv / 1000000) * 1000; // 17. Let nanosecondsMV be remainder(microsecondsMV, 1) × 1000. nanoseconds_mv -= std::floor(nanoseconds_mv / 1000) * 1000; // 18. If sign contains the code point 0x002D (HYPHEN-MINUS) or 0x2212 (MINUS // SIGN), then a. Let factor be −1. // 19. Else, // a. Let factor be 1. double factor = parsed->sign; // 20. Return ? CreateDurationRecord(yearsMV × factor, monthsMV × factor, // weeksMV × factor, daysMV × factor, hoursMV × factor, floor(minutesMV) × // factor, floor(secondsMV) × factor, floor(millisecondsMV) × factor, // floor(microsecondsMV) × factor, floor(nanosecondsMV) × factor). return CreateDurationRecord( isolate, {years_mv * factor, months_mv * factor, weeks_mv * factor, {days_mv * factor, hours_mv * factor, std::floor(minutes_mv) * factor, std::floor(seconds_mv) * factor, milliseconds_mv * factor, microseconds_mv * factor, nanoseconds_mv * factor}}); } // #sec-temporal-parsetemporaltimezonestring Maybe<TimeZoneRecord> ParseTemporalTimeZoneString( Isolate* isolate, Handle<String> time_zone_string) { TEMPORAL_ENTER_FUNC(); // 1. Let parseResult be ParseText(StringToCodePoints(timeZoneString), // TimeZoneIdentifier). base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseTimeZoneIdentifier(isolate, time_zone_string); // 2. If parseResult is a Parse Node, then if (parsed.has_value()) { // a. Return the Record { [[Z]]: false, [[OffsetString]]: undefined, // [[Name]]: timeZoneString }. return Just(TimeZoneRecord( {false, isolate->factory()->undefined_value(), time_zone_string})); } // 3. Let result be ? ParseISODateTime(timeZoneString). DateTimeRecordWithCalendar result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ParseISODateTime(isolate, time_zone_string), Nothing<TimeZoneRecord>()); // 4. Let timeZoneResult be result.[[TimeZone]]. // 5. If timeZoneResult.[[Z]] is false, timeZoneResult.[[OffsetString]] is // undefined, and timeZoneResult.[[Name]] is undefined, throw a RangeError // exception. if (!result.time_zone.z && IsUndefined(*result.time_zone.offset_string) && IsUndefined(*result.time_zone.name)) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<TimeZoneRecord>()); } // 6. Return timeZoneResult. return Just(result.time_zone); } Maybe<int64_t> ParseTimeZoneOffsetString(Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); // 1. Assert: Type(offsetString) is String. // 2. If offsetString does not satisfy the syntax of a // TimeZoneNumericUTCOffset (see 13.33), then base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseTimeZoneNumericUTCOffset(isolate, iso_string); if (!parsed.has_value()) { /* a. Throw a RangeError exception. */ THROW_NEW_ERROR_RETURN_VALUE( isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<int64_t>()); } // 3. Let sign, hours, minutes, seconds, and fraction be the parts of // offsetString produced respectively by the TimeZoneUTCOffsetSign, // TimeZoneUTCOffsetHour, TimeZoneUTCOffsetMinute, TimeZoneUTCOffsetSecond, // and TimeZoneUTCOffsetFraction productions, or undefined if not present. // 4. If either hours or sign are undefined, throw a RangeError exception. if (parsed->tzuo_hour_is_undefined() || parsed->tzuo_sign_is_undefined()) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<int64_t>()); } // 5. If sign is the code unit 0x002D (HYPHEN-MINUS) or 0x2212 (MINUS SIGN), // then a. Set sign to −1. // 6. Else, // a. Set sign to 1. int64_t sign = parsed->tzuo_sign; // 7. Set hours to ! ToIntegerOrInfinity(hours). int64_t hours = parsed->tzuo_hour; // 8. Set minutes to ! ToIntegerOrInfinity(minutes). int64_t minutes = parsed->tzuo_minute_is_undefined() ? 0 : parsed->tzuo_minute; // 9. Set seconds to ! ToIntegerOrInfinity(seconds). int64_t seconds = parsed->tzuo_second_is_undefined() ? 0 : parsed->tzuo_second; // 10. If fraction is not undefined, then int64_t nanoseconds; if (!parsed->tzuo_nanosecond_is_undefined()) { // a. Set fraction to the string-concatenation of the previous value of // fraction and the string "000000000". // b. Let nanoseconds be the String value equal to the substring of fraction // consisting of the code units with indices 0 (inclusive) through 9 // (exclusive). c. Set nanoseconds to ! ToIntegerOrInfinity(nanoseconds). nanoseconds = parsed->tzuo_nanosecond; // 11. Else, } else { // a. Let nanoseconds be 0. nanoseconds = 0; } // 12. Return sign × (((hours × 60 + minutes) × 60 + seconds) × 10^9 + // nanoseconds). return Just(sign * (((hours * 60 + minutes) * 60 + seconds) * 1000000000 + nanoseconds)); } bool IsValidTimeZoneNumericUTCOffsetString(Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseTimeZoneNumericUTCOffset(isolate, iso_string); return parsed.has_value(); } // #sec-temporal-parsetemporalcalendarstring MaybeHandle<String> ParseTemporalCalendarString(Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); // 1. Let parseResult be Completion(ParseISODateTime(isoString)). Maybe<DateTimeRecordWithCalendar> parse_result = ParseISODateTime(isolate, iso_string); // 2. If parseResult is a normal completion, then if (parse_result.IsJust()) { // a. Let calendar be parseResult.[[Value]].[[Calendar]]. Handle<Object> calendar = parse_result.FromJust().calendar; // b. If calendar is undefined, return "iso8601". if (IsUndefined(*calendar)) { return isolate->factory()->iso8601_string(); // c. Else, return calendar. } else { CHECK(IsString(*calendar)); return Handle<String>::cast(calendar); } // 3. Else, } else { DCHECK(isolate->has_pending_exception()); isolate->clear_pending_exception(); // a. Set parseResult to ParseText(StringToCodePoints(isoString), // CalendarName). base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseCalendarName(isolate, iso_string); // b. If parseResult is a List of errors, throw a RangeError exception. if (!parsed.has_value()) { THROW_NEW_ERROR( isolate, NewRangeError(MessageTemplate::kInvalidCalendar, iso_string), String); } // c. Else, return isoString. return iso_string; } } // #sec-temporal-calendarequals Maybe<bool> CalendarEqualsBool(Isolate* isolate, Handle<JSReceiver> one, Handle<JSReceiver> two) { // 1. If one and two are the same Object value, return true. if (one.is_identical_to(two)) { return Just(true); } // 2. Let calendarOne be ? ToString(one). Handle<String> calendar_one; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, calendar_one, Object::ToString(isolate, one), Nothing<bool>()); // 3. Let calendarTwo be ? ToString(two). Handle<String> calendar_two; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, calendar_two, Object::ToString(isolate, two), Nothing<bool>()); // 4. If calendarOne is calendarTwo, return true. if (String::Equals(isolate, calendar_one, calendar_two)) { return Just(true); } // 5. Return false. return Just(false); } MaybeHandle<Oddball> CalendarEquals(Isolate* isolate, Handle<JSReceiver> one, Handle<JSReceiver> two) { bool result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, result, CalendarEqualsBool(isolate, one, two), Handle<Oddball>()); return isolate->factory()->ToBoolean(result); } // #sec-temporal-calendarfields MaybeHandle<FixedArray> CalendarFields(Isolate* isolate, Handle<JSReceiver> calendar, Handle<FixedArray> field_names) { // 1. Let fields be ? GetMethod(calendar, "fields"). Handle<Object> fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, Object::GetMethod(isolate, calendar, isolate->factory()->fields_string()), FixedArray); // 2. Let fieldsArray be ! CreateArrayFromList(fieldNames). Handle<Object> fields_array = isolate->factory()->NewJSArrayWithElements(field_names); // 3. If fields is not undefined, then if (!IsUndefined(*fields)) { // a. Set fieldsArray to ? Call(fields, calendar, « fieldsArray »). Handle<Object> argv[] = {fields_array}; ASSIGN_RETURN_ON_EXCEPTION( isolate, fields_array, Execution::Call(isolate, fields, calendar, 1, argv), FixedArray); } // 4. Return ? IterableToListOfType(fieldsArray, « String »). Handle<Object> argv[] = {fields_array}; ASSIGN_RETURN_ON_EXCEPTION( isolate, fields_array, Execution::CallBuiltin(isolate, isolate->string_fixed_array_from_iterable(), fields_array, 1, argv), FixedArray); DCHECK(IsFixedArray(*fields_array)); return Handle<FixedArray>::cast(fields_array); } MaybeHandle<JSTemporalPlainDate> CalendarDateAdd(Isolate* isolate, Handle<JSReceiver> calendar, Handle<Object> date, Handle<Object> duration) { // 2. If options is not present, set options to undefined. return CalendarDateAdd(isolate, calendar, date, duration, isolate->factory()->undefined_value()); } MaybeHandle<JSTemporalPlainDate> CalendarDateAdd(Isolate* isolate, Handle<JSReceiver> calendar, Handle<Object> date, Handle<Object> duration, Handle<Object> options) { Handle<Object> date_add; // 4. If dateAdd is not present, set dateAdd to ? GetMethod(calendar, ASSIGN_RETURN_ON_EXCEPTION( isolate, date_add, Object::GetMethod(isolate, calendar, isolate->factory()->dateAdd_string()), JSTemporalPlainDate); return CalendarDateAdd(isolate, calendar, date, duration, options, date_add); } MaybeHandle<JSTemporalPlainDate> CalendarDateAdd( Isolate* isolate, Handle<JSReceiver> calendar, Handle<Object> date, Handle<Object> duration, Handle<Object> options, Handle<Object> date_add) { // 1. Assert: Type(options) is Object or Undefined. DCHECK(IsJSReceiver(*options) || IsUndefined(*options)); // 3. Let addedDate be ? Call(dateAdd, calendar, « date, duration, options »). Handle<Object> argv[] = {date, duration, options}; Handle<Object> added_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, added_date, Execution::Call(isolate, date_add, calendar, arraysize(argv), argv), JSTemporalPlainDate); // 4. Perform ? RequireInternalSlot(addedDate, [[InitializedTemporalDate]]). if (!IsJSTemporalPlainDate(*added_date)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalPlainDate); } // 5. Return addedDate. return Handle<JSTemporalPlainDate>::cast(added_date); } MaybeHandle<JSTemporalDuration> CalendarDateUntil(Isolate* isolate, Handle<JSReceiver> calendar, Handle<Object> one, Handle<Object> two, Handle<Object> options) { return CalendarDateUntil(isolate, calendar, one, two, options, isolate->factory()->undefined_value()); } MaybeHandle<JSTemporalDuration> CalendarDateUntil( Isolate* isolate, Handle<JSReceiver> calendar, Handle<Object> one, Handle<Object> two, Handle<Object> options, Handle<Object> date_until) { // 1. Assert: Type(calendar) is Object. // 2. If dateUntil is not present, set dateUntil to ? GetMethod(calendar, // "dateUntil"). if (IsUndefined(*date_until)) { ASSIGN_RETURN_ON_EXCEPTION( isolate, date_until, Object::GetMethod(isolate, calendar, isolate->factory()->dateUntil_string()), JSTemporalDuration); } // 3. Let duration be ? Call(dateUntil, calendar, « one, two, options »). Handle<Object> argv[] = {one, two, options}; Handle<Object> duration; ASSIGN_RETURN_ON_EXCEPTION( isolate, duration, Execution::Call(isolate, date_until, calendar, arraysize(argv), argv), JSTemporalDuration); // 4. Perform ? RequireInternalSlot(duration, // [[InitializedTemporalDuration]]). if (!IsJSTemporalDuration(*duration)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalDuration); } // 5. Return duration. return Handle<JSTemporalDuration>::cast(duration); } // #sec-temporal-defaultmergefields MaybeHandle<JSReceiver> DefaultMergeFields( Isolate* isolate, Handle<JSReceiver> fields, Handle<JSReceiver> additional_fields) { Factory* factory = isolate->factory(); // 1. Let merged be ! OrdinaryObjectCreate(%Object.prototype%). Handle<JSObject> merged = isolate->factory()->NewJSObject(isolate->object_function()); // 2. Let originalKeys be ? EnumerableOwnPropertyNames(fields, key). Handle<FixedArray> original_keys; ASSIGN_RETURN_ON_EXCEPTION( isolate, original_keys, KeyAccumulator::GetKeys(isolate, fields, KeyCollectionMode::kOwnOnly, ENUMERABLE_STRINGS, GetKeysConversion::kConvertToString), JSReceiver); // 3. For each element nextKey of originalKeys, do for (int i = 0; i < original_keys->length(); i++) { // a. If nextKey is not "month" or "monthCode", then Handle<Object> next_key(original_keys->get(i), isolate); DCHECK(IsString(*next_key)); Handle<String> next_key_string = Handle<String>::cast(next_key); if (!(String::Equals(isolate, factory->month_string(), next_key_string) || String::Equals(isolate, factory->monthCode_string(), next_key_string))) { // i. Let propValue be ? Get(fields, nextKey). Handle<Object> prop_value; ASSIGN_RETURN_ON_EXCEPTION( isolate, prop_value, Object::GetPropertyOrElement(isolate, fields, next_key_string), JSReceiver); // ii. If propValue is not undefined, then if (!IsUndefined(*prop_value)) { // 1. Perform ! CreateDataPropertyOrThrow(merged, nextKey, // propValue). CHECK(JSReceiver::CreateDataProperty(isolate, merged, next_key_string, prop_value, Just(kDontThrow)) .FromJust()); } } } // 4. Let newKeys be ? EnumerableOwnPropertyNames(additionalFields, key). Handle<FixedArray> new_keys; ASSIGN_RETURN_ON_EXCEPTION( isolate, new_keys, KeyAccumulator::GetKeys(isolate, additional_fields, KeyCollectionMode::kOwnOnly, ENUMERABLE_STRINGS, GetKeysConversion::kConvertToString), JSReceiver); bool new_keys_has_month_or_month_code = false; // 5. For each element nextKey of newKeys, do for (int i = 0; i < new_keys->length(); i++) { Handle<Object> next_key(new_keys->get(i), isolate); DCHECK(IsString(*next_key)); Handle<String> next_key_string = Handle<String>::cast(next_key); // a. Let propValue be ? Get(additionalFields, nextKey). Handle<Object> prop_value; ASSIGN_RETURN_ON_EXCEPTION(isolate, prop_value, Object::GetPropertyOrElement( isolate, additional_fields, next_key_string), JSReceiver); // b. If propValue is not undefined, then if (!IsUndefined(*prop_value)) { // 1. Perform ! CreateDataPropertyOrThrow(merged, nextKey, propValue). CHECK(JSReceiver::CreateDataProperty(isolate, merged, next_key_string, prop_value, Just(kDontThrow)) .FromJust()); } new_keys_has_month_or_month_code |= String::Equals(isolate, factory->month_string(), next_key_string) || String::Equals(isolate, factory->monthCode_string(), next_key_string); } // 6. If newKeys does not contain either "month" or "monthCode", then if (!new_keys_has_month_or_month_code) { // a. Let month be ? Get(fields, "month"). Handle<Object> month; ASSIGN_RETURN_ON_EXCEPTION( isolate, month, JSReceiver::GetProperty(isolate, fields, factory->month_string()), JSReceiver); // b. If month is not undefined, then if (!IsUndefined(*month)) { // i. Perform ! CreateDataPropertyOrThrow(merged, "month", month). CHECK(JSReceiver::CreateDataProperty(isolate, merged, factory->month_string(), month, Just(kDontThrow)) .FromJust()); } // c. Let monthCode be ? Get(fields, "monthCode"). Handle<Object> month_code; ASSIGN_RETURN_ON_EXCEPTION( isolate, month_code, JSReceiver::GetProperty(isolate, fields, factory->monthCode_string()), JSReceiver); // d. If monthCode is not undefined, then if (!IsUndefined(*month_code)) { // i. Perform ! CreateDataPropertyOrThrow(merged, "monthCode", monthCode). CHECK(JSReceiver::CreateDataProperty(isolate, merged, factory->monthCode_string(), month_code, Just(kDontThrow)) .FromJust()); } } // 7. Return merged. return merged; } // #sec-temporal-getoffsetnanosecondsfor Maybe<int64_t> GetOffsetNanosecondsFor(Isolate* isolate, Handle<JSReceiver> time_zone_obj, Handle<Object> instant, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. Let getOffsetNanosecondsFor be ? GetMethod(timeZone, // "getOffsetNanosecondsFor"). Handle<Object> get_offset_nanoseconds_for; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, get_offset_nanoseconds_for, Object::GetMethod(isolate, time_zone_obj, isolate->factory()->getOffsetNanosecondsFor_string()), Nothing<int64_t>()); if (!IsCallable(*get_offset_nanoseconds_for)) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewTypeError(MessageTemplate::kCalledNonCallable, isolate->factory()->getOffsetNanosecondsFor_string()), Nothing<int64_t>()); } Handle<Object> offset_nanoseconds_obj; // 3. Let offsetNanoseconds be ? Call(getOffsetNanosecondsFor, timeZone, « // instant »). Handle<Object> argv[] = {instant}; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_nanoseconds_obj, Execution::Call(isolate, get_offset_nanoseconds_for, time_zone_obj, 1, argv), Nothing<int64_t>()); // 4. If Type(offsetNanoseconds) is not Number, throw a TypeError exception. if (!IsNumber(*offset_nanoseconds_obj)) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Nothing<int64_t>()); } // 5. If ! IsIntegralNumber(offsetNanoseconds) is false, throw a RangeError // exception. if (!IsIntegralNumber(isolate, offset_nanoseconds_obj)) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<int64_t>()); } double offset_nanoseconds = Object::Number(*offset_nanoseconds_obj); // 6. Set offsetNanoseconds to ℝ(offsetNanoseconds). int64_t offset_nanoseconds_int = static_cast<int64_t>(offset_nanoseconds); // 7. If abs(offsetNanoseconds) >= 86400 × 10^9, throw a RangeError exception. if (std::abs(offset_nanoseconds_int) >= 86400e9) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<int64_t>()); } // 8. Return offsetNanoseconds. return Just(offset_nanoseconds_int); } // #sec-temporal-topositiveinteger MaybeHandle<Object> ToPositiveInteger(Isolate* isolate, Handle<Object> argument) { TEMPORAL_ENTER_FUNC(); // 1. Let integer be ? ToInteger(argument). ASSIGN_RETURN_ON_EXCEPTION( isolate, argument, ToIntegerThrowOnInfinity(isolate, argument), Object); // 2. If integer ≤ 0, then if (NumberToInt32(*argument) <= 0) { // a. Throw a RangeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Object); } return argument; } } // namespace namespace temporal { MaybeHandle<Object> InvokeCalendarMethod(Isolate* isolate, Handle<JSReceiver> calendar, Handle<String> name, Handle<JSReceiver> date_like) { Handle<Object> result; /* 1. Assert: Type(calendar) is Object. */ DCHECK(calendar->TaggedImpl::IsObject()); /* 2. Let result be ? Invoke(calendar, #name, « dateLike »). */ Handle<Object> function; ASSIGN_RETURN_ON_EXCEPTION( isolate, function, Object::GetProperty(isolate, calendar, name), Object); if (!IsCallable(*function)) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCalledNonCallable, name), Object); } Handle<Object> argv[] = {date_like}; ASSIGN_RETURN_ON_EXCEPTION( isolate, result, Execution::Call(isolate, function, calendar, arraysize(argv), argv), Object); return result; } #define CALENDAR_ABSTRACT_OPERATION_INT_ACTION(Name, name, Action) \ MaybeHandle<Object> Calendar##Name(Isolate* isolate, \ Handle<JSReceiver> calendar, \ Handle<JSReceiver> date_like) { \ /* 1. Assert: Type(calendar) is Object. */ \ /* 2. Let result be ? Invoke(calendar, property, « dateLike »). */ \ Handle<Object> result; \ ASSIGN_RETURN_ON_EXCEPTION( \ isolate, result, \ InvokeCalendarMethod(isolate, calendar, \ isolate->factory()->name##_string(), date_like), \ Object); \ /* 3. If result is undefined, throw a RangeError exception. */ \ if (IsUndefined(*result)) { \ THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), \ Object); \ } \ /* 4. Return ? Action(result). */ \ ASSIGN_RETURN_ON_EXCEPTION(isolate, result, Action(isolate, result), \ Object); \ return handle(Smi::FromInt(Object::Number(*result)), isolate); \ } #define CALENDAR_ABSTRACT_OPERATION(Name, property) \ MaybeHandle<Object> Calendar##Name(Isolate* isolate, \ Handle<JSReceiver> calendar, \ Handle<JSReceiver> date_like) { \ return InvokeCalendarMethod(isolate, calendar, \ isolate->factory()->property##_string(), \ date_like); \ } // #sec-temporal-calendaryear CALENDAR_ABSTRACT_OPERATION_INT_ACTION(Year, year, ToIntegerThrowOnInfinity) // #sec-temporal-calendarmonth CALENDAR_ABSTRACT_OPERATION_INT_ACTION(Month, month, ToPositiveInteger) // #sec-temporal-calendarday CALENDAR_ABSTRACT_OPERATION_INT_ACTION(Day, day, ToPositiveInteger) // #sec-temporal-calendarmonthcode MaybeHandle<Object> CalendarMonthCode(Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSReceiver> date_like) { // 1. Assert: Type(calendar) is Object. // 2. Let result be ? Invoke(calendar, monthCode , « dateLike »). Handle<Object> result; ASSIGN_RETURN_ON_EXCEPTION( isolate, result, InvokeCalendarMethod(isolate, calendar, isolate->factory()->monthCode_string(), date_like), Object); /* 3. If result is undefined, throw a RangeError exception. */ if (IsUndefined(*result)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Object); } // 4. Return ? ToString(result). return Object::ToString(isolate, result); } #ifdef V8_INTL_SUPPORT // #sec-temporal-calendarerayear MaybeHandle<Object> CalendarEraYear(Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSReceiver> date_like) { // 1. Assert: Type(calendar) is Object. // 2. Let result be ? Invoke(calendar, eraYear , « dateLike »). Handle<Object> result; ASSIGN_RETURN_ON_EXCEPTION( isolate, result, InvokeCalendarMethod(isolate, calendar, isolate->factory()->eraYear_string(), date_like), Object); // 3. If result is not undefined, set result to ? ToIntegerOrInfinity(result). if (!IsUndefined(*result)) { ASSIGN_RETURN_ON_EXCEPTION( isolate, result, ToIntegerThrowOnInfinity(isolate, result), Object); } // 4. Return result. return result; } // #sec-temporal-calendarera MaybeHandle<Object> CalendarEra(Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSReceiver> date_like) { // 1. Assert: Type(calendar) is Object. // 2. Let result be ? Invoke(calendar, era , « dateLike »). Handle<Object> result; ASSIGN_RETURN_ON_EXCEPTION( isolate, result, InvokeCalendarMethod(isolate, calendar, isolate->factory()->era_string(), date_like), Object); // 3. If result is not undefined, set result to ? ToString(result). if (!IsUndefined(*result)) { ASSIGN_RETURN_ON_EXCEPTION(isolate, result, Object::ToString(isolate, result), Object); } // 4. Return result. return result; } #endif // V8_INTL_SUPPORT // #sec-temporal-calendardayofweek CALENDAR_ABSTRACT_OPERATION(DayOfWeek, dayOfWeek) // #sec-temporal-calendardayofyear CALENDAR_ABSTRACT_OPERATION(DayOfYear, dayOfYear) // #sec-temporal-calendarweekofyear CALENDAR_ABSTRACT_OPERATION(WeekOfYear, weekOfYear) // #sec-temporal-calendardaysinweek CALENDAR_ABSTRACT_OPERATION(DaysInWeek, daysInWeek) // #sec-temporal-calendardaysinmonth CALENDAR_ABSTRACT_OPERATION(DaysInMonth, daysInMonth) // #sec-temporal-calendardaysinyear CALENDAR_ABSTRACT_OPERATION(DaysInYear, daysInYear) // #sec-temporal-calendarmonthsinyear CALENDAR_ABSTRACT_OPERATION(MonthsInYear, monthsInYear) // #sec-temporal-calendarinleapyear CALENDAR_ABSTRACT_OPERATION(InLeapYear, inLeapYear) // #sec-temporal-getiso8601calendar Handle<JSTemporalCalendar> GetISO8601Calendar(Isolate* isolate) { return CreateTemporalCalendar(isolate, isolate->factory()->iso8601_string()) .ToHandleChecked(); } } // namespace temporal namespace { bool IsUTC(Isolate* isolate, Handle<String> time_zone) { // 1. Assert: Type(timeZone) is String. // 2. Let tzText be ! StringToCodePoints(timeZone). // 3. Let tzUpperText be the result of toUppercase(tzText), according to the // Unicode Default Case Conversion algorithm. // 4. Let tzUpper be ! CodePointsToString(tzUpperText). // 5. If tzUpper and "UTC" are the same sequence of code points, return true. // 6. Return false. if (time_zone->length() != 3) return false; time_zone = String::Flatten(isolate, time_zone); DisallowGarbageCollection no_gc; const String::FlatContent& flat = time_zone->GetFlatContent(no_gc); return (flat.Get(0) == u'U' || flat.Get(0) == u'u') && (flat.Get(1) == u'T' || flat.Get(1) == u't') && (flat.Get(2) == u'C' || flat.Get(2) == u'c'); } #ifdef V8_INTL_SUPPORT class CalendarMap final { public: CalendarMap() { icu::Locale locale("und"); UErrorCode status = U_ZERO_ERROR; std::unique_ptr<icu::StringEnumeration> enumeration( icu::Calendar::getKeywordValuesForLocale("ca", locale, false, status)); calendar_ids.push_back("iso8601"); calendar_id_indices.insert({"iso8601", 0}); int32_t i = 1; for (const char* item = enumeration->next(nullptr, status); U_SUCCESS(status) && item != nullptr; item = enumeration->next(nullptr, status)) { if (strcmp(item, "iso8601") != 0) { const char* type = uloc_toUnicodeLocaleType("ca", item); calendar_ids.push_back(type); calendar_id_indices.insert({type, i++}); } } } bool Contains(const std::string& id) const { return calendar_id_indices.find(id) != calendar_id_indices.end(); } std::string Id(int32_t index) const { DCHECK_LT(index, calendar_ids.size()); return calendar_ids[index]; } int32_t Index(const char* id) const { return calendar_id_indices.find(id)->second; } private: std::map<std::string, int32_t> calendar_id_indices; std::vector<std::string> calendar_ids; }; DEFINE_LAZY_LEAKY_OBJECT_GETTER(CalendarMap, GetCalendarMap) bool IsBuiltinCalendar(Isolate* isolate, Handle<String> id) { // 1. Let calendars be AvailableCalendars(). // 2. If calendars contains the ASCII-lowercase of id, return true. // 3. Return false. id = Intl::ConvertToLower(isolate, String::Flatten(isolate, id)) .ToHandleChecked(); return GetCalendarMap()->Contains(id->ToCString().get()); } Handle<String> CalendarIdentifier(Isolate* isolate, int32_t index) { return isolate->factory()->NewStringFromAsciiChecked( GetCalendarMap()->Id(index).c_str()); } int32_t CalendarIndex(Isolate* isolate, Handle<String> id) { id = Intl::ConvertToLower(isolate, String::Flatten(isolate, id)) .ToHandleChecked(); return GetCalendarMap()->Index(id->ToCString().get()); } bool IsValidTimeZoneName(Isolate* isolate, Handle<String> time_zone) { return Intl::IsValidTimeZoneName(isolate, time_zone); } Handle<String> CanonicalizeTimeZoneName(Isolate* isolate, Handle<String> identifier) { return Intl::CanonicalizeTimeZoneName(isolate, identifier).ToHandleChecked(); } #else // V8_INTL_SUPPORT Handle<String> CalendarIdentifier(Isolate* isolate, int32_t index) { DCHECK_EQ(index, 0); return isolate->factory()->iso8601_string(); } // #sec-temporal-isbuiltincalendar bool IsBuiltinCalendar(Isolate* isolate, Handle<String> id) { // Note: For build without intl support, the only item in AvailableCalendars() // is "iso8601". // 1. Let calendars be AvailableCalendars(). // 2. If calendars contains the ASCII-lowercase of id, return true. // 3. Return false. // Fast path if (isolate->factory()->iso8601_string()->Equals(*id)) return true; if (id->length() != 7) return false; id = String::Flatten(isolate, id); DisallowGarbageCollection no_gc; const String::FlatContent& flat = id->GetFlatContent(no_gc); // Return true if id is case insensitive equals to "iso8601". return AsciiAlphaToLower(flat.Get(0)) == 'i' && AsciiAlphaToLower(flat.Get(1)) == 's' && AsciiAlphaToLower(flat.Get(2)) == 'o' && flat.Get(3) == '8' && flat.Get(4) == '6' && flat.Get(5) == '0' && flat.Get(6) == '1'; } int32_t CalendarIndex(Isolate* isolate, Handle<String> id) { return 0; } // #sec-isvalidtimezonename bool IsValidTimeZoneName(Isolate* isolate, Handle<String> time_zone) { return IsUTC(isolate, time_zone); } // #sec-canonicalizetimezonename Handle<String> CanonicalizeTimeZoneName(Isolate* isolate, Handle<String> identifier) { return isolate->factory()->UTC_string(); } #endif // V8_INTL_SUPPORT // Common routine shared by ToTemporalTimeRecord and ToPartialTime // #sec-temporal-topartialtime // #sec-temporal-totemporaltimerecord Maybe<TimeRecord> ToTemporalTimeRecordOrPartialTime( Isolate* isolate, Handle<JSReceiver> temporal_time_like, const TimeRecord& time, bool skip_undefined, const char* method_name) { TEMPORAL_ENTER_FUNC(); TimeRecord result(time); Factory* factory = isolate->factory(); // 1. Assert: Type(temporalTimeLike) is Object. // 2. Let result be the new Record { [[Hour]]: undefined, [[Minute]]: // undefined, [[Second]]: undefined, [[Millisecond]]: undefined, // [[Microsecond]]: undefined, [[Nanosecond]]: undefined }. // See https://github.com/tc39/proposal-temporal/pull/1862 // 3. Let _any_ be *false*. bool any = false; // 4. For each row of Table 4, except the header row, in table order, do std::array<std::pair<Handle<String>, int32_t*>, 6> table4 = { {{factory->hour_string(), &result.hour}, {factory->microsecond_string(), &result.microsecond}, {factory->millisecond_string(), &result.millisecond}, {factory->minute_string(), &result.minute}, {factory->nanosecond_string(), &result.nanosecond}, {factory->second_string(), &result.second}}}; for (const auto& row : table4) { Handle<Object> value; // a. Let property be the Property value of the current row. // b. Let value be ? Get(temporalTimeLike, property). ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, value, JSReceiver::GetProperty(isolate, temporal_time_like, row.first), Nothing<TimeRecord>()); // c. If value is not undefined, then if (!IsUndefined(*value)) { // i. Set _any_ to *true*. any = true; // If it is inside ToPartialTime, we only continue if it is not undefined. } else if (skip_undefined) { continue; } // d. / ii. Set value to ? ToIntegerThrowOnOInfinity(value). ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, ToIntegerThrowOnInfinity(isolate, value), Nothing<TimeRecord>()); // e. / iii. Set result's internal slot whose name is the Internal Slot // value of the current row to value. *(row.second) = Object::Number(*value); } // 5. If _any_ is *false*, then if (!any) { // a. Throw a *TypeError* exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Nothing<TimeRecord>()); } // 4. Return result. return Just(result); } // #sec-temporal-topartialtime Maybe<TimeRecord> ToPartialTime(Isolate* isolate, Handle<JSReceiver> temporal_time_like, const TimeRecord& time, const char* method_name) { return ToTemporalTimeRecordOrPartialTime(isolate, temporal_time_like, time, true, method_name); } // #sec-temporal-totemporaltimerecord Maybe<TimeRecord> ToTemporalTimeRecord(Isolate* isolate, Handle<JSReceiver> temporal_time_like, const char* method_name) { return ToTemporalTimeRecordOrPartialTime( isolate, temporal_time_like, {kMinInt31, kMinInt31, kMinInt31, kMinInt31, kMinInt31, kMinInt31}, false, method_name); } // #sec-temporal-gettemporalunit // In the spec text, the extraValues is defined as an optional argument of // "a List of ECMAScript language values". Most of the caller does not pass in // value for extraValues, which is represented by the default Unit::kNotPresent. // For the three places in the spec text calling GetTemporalUnit with // an extraValues argument: // << "day" >> is passed in as in the algorithm of // Temporal.PlainDateTime.prototype.round() and // Temporal.ZonedDateTime.prototype.round(); // << "auto" >> is passed in as in the algorithm of // Temporal.Duration.prototype.round(). // Therefore we can simply use a Unit of three possible value, the default // Unit::kNotPresent, Unit::kDay, and Unit::kAuto to cover all the possible // value for extraValues. Maybe<Unit> GetTemporalUnit(Isolate* isolate, Handle<JSReceiver> normalized_options, const char* key, UnitGroup unit_group, Unit default_value, bool default_is_required, const char* method_name, Unit extra_values = Unit::kNotPresent) { std::vector<const char*> str_values; std::vector<Unit> enum_values; switch (unit_group) { case UnitGroup::kDate: if (default_value == Unit::kAuto || extra_values == Unit::kAuto) { str_values = {"year", "month", "week", "day", "auto", "years", "months", "weeks", "days"}; enum_values = {Unit::kYear, Unit::kMonth, Unit::kWeek, Unit::kDay, Unit::kAuto, Unit::kYear, Unit::kMonth, Unit::kWeek, Unit::kDay}; } else { DCHECK(default_value == Unit::kNotPresent || default_value == Unit::kYear || default_value == Unit::kMonth || default_value == Unit::kWeek || default_value == Unit::kDay); str_values = {"year", "month", "week", "day", "years", "months", "weeks", "days"}; enum_values = {Unit::kYear, Unit::kMonth, Unit::kWeek, Unit::kDay, Unit::kYear, Unit::kMonth, Unit::kWeek, Unit::kDay}; } break; case UnitGroup::kTime: if (default_value == Unit::kAuto || extra_values == Unit::kAuto) { str_values = {"hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "auto", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"}; enum_values = { Unit::kHour, Unit::kMinute, Unit::kSecond, Unit::kMillisecond, Unit::kMicrosecond, Unit::kNanosecond, Unit::kAuto, Unit::kHour, Unit::kMinute, Unit::kSecond, Unit::kMillisecond, Unit::kMicrosecond, Unit::kNanosecond}; } else if (default_value == Unit::kDay || extra_values == Unit::kDay) { str_values = {"hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "day", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds", "days"}; enum_values = { Unit::kHour, Unit::kMinute, Unit::kSecond, Unit::kMillisecond, Unit::kMicrosecond, Unit::kNanosecond, Unit::kDay, Unit::kHour, Unit::kMinute, Unit::kSecond, Unit::kMillisecond, Unit::kMicrosecond, Unit::kNanosecond, Unit::kDay}; } else { DCHECK(default_value == Unit::kNotPresent || default_value == Unit::kHour || default_value == Unit::kMinute || default_value == Unit::kSecond || default_value == Unit::kMillisecond || default_value == Unit::kMicrosecond || default_value == Unit::kNanosecond); str_values = {"hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"}; enum_values = { Unit::kHour, Unit::kMinute, Unit::kSecond, Unit::kMillisecond, Unit::kMicrosecond, Unit::kNanosecond, Unit::kHour, Unit::kMinute, Unit::kSecond, Unit::kMillisecond, Unit::kMicrosecond, Unit::kNanosecond}; } break; case UnitGroup::kDateTime: if (default_value == Unit::kAuto || extra_values == Unit::kAuto) { str_values = {"year", "month", "week", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "auto", "years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"}; enum_values = { Unit::kYear, Unit::kMonth, Unit::kWeek, Unit::kDay, Unit::kHour, Unit::kMinute, Unit::kSecond, Unit::kMillisecond, Unit::kMicrosecond, Unit::kNanosecond, Unit::kAuto, Unit::kYear, Unit::kMonth, Unit::kWeek, Unit::kDay, Unit::kHour, Unit::kMinute, Unit::kSecond, Unit::kMillisecond, Unit::kMicrosecond, Unit::kNanosecond}; } else { str_values = { "year", "month", "week", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"}; enum_values = { Unit::kYear, Unit::kMonth, Unit::kWeek, Unit::kDay, Unit::kHour, Unit::kMinute, Unit::kSecond, Unit::kMillisecond, Unit::kMicrosecond, Unit::kNanosecond, Unit::kYear, Unit::kMonth, Unit::kWeek, Unit::kDay, Unit::kHour, Unit::kMinute, Unit::kSecond, Unit::kMillisecond, Unit::kMicrosecond, Unit::kNanosecond}; } break; } // 4. If default is required, then if (default_is_required) default_value = Unit::kNotPresent; // a. Let defaultValue be undefined. // 5. Else, // a. Let defaultValue be default. // b. If defaultValue is not undefined and singularNames does not contain // defaultValue, then i. Append defaultValue to singularNames. // 9. Let value be ? GetOption(normalizedOptions, key, "string", // allowedValues, defaultValue). Unit value; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, value, GetStringOption<Unit>(isolate, normalized_options, key, method_name, str_values, enum_values, default_value), Nothing<Unit>()); // 10. If value is undefined and default is required, throw a RangeError // exception. if (default_is_required && value == Unit::kNotPresent) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError( MessageTemplate::kValueOutOfRange, isolate->factory()->undefined_value(), isolate->factory()->NewStringFromAsciiChecked(method_name), isolate->factory()->NewStringFromAsciiChecked(key)), Nothing<Unit>()); } // 12. Return value. return Just(value); } // #sec-temporal-mergelargestunitoption MaybeHandle<JSReceiver> MergeLargestUnitOption(Isolate* isolate, Handle<JSReceiver> options, Unit largest_unit) { TEMPORAL_ENTER_FUNC(); // 1. Let merged be OrdinaryObjectCreate(null). Handle<JSReceiver> merged = isolate->factory()->NewJSObjectWithNullProto(); // 2. Let keys be ? EnumerableOwnPropertyNames(options, key). // 3. For each element nextKey of keys, do // a. Let propValue be ? Get(options, nextKey). // b. Perform ! CreateDataPropertyOrThrow(merged, nextKey, propValue). JSReceiver::SetOrCopyDataProperties( isolate, merged, options, PropertiesEnumerationMode::kEnumerationOrder, nullptr, false) .Check(); // 4. Perform ! CreateDataPropertyOrThrow(merged, "largestUnit", largestUnit). CHECK(JSReceiver::CreateDataProperty( isolate, merged, isolate->factory()->largestUnit_string(), UnitToString(isolate, largest_unit), Just(kThrowOnError)) .FromJust()); // 5. Return merged. return merged; } // #sec-temporal-tointegerthrowoninfinity MaybeHandle<Object> ToIntegerThrowOnInfinity(Isolate* isolate, Handle<Object> argument) { TEMPORAL_ENTER_FUNC(); // 1. Let integer be ? ToIntegerOrInfinity(argument). ASSIGN_RETURN_ON_EXCEPTION(isolate, argument, Object::ToInteger(isolate, argument), Object); // 2. If integer is +∞ or -∞, throw a RangeError exception. if (!std::isfinite(Object::Number(*argument))) { // a. Throw a RangeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Object); } return argument; } // #sec-temporal-largeroftwotemporalunits Unit LargerOfTwoTemporalUnits(Unit u1, Unit u2) { // 1. If either u1 or u2 is "year", return "year". if (u1 == Unit::kYear || u2 == Unit::kYear) return Unit::kYear; // 2. If either u1 or u2 is "month", return "month". if (u1 == Unit::kMonth || u2 == Unit::kMonth) return Unit::kMonth; // 3. If either u1 or u2 is "week", return "week". if (u1 == Unit::kWeek || u2 == Unit::kWeek) return Unit::kWeek; // 4. If either u1 or u2 is "day", return "day". if (u1 == Unit::kDay || u2 == Unit::kDay) return Unit::kDay; // 5. If either u1 or u2 is "hour", return "hour". if (u1 == Unit::kHour || u2 == Unit::kHour) return Unit::kHour; // 6. If either u1 or u2 is "minute", return "minute". if (u1 == Unit::kMinute || u2 == Unit::kMinute) return Unit::kMinute; // 7. If either u1 or u2 is "second", return "second". if (u1 == Unit::kSecond || u2 == Unit::kSecond) return Unit::kSecond; // 8. If either u1 or u2 is "millisecond", return "millisecond". if (u1 == Unit::kMillisecond || u2 == Unit::kMillisecond) return Unit::kMillisecond; // 9. If either u1 or u2 is "microsecond", return "microsecond". if (u1 == Unit::kMicrosecond || u2 == Unit::kMicrosecond) return Unit::kMicrosecond; // 10. Return "nanosecond". return Unit::kNanosecond; } Handle<String> UnitToString(Isolate* isolate, Unit unit) { switch (unit) { case Unit::kYear: return ReadOnlyRoots(isolate).year_string_handle(); case Unit::kMonth: return ReadOnlyRoots(isolate).month_string_handle(); case Unit::kWeek: return ReadOnlyRoots(isolate).week_string_handle(); case Unit::kDay: return ReadOnlyRoots(isolate).day_string_handle(); case Unit::kHour: return ReadOnlyRoots(isolate).hour_string_handle(); case Unit::kMinute: return ReadOnlyRoots(isolate).minute_string_handle(); case Unit::kSecond: return ReadOnlyRoots(isolate).second_string_handle(); case Unit::kMillisecond: return ReadOnlyRoots(isolate).millisecond_string_handle(); case Unit::kMicrosecond: return ReadOnlyRoots(isolate).microsecond_string_handle(); case Unit::kNanosecond: return ReadOnlyRoots(isolate).nanosecond_string_handle(); case Unit::kNotPresent: case Unit::kAuto: UNREACHABLE(); } } // #sec-temporal-create-iso-date-record DateRecord CreateISODateRecord(Isolate* isolate, const DateRecord& date) { // 1. Assert: IsValidISODate(year, month, day) is true. DCHECK(IsValidISODate(isolate, date)); // 2. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day }. return date; } // #sec-temporal-balanceisodate DateRecord BalanceISODate(Isolate* isolate, const DateRecord& date) { TEMPORAL_ENTER_FUNC(); // 1. Let epochDays be MakeDay(𝔽(year), 𝔽(month - 1), 𝔽(day)). double epoch_days = MakeDay(date.year, date.month - 1, date.day); // 2. Assert: epochDays is finite. DCHECK(std::isfinite(epoch_days)); // 3. Let ms be MakeDate(epochDays, +0𝔽). double ms = MakeDate(epoch_days, 0); // 4. Return CreateISODateRecordWithCalendar(ℝ(YearFromTime(ms)), // ℝ(MonthFromTime(ms)) + 1, ℝ(DateFromTime(ms))). int year = 0; int month = 0; int day = 0; int wday = 0; int hour = 0; int minute = 0; int second = 0; int millisecond = 0; DCHECK(std::isfinite(ms)); DCHECK_LT(ms, static_cast<double>(std::numeric_limits<int64_t>::max())); DCHECK_GT(ms, static_cast<double>(std::numeric_limits<int64_t>::min())); isolate->date_cache()->BreakDownTime(ms, &year, &month, &day, &wday, &hour, &minute, &second, &millisecond); return CreateISODateRecord(isolate, {year, month + 1, day}); } // #sec-temporal-adddatetime Maybe<DateTimeRecord> AddDateTime(Isolate* isolate, const DateTimeRecord& date_time, Handle<JSReceiver> calendar, const DurationRecord& dur, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); // 1. Assert: ISODateTimeWithinLimits(year, month, day, hour, minute, second, // millisecond, microsecond, nanosecond) is true. DCHECK(ISODateTimeWithinLimits(isolate, date_time)); // 2. Let timeResult be ! AddTime(hour, minute, second, millisecond, // microsecond, nanosecond, hours, minutes, seconds, milliseconds, // microseconds, nanoseconds). const TimeDurationRecord& time = dur.time_duration; DateTimeRecord time_result = AddTime(isolate, date_time.time, {0, time.hours, time.minutes, time.seconds, time.milliseconds, time.microseconds, time.nanoseconds}); // 3. Let datePart be ? CreateTemporalDate(year, month, day, calendar). Handle<JSTemporalPlainDate> date_part; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_part, CreateTemporalDate(isolate, date_time.date, calendar), Nothing<DateTimeRecord>()); // 4. Let dateDuration be ? CreateTemporalDuration(years, months, weeks, days // + timeResult.[[Days]], 0, 0, 0, 0, 0, 0). Handle<JSTemporalDuration> date_duration; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_duration, CreateTemporalDuration( isolate, {dur.years, dur.months, dur.weeks, {dur.time_duration.days + time_result.date.day, 0, 0, 0, 0, 0, 0}}), Nothing<DateTimeRecord>()); // 5. Let addedDate be ? CalendarDateAdd(calendar, datePart, dateDuration, // options). Handle<JSTemporalPlainDate> added_date; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, added_date, CalendarDateAdd(isolate, calendar, date_part, date_duration, options), Nothing<DateTimeRecord>()); // 6. Return the new Record { [[Year]]: addedDate.[[ISOYear]], [[Month]]: // addedDate.[[ISOMonth]], [[Day]]: addedDate.[[ISODay]], [[Hour]]: // timeResult.[[Hour]], [[Minute]]: timeResult.[[Minute]], [[Second]]: // timeResult.[[Second]], [[Millisecond]]: timeResult.[[Millisecond]], // [[Microsecond]]: timeResult.[[Microsecond]], [[Nanosecond]]: // timeResult.[[Nanosecond]], }. time_result.date = {added_date->iso_year(), added_date->iso_month(), added_date->iso_day()}; return Just(time_result); } // #sec-temporal-balanceduration Maybe<TimeDurationRecord> BalanceDuration(Isolate* isolate, Unit largest_unit, const TimeDurationRecord& duration, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If relativeTo is not present, set relativeTo to undefined. return BalanceDuration(isolate, largest_unit, isolate->factory()->undefined_value(), duration, method_name); } Maybe<TimeDurationRecord> BalanceDuration(Isolate* isolate, Unit largest_unit, Handle<BigInt> nanoseconds, const char* method_name) { // 1. Let balanceResult be ? BalancePossiblyInfiniteDuration(days, hours, // minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit, // relativeTo). BalancePossiblyInfiniteDurationResult balance_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, balance_result, BalancePossiblyInfiniteDuration(isolate, largest_unit, 0, nanoseconds, method_name), Nothing<TimeDurationRecord>()); // 2. If balanceResult is positive overflow or negative overflow, then if (balance_result.overflow != BalanceOverflow::kNone) { // a. Throw a RangeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<TimeDurationRecord>()); // 3. Else, } else { // a. Return balanceResult. return Just(balance_result.value); } } Maybe<TimeDurationRecord> BalanceDuration(Isolate* isolate, Unit largest_unit, const TimeDurationRecord& dur1, const TimeDurationRecord& dur2, const char* method_name) { // Add the two TimeDurationRecord as BigInt in nanoseconds. Handle<BigInt> nanoseconds = BigInt::Add(isolate, TotalDurationNanoseconds(isolate, dur1, 0), TotalDurationNanoseconds(isolate, dur2, 0)) .ToHandleChecked(); return BalanceDuration(isolate, largest_unit, nanoseconds, method_name); } // #sec-temporal-balanceduration Maybe<TimeDurationRecord> BalanceDuration(Isolate* isolate, Unit largest_unit, Handle<Object> relative_to_obj, const TimeDurationRecord& value, const char* method_name) { // 1. Let balanceResult be ? BalancePossiblyInfiniteDuration(days, hours, // minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit, // relativeTo). BalancePossiblyInfiniteDurationResult balance_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, balance_result, BalancePossiblyInfiniteDuration(isolate, largest_unit, relative_to_obj, value, method_name), Nothing<TimeDurationRecord>()); // 2. If balanceResult is positive overflow or negative overflow, then if (balance_result.overflow != BalanceOverflow::kNone) { // a. Throw a RangeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<TimeDurationRecord>()); // 3. Else, } else { // a. Return balanceResult. return Just(balance_result.value); } } // sec-temporal-balancepossiblyinfiniteduration Maybe<BalancePossiblyInfiniteDurationResult> BalancePossiblyInfiniteDuration( Isolate* isolate, Unit largest_unit, Handle<Object> relative_to_obj, const TimeDurationRecord& value, const char* method_name) { TEMPORAL_ENTER_FUNC(); TimeDurationRecord duration = value; Handle<BigInt> nanoseconds; // 2. If Type(relativeTo) is Object and relativeTo has an // [[InitializedTemporalZonedDateTime]] internal slot, then if (IsJSTemporalZonedDateTime(*relative_to_obj)) { Handle<JSTemporalZonedDateTime> relative_to = Handle<JSTemporalZonedDateTime>::cast(relative_to_obj); // a. Let endNs be ? AddZonedDateTime(relativeTo.[[Nanoseconds]], // relativeTo.[[TimeZone]], relativeTo.[[Calendar]], 0, 0, 0, days, hours, // minutes, seconds, milliseconds, microseconds, nanoseconds). Handle<BigInt> end_ns; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, end_ns, AddZonedDateTime(isolate, handle(relative_to->nanoseconds(), isolate), handle(relative_to->time_zone(), isolate), handle(relative_to->calendar(), isolate), {0, 0, 0, duration}, method_name), Nothing<BalancePossiblyInfiniteDurationResult>()); // b. Set nanoseconds to endNs − relativeTo.[[Nanoseconds]]. ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, nanoseconds, BigInt::Subtract(isolate, end_ns, handle(relative_to->nanoseconds(), isolate)), Nothing<BalancePossiblyInfiniteDurationResult>()); // 3. Else, } else { // a. Set nanoseconds to ℤ(! TotalDurationNanoseconds(days, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds, 0)). nanoseconds = TotalDurationNanoseconds(isolate, duration, 0); } // Call the BigInt version for the same process after step 4 // The only value need to pass in is nanoseconds and days because // 1) step 4 and 5 use nanoseconds and days only, and // 2) step 6 is "Set hours, minutes, seconds, milliseconds, and microseconds // to 0." return BalancePossiblyInfiniteDuration(isolate, largest_unit, relative_to_obj, duration.days, nanoseconds, method_name); } // The special case of BalancePossiblyInfiniteDuration while the nanosecond is a // large value and days contains non-zero values but the rest are 0. // This version has no relative_to. Maybe<BalancePossiblyInfiniteDurationResult> BalancePossiblyInfiniteDuration( Isolate* isolate, Unit largest_unit, Handle<Object> relative_to_obj, double days, Handle<BigInt> nanoseconds, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 4. If largestUnit is one of "year", "month", "week", or "day", then if (largest_unit == Unit::kYear || largest_unit == Unit::kMonth || largest_unit == Unit::kWeek || largest_unit == Unit::kDay) { // a. Let result be ? NanosecondsToDays(nanoseconds, relativeTo). NanosecondsToDaysResult result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, NanosecondsToDays(isolate, nanoseconds, relative_to_obj, method_name), Nothing<BalancePossiblyInfiniteDurationResult>()); // b. Set days to result.[[Days]]. days = result.days; // c. Set nanoseconds to result.[[Nanoseconds]]. nanoseconds = BigInt::FromInt64(isolate, result.nanoseconds); // 5. Else, } else { // a. Set days to 0. days = 0; } // 6. Set hours, minutes, seconds, milliseconds, and microseconds to 0. Handle<BigInt> thousand = BigInt::FromInt64(isolate, 1000); Handle<BigInt> sixty = BigInt::FromInt64(isolate, 60); Handle<BigInt> zero = BigInt::FromInt64(isolate, 0); Handle<BigInt> hours = zero; Handle<BigInt> minutes = zero; Handle<BigInt> seconds = zero; Handle<BigInt> milliseconds = zero; Handle<BigInt> microseconds = zero; // 7. If nanoseconds < 0, let sign be −1; else, let sign be 1. // 8. Set nanoseconds to abs(nanoseconds). int32_t sign = 1; if (nanoseconds->IsNegative()) { sign = -1; nanoseconds = BigInt::UnaryMinus(isolate, nanoseconds); } // 9 If largestUnit is "year", "month", "week", "day", or "hour", then switch (largest_unit) { case Unit::kYear: case Unit::kMonth: case Unit::kWeek: case Unit::kDay: case Unit::kHour: // a. Set microseconds to floor(nanoseconds / 1000). microseconds = BigInt::Divide(isolate, nanoseconds, thousand).ToHandleChecked(); // b. Set nanoseconds to nanoseconds modulo 1000. nanoseconds = BigInt::Remainder(isolate, nanoseconds, thousand).ToHandleChecked(); // c. Set milliseconds to floor(microseconds / 1000). milliseconds = BigInt::Divide(isolate, microseconds, thousand).ToHandleChecked(); // d. Set microseconds to microseconds modulo 1000. microseconds = BigInt::Remainder(isolate, microseconds, thousand).ToHandleChecked(); // e. Set seconds to floor(milliseconds / 1000). seconds = BigInt::Divide(isolate, milliseconds, thousand).ToHandleChecked(); // f. Set milliseconds to milliseconds modulo 1000. milliseconds = BigInt::Remainder(isolate, milliseconds, thousand).ToHandleChecked(); // g. Set minutes to floor(seconds, 60). minutes = BigInt::Divide(isolate, seconds, sixty).ToHandleChecked(); // h. Set seconds to seconds modulo 60. seconds = BigInt::Remainder(isolate, seconds, sixty).ToHandleChecked(); // i. Set hours to floor(minutes / 60). hours = BigInt::Divide(isolate, minutes, sixty).ToHandleChecked(); // j. Set minutes to minutes modulo 60. minutes = BigInt::Remainder(isolate, minutes, sixty).ToHandleChecked(); break; // 10. Else if largestUnit is "minute", then case Unit::kMinute: // a. Set microseconds to floor(nanoseconds / 1000). microseconds = BigInt::Divide(isolate, nanoseconds, thousand).ToHandleChecked(); // b. Set nanoseconds to nanoseconds modulo 1000. nanoseconds = BigInt::Remainder(isolate, nanoseconds, thousand).ToHandleChecked(); // c. Set milliseconds to floor(microseconds / 1000). milliseconds = BigInt::Divide(isolate, microseconds, thousand).ToHandleChecked(); // d. Set microseconds to microseconds modulo 1000. microseconds = BigInt::Remainder(isolate, microseconds, thousand).ToHandleChecked(); // e. Set seconds to floor(milliseconds / 1000). seconds = BigInt::Divide(isolate, milliseconds, thousand).ToHandleChecked(); // f. Set milliseconds to milliseconds modulo 1000. milliseconds = BigInt::Remainder(isolate, milliseconds, thousand).ToHandleChecked(); // g. Set minutes to floor(seconds / 60). minutes = BigInt::Divide(isolate, seconds, sixty).ToHandleChecked(); // h. Set seconds to seconds modulo 60. seconds = BigInt::Remainder(isolate, seconds, sixty).ToHandleChecked(); break; // 11. Else if largestUnit is "second", then case Unit::kSecond: // a. Set microseconds to floor(nanoseconds / 1000). microseconds = BigInt::Divide(isolate, nanoseconds, thousand).ToHandleChecked(); // b. Set nanoseconds to nanoseconds modulo 1000. nanoseconds = BigInt::Remainder(isolate, nanoseconds, thousand).ToHandleChecked(); // c. Set milliseconds to floor(microseconds / 1000). milliseconds = BigInt::Divide(isolate, microseconds, thousand).ToHandleChecked(); // d. Set microseconds to microseconds modulo 1000. microseconds = BigInt::Remainder(isolate, microseconds, thousand).ToHandleChecked(); // e. Set seconds to floor(milliseconds / 1000). seconds = BigInt::Divide(isolate, milliseconds, thousand).ToHandleChecked(); // f. Set milliseconds to milliseconds modulo 1000. milliseconds = BigInt::Remainder(isolate, milliseconds, thousand).ToHandleChecked(); break; // 12. Else if largestUnit is "millisecond", then case Unit::kMillisecond: // a. Set microseconds to floor(nanoseconds / 1000). microseconds = BigInt::Divide(isolate, nanoseconds, thousand).ToHandleChecked(); // b. Set nanoseconds to nanoseconds modulo 1000. nanoseconds = BigInt::Remainder(isolate, nanoseconds, thousand).ToHandleChecked(); // c. Set milliseconds to floor(microseconds / 1000). milliseconds = BigInt::Divide(isolate, microseconds, thousand).ToHandleChecked(); // d. Set microseconds to microseconds modulo 1000. microseconds = BigInt::Remainder(isolate, microseconds, thousand).ToHandleChecked(); break; // 13. Else if largestUnit is "microsecond", then case Unit::kMicrosecond: // a. Set microseconds to floor(nanoseconds / 1000). microseconds = BigInt::Divide(isolate, nanoseconds, thousand).ToHandleChecked(); // b. Set nanoseconds to nanoseconds modulo 1000. nanoseconds = BigInt::Remainder(isolate, nanoseconds, thousand).ToHandleChecked(); break; // 14. Else, case Unit::kNanosecond: // a. Assert: largestUnit is "nanosecond". break; case Unit::kAuto: case Unit::kNotPresent: UNREACHABLE(); } // 15. For each value v of « days, hours, minutes, seconds, milliseconds, // microseconds, nanoseconds », do a. If 𝔽(v) is not finite, then i. If sign // = 1, then // 1. Return positive overflow. // ii. Else if sign = -1, then // 1. Return negative overflow. double hours_value = Object::Number(*BigInt::ToNumber(isolate, hours)); double minutes_value = Object::Number(*BigInt::ToNumber(isolate, minutes)); double seconds_value = Object::Number(*BigInt::ToNumber(isolate, seconds)); double milliseconds_value = Object::Number(*BigInt::ToNumber(isolate, milliseconds)); double microseconds_value = Object::Number(*BigInt::ToNumber(isolate, microseconds)); double nanoseconds_value = Object::Number(*BigInt::ToNumber(isolate, nanoseconds)); if (std::isinf(days) || std::isinf(hours_value) || std::isinf(minutes_value) || std::isinf(seconds_value) || std::isinf(milliseconds_value) || std::isinf(microseconds_value) || std::isinf(nanoseconds_value)) { return Just(BalancePossiblyInfiniteDurationResult( {{0, 0, 0, 0, 0, 0, 0}, sign == 1 ? BalanceOverflow::kPositive : BalanceOverflow::kNegative})); } // 16. Return ? CreateTimeDurationRecord(days, hours × sign, minutes × sign, // seconds × sign, milliseconds × sign, microseconds × sign, nanoseconds × // sign). TimeDurationRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, TimeDurationRecord::Create( isolate, days, hours_value * sign, minutes_value * sign, seconds_value * sign, milliseconds_value * sign, microseconds_value * sign, nanoseconds_value * sign), Nothing<BalancePossiblyInfiniteDurationResult>()); return Just( BalancePossiblyInfiniteDurationResult({result, BalanceOverflow::kNone})); } // #sec-temporal-addzoneddatetime MaybeHandle<BigInt> AddZonedDateTime(Isolate* isolate, Handle<BigInt> epoch_nanoseconds, Handle<JSReceiver> time_zone, Handle<JSReceiver> calendar, const DurationRecord& duration, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If options is not present, set options to undefined. return AddZonedDateTime(isolate, epoch_nanoseconds, time_zone, calendar, duration, isolate->factory()->undefined_value(), method_name); } // #sec-temporal-addzoneddatetime MaybeHandle<BigInt> AddZonedDateTime(Isolate* isolate, Handle<BigInt> epoch_nanoseconds, Handle<JSReceiver> time_zone, Handle<JSReceiver> calendar, const DurationRecord& duration, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); TimeDurationRecord time_duration = duration.time_duration; // 2. If all of years, months, weeks, and days are 0, then if (duration.years == 0 && duration.months == 0 && duration.weeks == 0 && time_duration.days == 0) { // a. Return ? AddInstant(epochNanoseconds, hours, minutes, seconds, // milliseconds, microseconds, nanoseconds). return AddInstant(isolate, epoch_nanoseconds, time_duration); } // 3. Let instant be ! CreateTemporalInstant(epochNanoseconds). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant(isolate, epoch_nanoseconds) .ToHandleChecked(); // 4. Let temporalDateTime be ? // BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar). Handle<JSTemporalPlainDateTime> temporal_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor(isolate, time_zone, instant, calendar, method_name), BigInt); // 5. Let datePart be ? CreateTemporalDate(temporalDateTime.[[ISOYear]], // temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], calendar). Handle<JSTemporalPlainDate> date_part; ASSIGN_RETURN_ON_EXCEPTION( isolate, date_part, CreateTemporalDate( isolate, {temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day()}, calendar), BigInt); // 6. Let dateDuration be ? CreateTemporalDuration(years, months, weeks, days, // 0, 0, 0, 0, 0, 0). Handle<JSTemporalDuration> date_duration; ASSIGN_RETURN_ON_EXCEPTION( isolate, date_duration, CreateTemporalDuration(isolate, {duration.years, duration.months, duration.weeks, {time_duration.days, 0, 0, 0, 0, 0, 0}}), BigInt); // 7. Let addedDate be ? CalendarDateAdd(calendar, datePart, dateDuration, // options). Handle<JSTemporalPlainDate> added_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, added_date, CalendarDateAdd(isolate, calendar, date_part, date_duration, options), BigInt); // 8. Let intermediateDateTime be ? // CreateTemporalDateTime(addedDate.[[ISOYear]], addedDate.[[ISOMonth]], // addedDate.[[ISODay]], temporalDateTime.[[ISOHour]], // temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], // temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], // temporalDateTime.[[ISONanosecond]], calendar). Handle<JSTemporalPlainDateTime> intermediate_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, intermediate_date_time, temporal::CreateTemporalDateTime( isolate, {{added_date->iso_year(), added_date->iso_month(), added_date->iso_day()}, {temporal_date_time->iso_hour(), temporal_date_time->iso_minute(), temporal_date_time->iso_second(), temporal_date_time->iso_millisecond(), temporal_date_time->iso_microsecond(), temporal_date_time->iso_nanosecond()}}, calendar), BigInt); // 9. Let intermediateInstant be ? BuiltinTimeZoneGetInstantFor(timeZone, // intermediateDateTime, "compatible"). Handle<JSTemporalInstant> intermediate_instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, intermediate_instant, BuiltinTimeZoneGetInstantFor(isolate, time_zone, intermediate_date_time, Disambiguation::kCompatible, method_name), BigInt); // 10. Return ? AddInstant(intermediateInstant.[[Nanoseconds]], hours, // minutes, seconds, milliseconds, microseconds, nanoseconds). time_duration.days = 0; return AddInstant(isolate, handle(intermediate_instant->nanoseconds(), isolate), time_duration); } Maybe<NanosecondsToDaysResult> NanosecondsToDays(Isolate* isolate, Handle<BigInt> nanoseconds, Handle<Object> relative_to_obj, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. Let dayLengthNs be nsPerDay. constexpr int64_t kDayLengthNs = 86400000000000LLU; Handle<BigInt> day_length_ns = BigInt::FromInt64(isolate, kDayLengthNs); double sign; switch (BigInt::CompareToNumber(nanoseconds, handle(Smi::zero(), isolate))) { // 2. If nanoseconds = 0, then case ComparisonResult::kEqual: // a. Return the Record { [[Days]]: 0, [[Nanoseconds]]: 0, [[DayLength]]: // dayLengthNs }. return Just(NanosecondsToDaysResult({0, 0, kDayLengthNs})); // 3. If nanoseconds < 0, let sign be -1; else, let sign be 1. case ComparisonResult::kLessThan: sign = -1; break; case ComparisonResult::kGreaterThan: sign = 1; break; default: UNREACHABLE(); } // 4. If Type(relativeTo) is not Object or relativeTo does not have an // [[InitializedTemporalZonedDateTime]] internal slot, then if (!IsJSTemporalZonedDateTime(*relative_to_obj)) { // a. Return the Record { [[Days]]: RoundTowardsZero(nanoseconds / // dayLengthNs), [[Nanoseconds]]: (abs(nanoseconds) modulo dayLengthNs) × // sign, [[DayLength]]: dayLengthNs }. if (sign == -1) { nanoseconds = BigInt::UnaryMinus(isolate, nanoseconds); } Handle<BigInt> days_bigint; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, days_bigint, BigInt::Divide(isolate, nanoseconds, day_length_ns), Nothing<NanosecondsToDaysResult>()); ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, nanoseconds, BigInt::Remainder(isolate, nanoseconds, day_length_ns), Nothing<NanosecondsToDaysResult>()); if (sign == -1) { days_bigint = BigInt::UnaryMinus(isolate, days_bigint); nanoseconds = BigInt::UnaryMinus(isolate, nanoseconds); } return Just(NanosecondsToDaysResult( {Object::Number(*BigInt::ToNumber(isolate, days_bigint)), Object::Number(*BigInt::ToNumber(isolate, nanoseconds)), kDayLengthNs})); } Handle<JSTemporalZonedDateTime> relative_to = Handle<JSTemporalZonedDateTime>::cast(relative_to_obj); // 5. Let startNs be ℝ(relativeTo.[[Nanoseconds]]). Handle<BigInt> start_ns = handle(relative_to->nanoseconds(), isolate); // 6. Let startInstant be ! CreateTemporalInstant(ℤ(sartNs)). Handle<JSTemporalInstant> start_instant = temporal::CreateTemporalInstant( isolate, handle(relative_to->nanoseconds(), isolate)) .ToHandleChecked(); // 7. Let startDateTime be ? // BuiltinTimeZoneGetPlainDateTimeFor(relativeTo.[[TimeZone]], // startInstant, relativeTo.[[Calendar]]). Handle<JSReceiver> time_zone = Handle<JSReceiver>(relative_to->time_zone(), isolate); Handle<JSReceiver> calendar = Handle<JSReceiver>(relative_to->calendar(), isolate); Handle<JSTemporalPlainDateTime> start_date_time; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, start_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor( isolate, time_zone, start_instant, calendar, method_name), Nothing<NanosecondsToDaysResult>()); // 8. Let endNs be startNs + nanoseconds. Handle<BigInt> end_ns; ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, end_ns, BigInt::Add(isolate, start_ns, nanoseconds), Nothing<NanosecondsToDaysResult>()); // 9. If ! IsValidEpochNanoseconds(ℤ(endNs)) is false, throw a RangeError // exception. if (!IsValidEpochNanoseconds(isolate, end_ns)) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<NanosecondsToDaysResult>()); } // 10. Let endInstant be ! CreateTemporalInstant(ℤ(endNs)). Handle<JSTemporalInstant> end_instant = temporal::CreateTemporalInstant(isolate, end_ns).ToHandleChecked(); // 11. Let endDateTime be ? // BuiltinTimeZoneGetPlainDateTimeFor(relativeTo.[[TimeZone]], // endInstant, relativeTo.[[Calendar]]). Handle<JSTemporalPlainDateTime> end_date_time; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, end_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor( isolate, time_zone, end_instant, calendar, method_name), Nothing<NanosecondsToDaysResult>()); // 12. Let dateDifference be ? // DifferenceISODateTime(startDateTime.[[ISOYear]], // startDateTime.[[ISOMonth]], startDateTime.[[ISODay]], // startDateTime.[[ISOHour]], startDateTime.[[ISOMinute]], // startDateTime.[[ISOSecond]], startDateTime.[[ISOMillisecond]], // startDateTime.[[ISOMicrosecond]], startDateTime.[[ISONanosecond]], // endDateTime.[[ISOYear]], endDateTime.[[ISOMonth]], endDateTime.[[ISODay]], // endDateTime.[[ISOHour]], endDateTime.[[ISOMinute]], // endDateTime.[[ISOSecond]], endDateTime.[[ISOMillisecond]], // endDateTime.[[ISOMicrosecond]], endDateTime.[[ISONanosecond]], // relativeTo.[[Calendar]], "day", OrdinaryObjectCreate(null)). DurationRecord date_difference; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_difference, DifferenceISODateTime( isolate, {{start_date_time->iso_year(), start_date_time->iso_month(), start_date_time->iso_day()}, {start_date_time->iso_hour(), start_date_time->iso_minute(), start_date_time->iso_second(), start_date_time->iso_millisecond(), start_date_time->iso_microsecond(), start_date_time->iso_nanosecond()}}, {{end_date_time->iso_year(), end_date_time->iso_month(), end_date_time->iso_day()}, {end_date_time->iso_hour(), end_date_time->iso_minute(), end_date_time->iso_second(), end_date_time->iso_millisecond(), end_date_time->iso_microsecond(), end_date_time->iso_nanosecond()}}, calendar, Unit::kDay, isolate->factory()->NewJSObjectWithNullProto(), method_name), Nothing<NanosecondsToDaysResult>()); // 13. Let days be dateDifference.[[Days]]. double days = date_difference.time_duration.days; // 14. Let intermediateNs be ℝ(? AddZonedDateTime(ℤ(startNs), // relativeTo.[[TimeZone]], relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, // 0, 0, 0)). Handle<BigInt> intermediate_ns; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, intermediate_ns, AddZonedDateTime(isolate, start_ns, time_zone, calendar, {0, 0, 0, {days, 0, 0, 0, 0, 0, 0}}, method_name), Nothing<NanosecondsToDaysResult>()); // 15. If sign is 1, then if (sign == 1) { // a. Repeat, while days > 0 and intermediateNs > endNs, while (days > 0 && BigInt::CompareToBigInt(intermediate_ns, end_ns) == ComparisonResult::kGreaterThan) { // i. Set days to days − 1. days -= 1; // ii. Set intermediateNs to ℝ(? AddZonedDateTime(ℤ(startNs), // relativeTo.[[TimeZone]], relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, // 0, 0, 0, 0)). ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, intermediate_ns, AddZonedDateTime(isolate, start_ns, time_zone, calendar, {0, 0, 0, {days, 0, 0, 0, 0, 0, 0}}, method_name), Nothing<NanosecondsToDaysResult>()); } } // 16. Set nanoseconds to endNs − intermediateNs. ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, nanoseconds, BigInt::Subtract(isolate, end_ns, intermediate_ns), Nothing<NanosecondsToDaysResult>()); // 17. Let done be false. bool done = false; // 18. Repeat, while done is false, while (!done) { // a. Let oneDayFartherNs be ℝ(? AddZonedDateTime(ℤ(intermediateNs), // relativeTo.[[TimeZone]], relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, // 0, 0, 0)). Handle<BigInt> one_day_farther_ns; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, one_day_farther_ns, AddZonedDateTime(isolate, intermediate_ns, time_zone, calendar, {0, 0, 0, {sign, 0, 0, 0, 0, 0, 0}}, method_name), Nothing<NanosecondsToDaysResult>()); // b. Set dayLengthNs to oneDayFartherNs − intermediateNs. ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, day_length_ns, BigInt::Subtract(isolate, one_day_farther_ns, intermediate_ns), Nothing<NanosecondsToDaysResult>()); // c. If (nanoseconds − dayLengthNs) × sign ≥ 0, then if (sign * CompareResultToSign( BigInt::CompareToBigInt(nanoseconds, day_length_ns)) >= 0) { // i. Set nanoseconds to nanoseconds − dayLengthNs. ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, nanoseconds, BigInt::Subtract(isolate, nanoseconds, day_length_ns), Nothing<NanosecondsToDaysResult>()); // ii. Set intermediateNs to oneDayFartherNs. intermediate_ns = one_day_farther_ns; // iii. Set days to days + sign. days += sign; // d. Else, } else { // i. Set done to true. done = true; } } // 20. Return the new Record { [[Days]]: days, [[Nanoseconds]]: nanoseconds, // [[DayLength]]: abs(dayLengthNs) }. NanosecondsToDaysResult result( {days, Object::Number(*BigInt::ToNumber(isolate, nanoseconds)), std::abs(day_length_ns->AsInt64())}); return Just(result); } // #sec-temporal-differenceisodatetime Maybe<DurationRecord> DifferenceISODateTime( Isolate* isolate, const DateTimeRecord& date_time1, const DateTimeRecord& date_time2, Handle<JSReceiver> calendar, Unit largest_unit, Handle<JSReceiver> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. Assert: ISODateTimeWithinLimits(y1, mon1, d1, h1, min1, s1, ms1, mus1, // ns1) is true. DCHECK(ISODateTimeWithinLimits(isolate, date_time1)); // 2. Assert: ISODateTimeWithinLimits(y2, mon2, d2, h2, min2, s2, ms2, mus2, // ns2) is true. DCHECK(ISODateTimeWithinLimits(isolate, date_time2)); // 3. Let timeDifference be ! DifferenceTime(h1, min1, s1, ms1, mus1, ns1, h2, // min2, s2, ms2, mus2, ns2). TimeDurationRecord time_difference = DifferenceTime(isolate, date_time1.time, date_time2.time).ToChecked(); // 4. Let timeSign be ! DurationSign(0, 0, 0, 0, timeDifference.[[Hours]], // timeDifference.[[Minutes]], timeDifference.[[Seconds]], // timeDifference.[[Milliseconds]], timeDifference.[[Microseconds]], // timeDifference.[[Nanoseconds]]). time_difference.days = 0; double time_sign = DurationSign(isolate, {0, 0, 0, time_difference}); // 5. Let dateSign be ! CompareISODate(y2, mon2, d2, y1, mon1, d1). double date_sign = CompareISODate(date_time2.date, date_time1.date); // 6. Let adjustedDate be CreateISODateRecordWithCalendar(y1, mon1, d1). DateRecord adjusted_date = date_time1.date; CHECK(IsValidISODate(isolate, adjusted_date)); // 7. If timeSign is -dateSign, then if (time_sign == -date_sign) { adjusted_date.day -= time_sign; // a. Set adjustedDate to BalanceISODate(adjustedDate.[[Year]], // adjustedDate.[[Month]], adjustedDate.[[Day]] - timeSign). adjusted_date = BalanceISODate(isolate, adjusted_date); // b. Set timeDifference to ! BalanceDuration(-timeSign, // timeDifference.[[Hours]], timeDifference.[[Minutes]], // timeDifference.[[Seconds]], timeDifference.[[Milliseconds]], // timeDifference.[[Microseconds]], timeDifference.[[Nanoseconds]], // largestUnit). time_difference.days = -time_sign; time_difference = BalanceDuration(isolate, largest_unit, time_difference, method_name) .ToChecked(); } // 8. Let date1 be ! CreateTemporalDate(adjustedDate.[[Year]], // adjustedDate.[[Month]], adjustedDate.[[Day]], calendar). Handle<JSTemporalPlainDate> date1 = CreateTemporalDate(isolate, adjusted_date, calendar).ToHandleChecked(); // 9. Let date2 be ! CreateTemporalDate(y2, mon2, d2, calendar). Handle<JSTemporalPlainDate> date2 = CreateTemporalDate(isolate, date_time2.date, calendar).ToHandleChecked(); // 10. Let dateLargestUnit be ! LargerOfTwoTemporalUnits("day", largestUnit). Unit date_largest_unit = LargerOfTwoTemporalUnits(Unit::kDay, largest_unit); // 11. Let untilOptions be ? MergeLargestUnitOption(options, dateLargestUnit). Handle<JSReceiver> until_options; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, until_options, MergeLargestUnitOption(isolate, options, date_largest_unit), Nothing<DurationRecord>()); // 12. Let dateDifference be ? CalendarDateUntil(calendar, date1, date2, // untilOptions). Handle<JSTemporalDuration> date_difference; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_difference, CalendarDateUntil(isolate, calendar, date1, date2, until_options), Nothing<DurationRecord>()); // 13. Let balanceResult be ? BalanceDuration(dateDifference.[[Days]], // timeDifference.[[Hours]], timeDifference.[[Minutes]], // timeDifference.[[Seconds]], timeDifference.[[Milliseconds]], // timeDifference.[[Microseconds]], timeDifference.[[Nanoseconds]], // largestUnit). time_difference.days = Object::Number(date_difference->days()); MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, time_difference, BalanceDuration(isolate, largest_unit, time_difference, method_name), Nothing<DurationRecord>()); // 14. Return ! CreateDurationRecord(dateDifference.[[Years]], // dateDifference.[[Months]], dateDifference.[[Weeks]], // balanceResult.[[Days]], balanceResult.[[Hours]], balanceResult.[[Minutes]], // balanceResult.[[Seconds]], balanceResult.[[Milliseconds]], // balanceResult.[[Microseconds]], balanceResult.[[Nanoseconds]]). return Just( CreateDurationRecord( isolate, {Object::Number(date_difference->years()), Object::Number(date_difference->months()), Object::Number(date_difference->weeks()), time_difference}) .ToChecked()); } // #sec-temporal-addinstant MaybeHandle<BigInt> AddInstant(Isolate* isolate, Handle<BigInt> epoch_nanoseconds, const TimeDurationRecord& addend) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 1. Assert: hours, minutes, seconds, milliseconds, microseconds, and // nanoseconds are integer Number values. // 2. Let result be epochNanoseconds + ℤ(nanoseconds) + // ℤ(microseconds) × 1000ℤ + ℤ(milliseconds) × 10^6ℤ + ℤ(seconds) × 10^9ℤ + // ℤ(minutes) × 60ℤ × 10^9ℤ + ℤ(hours) × 3600ℤ × 10^9ℤ. // epochNanoseconds + ℤ(nanoseconds) Handle<BigInt> result = BigInt::Add( isolate, epoch_nanoseconds, BigInt::FromNumber(isolate, factory->NewNumber(addend.nanoseconds)) .ToHandleChecked()) .ToHandleChecked(); // + ℤ(microseconds) × 1000ℤ Handle<BigInt> temp = BigInt::Multiply( isolate, BigInt::FromNumber(isolate, factory->NewNumber(addend.microseconds)) .ToHandleChecked(), BigInt::FromInt64(isolate, 1000)) .ToHandleChecked(); result = BigInt::Add(isolate, result, temp).ToHandleChecked(); // + ℤ(milliseconds) × 10^6ℤ temp = BigInt::Multiply(isolate, BigInt::FromNumber( isolate, factory->NewNumber(addend.milliseconds)) .ToHandleChecked(), BigInt::FromInt64(isolate, 1000000)) .ToHandleChecked(); result = BigInt::Add(isolate, result, temp).ToHandleChecked(); // + ℤ(seconds) × 10^9ℤ temp = BigInt::Multiply( isolate, BigInt::FromNumber(isolate, factory->NewNumber(addend.seconds)) .ToHandleChecked(), BigInt::FromInt64(isolate, 1000000000)) .ToHandleChecked(); result = BigInt::Add(isolate, result, temp).ToHandleChecked(); // + ℤ(minutes) × 60ℤ × 10^9ℤ. temp = BigInt::Multiply( isolate, BigInt::FromNumber(isolate, factory->NewNumber(addend.minutes)) .ToHandleChecked(), BigInt::FromInt64(isolate, 60000000000)) .ToHandleChecked(); result = BigInt::Add(isolate, result, temp).ToHandleChecked(); // + ℤ(hours) × 3600ℤ × 10^9ℤ. temp = BigInt::Multiply( isolate, BigInt::FromNumber(isolate, factory->NewNumber(addend.hours)) .ToHandleChecked(), BigInt::FromInt64(isolate, 3600000000000)) .ToHandleChecked(); result = BigInt::Add(isolate, result, temp).ToHandleChecked(); // 3. If ! IsValidEpochNanoseconds(result) is false, throw a RangeError // exception. if (!IsValidEpochNanoseconds(isolate, result)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), BigInt); } // 4. Return result. return result; } // #sec-temporal-isvalidepochnanoseconds bool IsValidEpochNanoseconds(Isolate* isolate, Handle<BigInt> epoch_nanoseconds) { TEMPORAL_ENTER_FUNC(); // nsMinInstant = -nsMaxInstant = -8.64 × 10^21 constexpr double kNsMinInstant = -8.64e21; // nsMaxInstant = 10^8 × nsPerDay = 8.64 × 1021 constexpr double kNsMaxInstant = 8.64e21; // 1. Assert: Type(epochNanoseconds) is BigInt. // 2. If ℝ(epochNanoseconds) < nsMinInstant or ℝ(epochNanoseconds) > // nsMaxInstant, then if (BigInt::CompareToNumber(epoch_nanoseconds, isolate->factory()->NewNumber(kNsMinInstant)) == ComparisonResult::kLessThan || BigInt::CompareToNumber(epoch_nanoseconds, isolate->factory()->NewNumber(kNsMaxInstant)) == ComparisonResult::kGreaterThan) { // a. Return false. return false; } return true; } Handle<BigInt> GetEpochFromISOParts(Isolate* isolate, const DateTimeRecord& date_time) { TEMPORAL_ENTER_FUNC(); // 1. Assert: year, month, day, hour, minute, second, millisecond, // microsecond, and nanosecond are integers. // 2. Assert: ! IsValidISODate(year, month, day) is true. DCHECK(IsValidISODate(isolate, date_time.date)); // 3. Assert: ! IsValidTime(hour, minute, second, millisecond, microsecond, // nanosecond) is true. DCHECK(IsValidTime(isolate, date_time.time)); // 4. Let date be ! MakeDay(𝔽(year), 𝔽(month − 1), 𝔽(day)). double date = MakeDay(date_time.date.year, date_time.date.month - 1, date_time.date.day); // 5. Let time be ! MakeTime(𝔽(hour), 𝔽(minute), 𝔽(second), 𝔽(millisecond)). double time = MakeTime(date_time.time.hour, date_time.time.minute, date_time.time.second, date_time.time.millisecond); // 6. Let ms be ! MakeDate(date, time). double ms = MakeDate(date, time); // 7. Assert: ms is finite. // 8. Return ℝ(ms) × 10^6 + microsecond × 10^3 + nanosecond. return BigInt::Add( isolate, BigInt::Add( isolate, BigInt::Multiply( isolate, BigInt::FromNumber(isolate, isolate->factory()->NewNumber(ms)) .ToHandleChecked(), BigInt::FromInt64(isolate, 1000000)) .ToHandleChecked(), BigInt::Multiply( isolate, BigInt::FromInt64(isolate, date_time.time.microsecond), BigInt::FromInt64(isolate, 1000)) .ToHandleChecked()) .ToHandleChecked(), BigInt::FromInt64(isolate, date_time.time.nanosecond)) .ToHandleChecked(); } // #sec-temporal-durationsign int32_t DurationSign(Isolate* isolaet, const DurationRecord& dur) { TEMPORAL_ENTER_FUNC(); // 1. For each value v of « years, months, weeks, days, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds », do a. If v < 0, return // −1. b. If v > 0, return 1. // 2. Return 0. if (dur.years < 0) return -1; if (dur.years > 0) return 1; if (dur.months < 0) return -1; if (dur.months > 0) return 1; if (dur.weeks < 0) return -1; if (dur.weeks > 0) return 1; const TimeDurationRecord& time = dur.time_duration; if (time.days < 0) return -1; if (time.days > 0) return 1; if (time.hours < 0) return -1; if (time.hours > 0) return 1; if (time.minutes < 0) return -1; if (time.minutes > 0) return 1; if (time.seconds < 0) return -1; if (time.seconds > 0) return 1; if (time.milliseconds < 0) return -1; if (time.milliseconds > 0) return 1; if (time.microseconds < 0) return -1; if (time.microseconds > 0) return 1; if (time.nanoseconds < 0) return -1; if (time.nanoseconds > 0) return 1; return 0; } } // namespace namespace temporal { // #sec-temporal-isvalidduration bool IsValidDuration(Isolate* isolate, const DurationRecord& dur) { TEMPORAL_ENTER_FUNC(); // 1. Let sign be ! DurationSign(years, months, weeks, days, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds). int32_t sign = DurationSign(isolate, dur); // 2. For each value v of « years, months, weeks, days, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds », do a. If v is not // finite, return false. b. If v < 0 and sign > 0, return false. c. If v > 0 // and sign < 0, return false. // 3. Return true. const TimeDurationRecord& time = dur.time_duration; if (!(std::isfinite(dur.years) && std::isfinite(dur.months) && std::isfinite(dur.weeks) && std::isfinite(time.days) && std::isfinite(time.hours) && std::isfinite(time.minutes) && std::isfinite(time.seconds) && std::isfinite(time.milliseconds) && std::isfinite(time.microseconds) && std::isfinite(time.nanoseconds))) { return false; } return !((sign > 0 && (dur.years < 0 || dur.months < 0 || dur.weeks < 0 || time.days < 0 || time.hours < 0 || time.minutes < 0 || time.seconds < 0 || time.milliseconds < 0 || time.microseconds < 0 || time.nanoseconds < 0)) || (sign < 0 && (dur.years > 0 || dur.months > 0 || dur.weeks > 0 || time.days > 0 || time.hours > 0 || time.minutes > 0 || time.seconds > 0 || time.milliseconds > 0 || time.microseconds > 0 || time.nanoseconds > 0))); } } // namespace temporal namespace { // #sec-temporal-isisoleapyear bool IsISOLeapYear(Isolate* isolate, int32_t year) { TEMPORAL_ENTER_FUNC(); // 1. Assert: year is an integer. // 2. If year modulo 4 ≠ 0, return false. // 3. If year modulo 400 = 0, return true. // 4. If year modulo 100 = 0, return false. // 5. Return true. return isolate->date_cache()->IsLeap(year); } // #sec-temporal-isodaysinmonth int32_t ISODaysInMonth(Isolate* isolate, int32_t year, int32_t month) { TEMPORAL_ENTER_FUNC(); // 1. Assert: year is an integer. // 2. Assert: month is an integer, month ≥ 1, and month ≤ 12. DCHECK_GE(month, 1); DCHECK_LE(month, 12); // 3. If month is 1, 3, 5, 7, 8, 10, or 12, return 31. if (month % 2 == ((month < 8) ? 1 : 0)) return 31; // 4. If month is 4, 6, 9, or 11, return 30. DCHECK(month == 2 || month == 4 || month == 6 || month == 9 || month == 11); if (month != 2) return 30; // 5. If ! IsISOLeapYear(year) is true, return 29. return IsISOLeapYear(isolate, year) ? 29 : 28; // 6. Return 28. } // #sec-temporal-isodaysinyear int32_t ISODaysInYear(Isolate* isolate, int32_t year) { TEMPORAL_ENTER_FUNC(); // 1. Assert: year is an integer. // 2. If ! IsISOLeapYear(year) is true, then // a. Return 366. // 3. Return 365. return IsISOLeapYear(isolate, year) ? 366 : 365; } bool IsValidTime(Isolate* isolate, const TimeRecord& time) { TEMPORAL_ENTER_FUNC(); // 2. If hour < 0 or hour > 23, then // a. Return false. if (time.hour < 0 || time.hour > 23) return false; // 3. If minute < 0 or minute > 59, then // a. Return false. if (time.minute < 0 || time.minute > 59) return false; // 4. If second < 0 or second > 59, then // a. Return false. if (time.second < 0 || time.second > 59) return false; // 5. If millisecond < 0 or millisecond > 999, then // a. Return false. if (time.millisecond < 0 || time.millisecond > 999) return false; // 6. If microsecond < 0 or microsecond > 999, then // a. Return false. if (time.microsecond < 0 || time.microsecond > 999) return false; // 7. If nanosecond < 0 or nanosecond > 999, then // a. Return false. if (time.nanosecond < 0 || time.nanosecond > 999) return false; // 8. Return true. return true; } // #sec-temporal-isvalidisodate bool IsValidISODate(Isolate* isolate, const DateRecord& date) { TEMPORAL_ENTER_FUNC(); // 1. Assert: year, month, and day are integers. // 2. If month < 1 or month > 12, then // a. Return false. if (date.month < 1 || date.month > 12) return false; // 3. Let daysInMonth be ! ISODaysInMonth(year, month). // 4. If day < 1 or day > daysInMonth, then // a. Return false. if (date.day < 1 || date.day > ISODaysInMonth(isolate, date.year, date.month)) { return false; } // 5. Return true. return true; } // #sec-temporal-compareisodate int32_t CompareISODate(const DateRecord& one, const DateRecord& two) { TEMPORAL_ENTER_FUNC(); // 1. Assert: y1, m1, d1, y2, m2, and d2 are integers. // 2. If y1 > y2, return 1. if (one.year > two.year) return 1; // 3. If y1 < y2, return -1. if (one.year < two.year) return -1; // 4. If m1 > m2, return 1. if (one.month > two.month) return 1; // 5. If m1 < m2, return -1. if (one.month < two.month) return -1; // 6. If d1 > d2, return 1. if (one.day > two.day) return 1; // 7. If d1 < d2, return -1. if (one.day < two.day) return -1; // 8. Return 0. return 0; } int32_t CompareTemporalTime(const TimeRecord& time1, const TimeRecord& time2); // #sec-temporal-compareisodatetime int32_t CompareISODateTime(const DateTimeRecord& one, const DateTimeRecord& two) { // 2. Let dateResult be ! CompareISODate(y1, mon1, d1, y2, mon2, d2). int32_t date_result = CompareISODate(one.date, two.date); // 3. If dateResult is not 0, then if (date_result != 0) { // a. Return dateResult. return date_result; } // 4. Return ! CompareTemporalTime(h1, min1, s1, ms1, mus1, ns1, h2, min2, s2, // ms2, mus2, ns2). return CompareTemporalTime(one.time, two.time); } inline int32_t floor_divid(int32_t a, int32_t b) { return (((a) / (b)) + ((((a) < 0) && (((a) % (b)) != 0)) ? -1 : 0)); } // #sec-temporal-balanceisoyearmonth void BalanceISOYearMonth(Isolate* isolate, int32_t* year, int32_t* month) { TEMPORAL_ENTER_FUNC(); // 1. Assert: year and month are integers. // 2. Set year to year + floor((month - 1) / 12). *year += floor_divid((*month - 1), 12); // 3. Set month to (month − 1) modulo 12 + 1. *month = static_cast<int32_t>(modulo(*month - 1, 12)) + 1; // 4. Return the new Record { [[Year]]: year, [[Month]]: month }. } // #sec-temporal-balancetime DateTimeRecord BalanceTime(const UnbalancedTimeRecord& input) { TEMPORAL_ENTER_FUNC(); UnbalancedTimeRecord time(input); TimeRecord result; // 1. Assert: hour, minute, second, millisecond, microsecond, and nanosecond // are integers. // 2. Set microsecond to microsecond + floor(nanosecond / 1000). time.microsecond += std::floor(time.nanosecond / 1000.0); // 3. Set nanosecond to nanosecond modulo 1000. result.nanosecond = modulo(time.nanosecond, 1000); // 4. Set millisecond to millisecond + floor(microsecond / 1000). time.millisecond += std::floor(time.microsecond / 1000.0); // 5. Set microsecond to microsecond modulo 1000. result.microsecond = modulo(time.microsecond, 1000); // 6. Set second to second + floor(millisecond / 1000). time.second += std::floor(time.millisecond / 1000.0); // 7. Set millisecond to millisecond modulo 1000. result.millisecond = modulo(time.millisecond, 1000); // 8. Set minute to minute + floor(second / 60). time.minute += std::floor(time.second / 60.0); // 9. Set second to second modulo 60. result.second = modulo(time.second, 60); // 10. Set hour to hour + floor(minute / 60). time.hour += std::floor(time.minute / 60.0); // 11. Set minute to minute modulo 60. result.minute = modulo(time.minute, 60); // 12. Let days be floor(hour / 24). int32_t days = std::floor(time.hour / 24.0); // 13. Set hour to hour modulo 24. result.hour = modulo(time.hour, 24); // 14. Return the new Record { [[Days]]: days, [[Hour]]: hour, [[Minute]]: // minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: // microsecond, [[Nanosecond]]: nanosecond }. return {{0, 0, days}, result}; } // #sec-temporal-differencetime Maybe<TimeDurationRecord> DifferenceTime(Isolate* isolate, const TimeRecord& time1, const TimeRecord& time2) { TEMPORAL_ENTER_FUNC(); // 1. Assert: h1, min1, s1, ms1, mus1, ns1, h2, min2, s2, ms2, mus2, and ns2 // are integers. TimeDurationRecord dur; // 2. Let hours be h2 − h1. dur.hours = time2.hour - time1.hour; // 3. Let minutes be min2 − min1. dur.minutes = time2.minute - time1.minute; // 4. Let seconds be s2 − s1. dur.seconds = time2.second - time1.second; // 5. Let milliseconds be ms2 − ms1. dur.milliseconds = time2.millisecond - time1.millisecond; // 6. Let microseconds be mus2 − mus1. dur.microseconds = time2.microsecond - time1.microsecond; // 7. Let nanoseconds be ns2 − ns1. dur.nanoseconds = time2.nanosecond - time1.nanosecond; // 8. Let sign be ! DurationSign(0, 0, 0, 0, hours, minutes, seconds, // milliseconds, microseconds, nanoseconds). double sign = DurationSign( isolate, {0, 0, 0, {0, dur.hours, dur.minutes, dur.seconds, dur.milliseconds, dur.microseconds, dur.nanoseconds}}); // 9. Let bt be ! BalanceTime(hours × sign, minutes × sign, seconds × sign, // milliseconds × sign, microseconds × sign, nanoseconds × sign). DateTimeRecord bt = BalanceTime({dur.hours * sign, dur.minutes * sign, dur.seconds * sign, dur.milliseconds * sign, dur.microseconds * sign, dur.nanoseconds * sign}); // 9. Return ! CreateTimeDurationRecord(bt.[[Days]] × sign, bt.[[Hour]] × // sign, bt.[[Minute]] × sign, bt.[[Second]] × sign, bt.[[Millisecond]] × // sign, bt.[[Microsecond]] × sign, bt.[[Nanosecond]] × sign). return TimeDurationRecord::Create( isolate, bt.date.day * sign, bt.time.hour * sign, bt.time.minute * sign, bt.time.second * sign, bt.time.millisecond * sign, bt.time.microsecond * sign, bt.time.nanosecond * sign); } // #sec-temporal-addtime DateTimeRecord AddTime(Isolate* isolate, const TimeRecord& time, const TimeDurationRecord& addend) { TEMPORAL_ENTER_FUNC(); DCHECK_EQ(addend.days, 0); // 1. Assert: hour, minute, second, millisecond, microsecond, nanosecond, // hours, minutes, seconds, milliseconds, microseconds, and nanoseconds are // integers. // 2. Let hour be hour + hours. return BalanceTime({time.hour + addend.hours, // 3. Let minute be minute + minutes. time.minute + addend.minutes, // 4. Let second be second + seconds. time.second + addend.seconds, // 5. Let millisecond be millisecond + milliseconds. time.millisecond + addend.milliseconds, // 6. Let microsecond be microsecond + microseconds. time.microsecond + addend.microseconds, // 7. Let nanosecond be nanosecond + nanoseconds. time.nanosecond + addend.nanoseconds}); // 8. Return ! BalanceTime(hour, minute, second, millisecond, microsecond, // nanosecond). } // #sec-temporal-totaldurationnanoseconds Handle<BigInt> TotalDurationNanoseconds(Isolate* isolate, const TimeDurationRecord& value, double offset_shift) { TEMPORAL_ENTER_FUNC(); TimeDurationRecord duration(value); Handle<BigInt> nanoseconds = BigInt::FromNumber(isolate, isolate->factory()->NewNumber(value.nanoseconds)) .ToHandleChecked(); // 1. Assert: offsetShift is an integer. // 2. Set nanoseconds to ℝ(nanoseconds). // 3. If days ≠ 0, then if (duration.days != 0) { // a. Set nanoseconds to nanoseconds − offsetShift. nanoseconds = BigInt::Subtract( isolate, nanoseconds, BigInt::FromNumber( isolate, isolate->factory()->NewNumber(offset_shift)) .ToHandleChecked()) .ToHandleChecked(); } Handle<BigInt> thousand = BigInt::FromInt64(isolate, 1000); Handle<BigInt> sixty = BigInt::FromInt64(isolate, 60); Handle<BigInt> twentyfour = BigInt::FromInt64(isolate, 24); // 4. Set hours to ℝ(hours) + ℝ(days) × 24. Handle<BigInt> x = BigInt::FromNumber(isolate, isolate->factory()->NewNumber(value.days)) .ToHandleChecked(); x = BigInt::Multiply(isolate, twentyfour, x).ToHandleChecked(); x = BigInt::Add(isolate, x, BigInt::FromNumber(isolate, isolate->factory()->NewNumber(value.hours)) .ToHandleChecked()) .ToHandleChecked(); // 5. Set minutes to ℝ(minutes) + hours × 60. x = BigInt::Multiply(isolate, sixty, x).ToHandleChecked(); x = BigInt::Add(isolate, x, BigInt::FromNumber( isolate, isolate->factory()->NewNumber(value.minutes)) .ToHandleChecked()) .ToHandleChecked(); // 6. Set seconds to ℝ(seconds) + minutes × 60. x = BigInt::Multiply(isolate, sixty, x).ToHandleChecked(); x = BigInt::Add(isolate, x, BigInt::FromNumber( isolate, isolate->factory()->NewNumber(value.seconds)) .ToHandleChecked()) .ToHandleChecked(); // 7. Set milliseconds to ℝ(milliseconds) + seconds × 1000. x = BigInt::Multiply(isolate, thousand, x).ToHandleChecked(); x = BigInt::Add(isolate, x, BigInt::FromNumber(isolate, isolate->factory()->NewNumber( value.milliseconds)) .ToHandleChecked()) .ToHandleChecked(); // 8. Set microseconds to ℝ(microseconds) + milliseconds × 1000. x = BigInt::Multiply(isolate, thousand, x).ToHandleChecked(); x = BigInt::Add(isolate, x, BigInt::FromNumber(isolate, isolate->factory()->NewNumber( value.microseconds)) .ToHandleChecked()) .ToHandleChecked(); // 9. Return nanoseconds + microseconds × 1000. x = BigInt::Multiply(isolate, thousand, x).ToHandleChecked(); x = BigInt::Add(isolate, x, nanoseconds).ToHandleChecked(); return x; } Maybe<DateRecord> RegulateISODate(Isolate* isolate, ShowOverflow overflow, const DateRecord& date); Maybe<int32_t> ResolveISOMonth(Isolate* isolate, Handle<JSReceiver> fields); // #sec-temporal-isomonthdayfromfields Maybe<DateRecord> ISOMonthDayFromFields(Isolate* isolate, Handle<JSReceiver> fields, Handle<JSReceiver> options, const char* method_name) { Factory* factory = isolate->factory(); // 1. Assert: Type(fields) is Object. // 2. Set fields to ? PrepareTemporalFields(fields, « "day", "month", // "monthCode", "year" », «"day"»). Handle<FixedArray> field_names = DayMonthMonthCodeYearInFixedArray(isolate); ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, fields, PrepareTemporalFields(isolate, fields, field_names, RequiredFields::kDay), Nothing<DateRecord>()); // 3. Let overflow be ? ToTemporalOverflow(options). ShowOverflow overflow; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, overflow, ToTemporalOverflow(isolate, options, method_name), Nothing<DateRecord>()); // 4. Let month be ! Get(fields, "month"). Handle<Object> month_obj = JSReceiver::GetProperty(isolate, fields, factory->month_string()) .ToHandleChecked(); // 5. Let monthCode be ! Get(fields, "monthCode"). Handle<Object> month_code_obj = JSReceiver::GetProperty(isolate, fields, factory->monthCode_string()) .ToHandleChecked(); // 6. Let year be ! Get(fields, "year"). Handle<Object> year_obj = JSReceiver::GetProperty(isolate, fields, factory->year_string()) .ToHandleChecked(); // 7. If month is not undefined, and monthCode and year are both undefined, // then if (!IsUndefined(*month_obj, isolate) && IsUndefined(*month_code_obj, isolate) && IsUndefined(*year_obj, isolate)) { // a. Throw a TypeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Nothing<DateRecord>()); } // 8. Set month to ? ResolveISOMonth(fields). DateRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, result.month, ResolveISOMonth(isolate, fields), Nothing<DateRecord>()); // 9. Let day be ! Get(fields, "day"). Handle<Object> day_obj = JSReceiver::GetProperty(isolate, fields, factory->day_string()) .ToHandleChecked(); // 10. Assert: Type(day) is Number. // Note: "day" in fields is always converted by // ToIntegerThrowOnInfinity inside the PrepareTemporalFields above. // Therefore the day_obj is always an integer. DCHECK(IsSmi(*day_obj) || IsHeapNumber(*day_obj)); result.day = FastD2I(floor(Object::Number(*day_obj))); // 11. Let referenceISOYear be 1972 (the first leap year after the Unix // epoch). int32_t reference_iso_year = 1972; // 12. If monthCode is undefined, then if (IsUndefined(*month_code_obj, isolate)) { result.year = FastD2I(floor(Object::Number(*year_obj))); // a. Let result be ? RegulateISODate(year, month, day, overflow). } else { // 13. Else, // a. Let result be ? RegulateISODate(referenceISOYear, month, day, // overflow). result.year = reference_iso_year; } MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, RegulateISODate(isolate, overflow, result), Nothing<DateRecord>()); // 14. Return the new Record { [[Month]]: result.[[Month]], [[Day]]: // result.[[Day]], [[ReferenceISOYear]]: referenceISOYear }. result.year = reference_iso_year; return Just(result); } } // namespace // #sec-temporal.duration MaybeHandle<JSTemporalDuration> JSTemporalDuration::Constructor( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<Object> years, Handle<Object> months, Handle<Object> weeks, Handle<Object> days, Handle<Object> hours, Handle<Object> minutes, Handle<Object> seconds, Handle<Object> milliseconds, Handle<Object> microseconds, Handle<Object> nanoseconds) { const char* method_name = "Temporal.Duration"; // 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( method_name)), JSTemporalDuration); } // 2. Let y be ? ToIntegerWithoutRounding(years). double y; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, y, ToIntegerWithoutRounding(isolate, years), Handle<JSTemporalDuration>()); // 3. Let mo be ? ToIntegerWithoutRounding(months). double mo; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, mo, ToIntegerWithoutRounding(isolate, months), Handle<JSTemporalDuration>()); // 4. Let w be ? ToIntegerWithoutRounding(weeks). double w; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, w, ToIntegerWithoutRounding(isolate, weeks), Handle<JSTemporalDuration>()); // 5. Let d be ? ToIntegerWithoutRounding(days). double d; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, d, ToIntegerWithoutRounding(isolate, days), Handle<JSTemporalDuration>()); // 6. Let h be ? ToIntegerWithoutRounding(hours). double h; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, h, ToIntegerWithoutRounding(isolate, hours), Handle<JSTemporalDuration>()); // 7. Let m be ? ToIntegerWithoutRounding(minutes). double m; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, m, ToIntegerWithoutRounding(isolate, minutes), Handle<JSTemporalDuration>()); // 8. Let s be ? ToIntegerWithoutRounding(seconds). double s; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, s, ToIntegerWithoutRounding(isolate, seconds), Handle<JSTemporalDuration>()); // 9. Let ms be ? ToIntegerWithoutRounding(milliseconds). double ms; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, ms, ToIntegerWithoutRounding(isolate, milliseconds), Handle<JSTemporalDuration>()); // 10. Let mis be ? ToIntegerWithoutRounding(microseconds). double mis; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, mis, ToIntegerWithoutRounding(isolate, microseconds), Handle<JSTemporalDuration>()); // 11. Let ns be ? ToIntegerWithoutRounding(nanoseconds). double ns; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, ns, ToIntegerWithoutRounding(isolate, nanoseconds), Handle<JSTemporalDuration>()); // 12. Return ? CreateTemporalDuration(y, mo, w, d, h, m, s, ms, mis, ns, // NewTarget). return CreateTemporalDuration(isolate, target, new_target, {y, mo, w, {d, h, m, s, ms, mis, ns}}); } namespace { // #sec-temporal-torelativetemporalobject MaybeHandle<Object> ToRelativeTemporalObject(Isolate* isolate, Handle<JSReceiver> options, const char* method_name); // #sec-temporal-defaulttemporallargestunit Unit DefaultTemporalLargestUnit(const DurationRecord& dur); // #sec-temporal-roundtemporalinstant Handle<BigInt> RoundTemporalInstant(Isolate* isolate, Handle<BigInt> ns, double increment, Unit unit, RoundingMode rounding_mode); // #sec-temporal-differenceinstant TimeDurationRecord DifferenceInstant(Isolate* isolate, Handle<BigInt> ns1, Handle<BigInt> ns2, double rounding_increment, Unit smallest_unit, Unit largest_unit, RoundingMode rounding_mode, const char* method_name); // #sec-temporal-differencezoneddatetime Maybe<DurationRecord> DifferenceZonedDateTime( Isolate* isolate, Handle<BigInt> ns1, Handle<BigInt> ns2, Handle<JSReceiver> time_zone, Handle<JSReceiver> calendar, Unit largest_unit, Handle<JSReceiver> options, const char* method_name); // #sec-temporal-addduration Maybe<DurationRecord> AddDuration(Isolate* isolate, const DurationRecord& dur1, const DurationRecord& dur2, Handle<Object> relative_to_obj, const char* method_name); // #sec-temporal-adjustroundeddurationdays Maybe<DurationRecord> AdjustRoundedDurationDays(Isolate* isolate, const DurationRecord& duration, double increment, Unit unit, RoundingMode rounding_mode, Handle<Object> relative_to_obj, const char* method_name) { // 1. If Type(relativeTo) is not Object; or relativeTo does not have an // [[InitializedTemporalZonedDateTime]] internal slot; or unit is one of // "year", "month", "week", or "day"; or unit is "nanosecond" and increment is // 1, then if (!IsJSTemporalZonedDateTime(*relative_to_obj) || (unit == Unit::kYear || unit == Unit::kMonth || unit == Unit::kWeek || unit == Unit::kDay) || (unit == Unit::kNanosecond && increment == 1)) { // a. Return ! CreateDurationRecord(years, months, weeks, days, hours, // minutes, seconds, milliseconds, microseconds, nanoseconds). return Just(CreateDurationRecord(isolate, duration).ToChecked()); } Handle<JSTemporalZonedDateTime> relative_to = Handle<JSTemporalZonedDateTime>::cast(relative_to_obj); // 2. Let timeRemainderNs be ! TotalDurationNanoseconds(0, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds, 0). Handle<BigInt> time_remainder_ns = TotalDurationNanoseconds( isolate, {0, duration.time_duration.hours, duration.time_duration.minutes, duration.time_duration.seconds, duration.time_duration.milliseconds, duration.time_duration.microseconds, duration.time_duration.nanoseconds}, 0); ComparisonResult compare = BigInt::CompareToNumber(time_remainder_ns, handle(Smi::zero(), isolate)); double direction; // 3. If timeRemainderNs = 0, let direction be 0. if (compare == ComparisonResult::kEqual) { direction = 0; // 4. Else if timeRemainderNs < 0, let direction be -1. } else if (compare == ComparisonResult::kLessThan) { direction = -1; // 5. Else, let direction be 1. } else { direction = 1; } // 6. Let dayStart be ? AddZonedDateTime(relativeTo.[[Nanoseconds]], // relativeTo.[[TimeZone]], relativeTo.[[Calendar]], years, months, weeks, // days, 0, 0, 0, 0, 0, 0). Handle<BigInt> day_start; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, day_start, AddZonedDateTime(isolate, handle(relative_to->nanoseconds(), isolate), handle(relative_to->time_zone(), isolate), handle(relative_to->calendar(), isolate), {duration.years, duration.months, duration.weeks, {duration.time_duration.days, 0, 0, 0, 0, 0, 0}}, method_name), Nothing<DurationRecord>()); // 7. Let dayEnd be ? AddZonedDateTime(dayStart, relativeTo.[[TimeZone]], // relativeTo.[[Calendar]], 0, 0, 0, direction, 0, 0, 0, 0, 0, 0). Handle<BigInt> day_end; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, day_end, AddZonedDateTime(isolate, day_start, handle(relative_to->time_zone(), isolate), handle(relative_to->calendar(), isolate), {0, 0, 0, {direction, 0, 0, 0, 0, 0, 0}}, method_name), Nothing<DurationRecord>()); // 8. Let dayLengthNs be ℝ(dayEnd - dayStart). Handle<BigInt> day_length_ns = BigInt::Subtract(isolate, day_end, day_start).ToHandleChecked(); // 9. If (timeRemainderNs - dayLengthNs) × direction < 0, then Handle<BigInt> time_remainder_ns_minus_day_length_ns = BigInt::Subtract(isolate, time_remainder_ns, day_length_ns) .ToHandleChecked(); if (time_remainder_ns_minus_day_length_ns->AsInt64() * direction < 0) { // a. Return ! CreateDurationRecord(years, months, weeks, days, hours, // minutes, seconds, milliseconds, microseconds, nanoseconds). return Just(CreateDurationRecord(isolate, duration).ToChecked()); } // 10. Set timeRemainderNs to ! RoundTemporalInstant(ℤ(timeRemainderNs - // dayLengthNs), increment, unit, roundingMode). time_remainder_ns = RoundTemporalInstant(isolate, time_remainder_ns_minus_day_length_ns, increment, unit, rounding_mode); // 11. Let adjustedDateDuration be ? AddDuration(years, months, weeks, days, // 0, 0, 0, 0, 0, 0, 0, 0, 0, direction, 0, 0, 0, 0, 0, 0, relativeTo). DurationRecord adjusted_date_duration; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, adjusted_date_duration, AddDuration(isolate, {duration.years, duration.months, duration.weeks, {duration.time_duration.days, 0, 0, 0, 0, 0, 0}}, {0, 0, 0, {direction, 0, 0, 0, 0, 0, 0}}, relative_to, method_name), Nothing<DurationRecord>()); // 12. Let adjustedTimeDuration be ? BalanceDuration(0, 0, 0, 0, 0, 0, // timeRemainderNs, "hour"). TimeDurationRecord adjusted_time_duration; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, adjusted_time_duration, BalanceDuration(isolate, Unit::kHour, time_remainder_ns, method_name), Nothing<DurationRecord>()); // 13. Return ! CreateDurationRecord(adjustedDateDuration.[[Years]], // adjustedDateDuration.[[Months]], adjustedDateDuration.[[Weeks]], // adjustedDateDuration.[[Days]], adjustedTimeDuration.[[Hours]], // adjustedTimeDuration.[[Minutes]], adjustedTimeDuration.[[Seconds]], // adjustedTimeDuration.[[Milliseconds]], // adjustedTimeDuration.[[Microseconds]], // adjustedTimeDuration.[[Nanoseconds]]). adjusted_time_duration.days = adjusted_date_duration.time_duration.days; return Just( CreateDurationRecord( isolate, {adjusted_date_duration.years, adjusted_date_duration.months, adjusted_date_duration.weeks, adjusted_time_duration}) .ToChecked()); } // #sec-temporal-calculateoffsetshift Maybe<int64_t> CalculateOffsetShift(Isolate* isolate, Handle<Object> relative_to_obj, const DateDurationRecord& dur, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If Type(relativeTo) is not Object or relativeTo does not have an // [[InitializedTemporalZonedDateTime]] internal slot, return 0. if (!IsJSTemporalZonedDateTime(*relative_to_obj)) { return Just(static_cast<int64_t>(0)); } Handle<JSTemporalZonedDateTime> relative_to = Handle<JSTemporalZonedDateTime>::cast(relative_to_obj); // 2. Let instant be ! CreateTemporalInstant(relativeTo.[[Nanoseconds]]). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant( isolate, handle(relative_to->nanoseconds(), isolate)) .ToHandleChecked(); // 3. Let offsetBefore be ? GetOffsetNanosecondsFor(relativeTo.[[TimeZone]], // instant). int64_t offset_before; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_before, GetOffsetNanosecondsFor(isolate, handle(relative_to->time_zone(), isolate), instant, method_name), Nothing<int64_t>()); // 4. Let after be ? AddZonedDateTime(relativeTo.[[Nanoseconds]], // relativeTo.[[TimeZone]], relativeTo.[[Calendar]], y, mon, w, d, 0, 0, 0, 0, // 0, 0). Handle<BigInt> after; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, after, AddZonedDateTime( isolate, handle(relative_to->nanoseconds(), isolate), handle(relative_to->time_zone(), isolate), handle(relative_to->calendar(), isolate), {dur.years, dur.months, dur.weeks, {dur.days, 0, 0, 0, 0, 0, 0}}, method_name), Nothing<int64_t>()); // 5. Let instantAfter be ! CreateTemporalInstant(after). Handle<JSTemporalInstant> instant_after = temporal::CreateTemporalInstant(isolate, after).ToHandleChecked(); // 6. Let offsetAfter be ? GetOffsetNanosecondsFor(relativeTo.[[TimeZone]], // instantAfter). int64_t offset_after; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_after, GetOffsetNanosecondsFor(isolate, handle(relative_to->time_zone(), isolate), instant_after, method_name), Nothing<int64_t>()); // 7. Return offsetAfter − offsetBefore return Just(offset_after - offset_before); } // #sec-temporal-moverelativedate struct MoveRelativeDateResult { Handle<JSTemporalPlainDate> relative_to; double days; }; Maybe<MoveRelativeDateResult> MoveRelativeDate( Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSTemporalPlainDate> relative_to, Handle<JSTemporalDuration> duration, const char* method_name); // #sec-temporal-unbalancedurationrelative Maybe<DateDurationRecord> UnbalanceDurationRelative( Isolate* isolate, const DateDurationRecord& dur, Unit largest_unit, Handle<Object> relative_to_obj, const char* method_name) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 1. If largestUnit is "year", or years, months, weeks, and days are all 0, // then if (largest_unit == Unit::kYear || (dur.years == 0 && dur.months == 0 && dur.weeks == 0 && dur.days == 0)) { // a. Return ! CreateDateDurationRecord(years, months, weeks, days). return Just(DateDurationRecord::Create(isolate, dur.years, dur.months, dur.weeks, dur.days) .ToChecked()); } // 2. Let sign be ! DurationSign(years, months, weeks, days, 0, 0, 0, 0, 0, // 0). double sign = DurationSign( isolate, {dur.years, dur.months, dur.weeks, {dur.days, 0, 0, 0, 0, 0, 0}}); // 3. Assert: sign ≠ 0. DCHECK_NE(sign, 0); // 4. Let oneYear be ! CreateTemporalDuration(sign, 0, 0, 0, 0, 0, 0, 0, 0, // 0). Handle<JSTemporalDuration> one_year = CreateTemporalDuration(isolate, {sign, 0, 0, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // 5. Let oneMonth be ! CreateTemporalDuration(0, sign, 0, 0, 0, 0, 0, 0, 0, // 0). Handle<JSTemporalDuration> one_month = CreateTemporalDuration(isolate, {0, sign, 0, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // 6. Let oneWeek be ! CreateTemporalDuration(0, 0, sign, 0, 0, 0, 0, 0, 0, // 0). Handle<JSTemporalDuration> one_week = CreateTemporalDuration(isolate, {0, 0, sign, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // 7. If relativeTo is not undefined, then Handle<JSTemporalPlainDate> relative_to; Handle<JSReceiver> calendar; if (!IsUndefined(*relative_to_obj)) { // a. Set relativeTo to ? ToTemporalDate(relativeTo). ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, relative_to, ToTemporalDate(isolate, relative_to_obj, method_name), Nothing<DateDurationRecord>()); // b. Let calendar be relativeTo.[[Calendar]]. calendar = handle(relative_to->calendar(), isolate); // 8. Else, } else { // a. Let calendar be undefined. } DateDurationRecord result = dur; // 9. If largestUnit is "month", then if (largest_unit == Unit::kMonth) { // a. If calendar is undefined, then if (calendar.is_null()) { // i. Throw a RangeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateDurationRecord>()); } // b. Let dateAdd be ? GetMethod(calendar, "dateAdd"). Handle<Object> date_add; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_add, Object::GetMethod(isolate, calendar, factory->dateAdd_string()), Nothing<DateDurationRecord>()); // c. Let dateUntil be ? GetMethod(calendar, "dateUntil"). Handle<Object> date_until; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_until, Object::GetMethod(isolate, calendar, factory->dateUntil_string()), Nothing<DateDurationRecord>()); // d. Repeat, while years ≠ 0, while (result.years != 0) { // i. Let newRelativeTo be ? CalendarDateAdd(calendar, relativeTo, // oneYear, undefined, dateAdd). Handle<JSTemporalPlainDate> new_relative_to; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, new_relative_to, CalendarDateAdd(isolate, calendar, relative_to, one_year, factory->undefined_value(), date_add), Nothing<DateDurationRecord>()); // ii. Let untilOptions be ! OrdinaryObjectCreate(null). Handle<JSObject> until_options = factory->NewJSObjectWithNullProto(); // iii. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", // "month"). CHECK(JSReceiver::CreateDataProperty( isolate, until_options, factory->largestUnit_string(), factory->month_string(), Just(kThrowOnError)) .FromJust()); // iv. Let untilResult be ? CalendarDateUntil(calendar, relativeTo, // newRelativeTo, untilOptions, dateUntil). Handle<JSTemporalDuration> until_result; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, until_result, CalendarDateUntil(isolate, calendar, relative_to, new_relative_to, until_options, date_until), Nothing<DateDurationRecord>()); // v. Let oneYearMonths be untilResult.[[Months]]. double one_year_months = Object::Number(until_result->months()); // vi. Set relativeTo to newRelativeTo. relative_to = new_relative_to; // vii. Set years to years − sign. result.years -= sign; // viii. Set months to months + oneYearMonths. result.months += one_year_months; } // 10. Else if largestUnit is "week", then } else if (largest_unit == Unit::kWeek) { // a. If calendar is undefined, then if (calendar.is_null()) { // i. Throw a RangeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateDurationRecord>()); } // b. Repeat, while years ≠ 0, while (result.years != 0) { // i. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneYear). MoveRelativeDateResult move_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_year, method_name), Nothing<DateDurationRecord>()); // ii. Set relativeTo to moveResult.[[RelativeTo]]. relative_to = move_result.relative_to; // iii. Set days to days + moveResult.[[Days]]. result.days += move_result.days; // iv. Set years to years - sign. result.years -= sign; } // c. Repeat, while months ≠ 0, while (result.months != 0) { // i. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, // oneMonth). MoveRelativeDateResult move_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_month, method_name), Nothing<DateDurationRecord>()); // ii. Set relativeTo to moveResult.[[RelativeTo]]. relative_to = move_result.relative_to; // iii. Set days to days + moveResult.[[Days]]. result.days += move_result.days; // iv. Set months to months - sign. result.months -= sign; } // 11. Else, } else { // a. If any of years, months, and weeks are not zero, then if ((result.years != 0) || (result.months != 0) || (result.weeks != 0)) { // i. If calendar is undefined, then if (calendar.is_null()) { // i. Throw a RangeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateDurationRecord>()); } // b. Repeat, while years ≠ 0, while (result.years != 0) { // i. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, // oneYear). MoveRelativeDateResult move_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_year, method_name), Nothing<DateDurationRecord>()); // ii. Set relativeTo to moveResult.[[RelativeTo]]. relative_to = move_result.relative_to; // iii. Set days to days + moveResult.[[Days]]. result.days += move_result.days; // iv. Set years to years - sign. result.years -= sign; } // c. Repeat, while months ≠ 0, while (result.months != 0) { // i. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, // oneMonth). MoveRelativeDateResult move_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_month, method_name), Nothing<DateDurationRecord>()); // ii. Set relativeTo to moveResult.[[RelativeTo]]. relative_to = move_result.relative_to; // iii. Set days to days + moveResult.[[Days]]. result.days += move_result.days; // iv. Set months to years - sign. result.months -= sign; } // d. Repeat, while weeks ≠ 0, while (result.weeks != 0) { // i. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, // oneWeek). MoveRelativeDateResult move_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_week, method_name), Nothing<DateDurationRecord>()); // ii. Set relativeTo to moveResult.[[RelativeTo]]. relative_to = move_result.relative_to; // iii. Set days to days + moveResult.[[Days]]. result.days += move_result.days; // iv. Set weeks to years - sign. result.weeks -= sign; } } } // 12. Return ? CreateDateDurationRecord(years, months, weeks, days). return DateDurationRecord::Create(isolate, result.years, result.months, result.weeks, result.days); } // #sec-temporal-balancedurationrelative Maybe<DateDurationRecord> BalanceDurationRelative( Isolate* isolate, const DateDurationRecord& dur, Unit largest_unit, Handle<Object> relative_to_obj, const char* method_name) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 1. If largestUnit is not one of "year", "month", or "week", or years, // months, weeks, and days are all 0, then if ((largest_unit != Unit::kYear && largest_unit != Unit::kMonth && largest_unit != Unit::kWeek) || (dur.years == 0 && dur.months == 0 && dur.weeks == 0 && dur.days == 0)) { // a. Return ! CreateDateDurationRecord(years, months, weeks, days). return Just(DateDurationRecord::Create(isolate, dur.years, dur.months, dur.weeks, dur.days) .ToChecked()); } // 2. If relativeTo is undefined, then if (IsUndefined(*relative_to_obj)) { // a. Throw a RangeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateDurationRecord>()); } // 3. Let sign be ! DurationSign(years, months, weeks, days, 0, 0, 0, 0, 0, // 0). double sign = DurationSign( isolate, {dur.years, dur.months, dur.weeks, {dur.days, 0, 0, 0, 0, 0, 0}}); // 4. Assert: sign ≠ 0. DCHECK_NE(sign, 0); // 5. Let oneYear be ! CreateTemporalDuration(sign, 0, 0, 0, 0, 0, 0, 0, 0, // 0). Handle<JSTemporalDuration> one_year = CreateTemporalDuration(isolate, {sign, 0, 0, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // 6. Let oneMonth be ! CreateTemporalDuration(0, sign, 0, 0, 0, 0, 0, 0, 0, // 0). Handle<JSTemporalDuration> one_month = CreateTemporalDuration(isolate, {0, sign, 0, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // 7. Let oneWeek be ! CreateTemporalDuration(0, 0, sign, 0, 0, 0, 0, 0, 0, // 0). Handle<JSTemporalDuration> one_week = CreateTemporalDuration(isolate, {0, 0, sign, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // 8. Set relativeTo to ? ToTemporalDate(relativeTo). Handle<JSTemporalPlainDate> relative_to; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, relative_to, ToTemporalDate(isolate, relative_to_obj, method_name), Nothing<DateDurationRecord>()); // 9. Let calendar be relativeTo.[[Calendar]]. Handle<JSReceiver> calendar(relative_to->calendar(), isolate); DateDurationRecord result = dur; // 10. If largestUnit is "year", then if (largest_unit == Unit::kYear) { // a. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneYear). MoveRelativeDateResult move_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_year, method_name), Nothing<DateDurationRecord>()); // b. Let newRelativeTo be moveResult.[[RelativeTo]]. Handle<JSTemporalPlainDate> new_relative_to = move_result.relative_to; // c. Let oneYearDays be moveResult.[[Days]]. double one_year_days = move_result.days; // d. Repeat, while abs(days) ≥ abs(oneYearDays), while (std::abs(result.days) >= std::abs(one_year_days)) { // i. Set days to days - oneYearDays. result.days -= one_year_days; // ii. Set years to years + sign. result.years += sign; // iii. Set relativeTo to newRelativeTo. relative_to = new_relative_to; // iv. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, // oneYear). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_year, method_name), Nothing<DateDurationRecord>()); // iv. Set newRelativeTo to moveResult.[[RelativeTo]]. new_relative_to = move_result.relative_to; // v. Set oneYearDays to moveResult.[[Days]]. one_year_days = move_result.days; } // e. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, oneMonth). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_month, method_name), Nothing<DateDurationRecord>()); // f. Set newRelativeTo to moveResult.[[RelativeTo]]. new_relative_to = move_result.relative_to; // g. Let oneMonthDays be moveResult.[[Days]]. double one_month_days = move_result.days; // h. Repeat, while abs(days) ≥ abs(oneMonthDays), while (std::abs(result.days) >= std::abs(one_month_days)) { // i. Set days to days - oneMonthDays. result.days -= one_month_days; // ii. Set months to months + sign. result.months += sign; // iii. Set relativeTo to newRelativeTo. relative_to = new_relative_to; // iv. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, // oneMonth). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_month, method_name), Nothing<DateDurationRecord>()); // iv. Set newRrelativeTo to moveResult.[[RelativeTo]]. new_relative_to = move_result.relative_to; // v. Set oneMonthDays to moveResult.[[Days]]. one_month_days = move_result.days; } // i. Let dateAdd be ? GetMethod(calendar, "dateAdd"). Handle<Object> date_add; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_add, Object::GetMethod(isolate, calendar, factory->dateAdd_string()), Nothing<DateDurationRecord>()); // j. Set newRelativeTo be ? CalendarDateAdd(calendar, relativeTo, oneYear, // undefined, dateAdd). ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, new_relative_to, CalendarDateAdd(isolate, calendar, relative_to, one_year, factory->undefined_value(), date_add), Nothing<DateDurationRecord>()); // k. Let dateUntil be ? GetMethod(calendar, "dateUntil"). Handle<Object> date_until; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_until, Object::GetMethod(isolate, calendar, factory->dateUntil_string()), Nothing<DateDurationRecord>()); // l. Let untilOptions be OrdinaryObjectCreate(null). Handle<JSObject> until_options = factory->NewJSObjectWithNullProto(); // m. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", // "month"). CHECK(JSReceiver::CreateDataProperty( isolate, until_options, factory->largestUnit_string(), factory->month_string(), Just(kThrowOnError)) .FromJust()); // n. Let untilResult be ? CalendarDateUntil(calendar, relativeTo, // newRelativeTo, untilOptions, dateUntil). Handle<JSTemporalDuration> until_result; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, until_result, CalendarDateUntil(isolate, calendar, relative_to, new_relative_to, until_options, date_until), Nothing<DateDurationRecord>()); // o. Let oneYearMonths be untilResult.[[Months]]. double one_year_months = Object::Number(until_result->months()); // p. Repeat, while abs(months) ≥ abs(oneYearMonths), while (std::abs(result.months) >= std::abs(one_year_months)) { // i. Set months to months - oneYearMonths. result.months -= one_year_months; // ii. Set years to years + sign. result.years += sign; // iii. Set relativeTo to newRelativeTo. relative_to = new_relative_to; // iv. Set newRelativeTo to ? CalendarDateAdd(calendar, relativeTo, // oneYear, undefined, dateAdd). ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, new_relative_to, CalendarDateAdd(isolate, calendar, relative_to, one_year, factory->undefined_value(), date_add), Nothing<DateDurationRecord>()); // v. Set untilOptions to OrdinaryObjectCreate(null). until_options = factory->NewJSObjectWithNullProto(); // vi. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", // "month"). CHECK(JSReceiver::CreateDataProperty( isolate, until_options, factory->largestUnit_string(), factory->month_string(), Just(kThrowOnError)) .FromJust()); // vii. Set untilResult to ? CalendarDateUntil(calendar, relativeTo, // newRelativeTo, untilOptions, dateUntil). ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, until_result, CalendarDateUntil(isolate, calendar, relative_to, new_relative_to, until_options, date_until), Nothing<DateDurationRecord>()); // viii. Set oneYearMonths to untilResult.[[Months]]. one_year_months = Object::Number(until_result->months()); } // 11. Else if largestUnit is "month", then } else if (largest_unit == Unit::kMonth) { // a. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneMonth). MoveRelativeDateResult move_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_month, method_name), Nothing<DateDurationRecord>()); // b. Let newRelativeTo be moveResult.[[RelativeTo]]. Handle<JSTemporalPlainDate> new_relative_to = move_result.relative_to; // c. Let oneMonthDays be moveResult.[[Days]]. double one_month_days = move_result.days; // d. Repeat, while abs(days) ≥ abs(oneMonthDays), while (std::abs(result.days) >= std::abs(one_month_days)) { // i. Set days to days - oneMonthDays. result.days -= one_month_days; // ii. Set months to months + sign. result.months += sign; // iii. Set relativeTo to newRelativeTo. relative_to = new_relative_to; // iv. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, // oneMonth). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_month, method_name), Nothing<DateDurationRecord>()); // v. Set newRelativeTo to moveResult.[[RelativeTo]]. new_relative_to = move_result.relative_to; // vi. Set oneMonthDays to moveResult.[[Days]]. one_month_days = move_result.days; } // 12. Else } else { // a. Assert: largestUnit is "week". DCHECK_EQ(largest_unit, Unit::kWeek); // b. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneWeek). MoveRelativeDateResult move_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_week, method_name), Nothing<DateDurationRecord>()); // c. Let newRelativeTo be moveResult.[[RelativeTo]]. Handle<JSTemporalPlainDate> new_relative_to = move_result.relative_to; // d. Let oneWeekDays be moveResult.[[Days]]. double one_week_days = move_result.days; // e. Repeat, while abs(days) ≥ abs(oneWeekDays), while (std::abs(result.days) >= std::abs(one_week_days)) { // i. Set days to days - oneWeekDays. result.days -= one_week_days; // ii. Set weeks to weeks + sign. result.weeks += sign; // iii. Set relativeTo to newRelativeTo. relative_to = new_relative_to; // v. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, // oneWeek). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, relative_to, one_week, method_name), Nothing<DateDurationRecord>()); // v. Set newRelativeTo to moveResult.[[RelativeTo]]. new_relative_to = move_result.relative_to; // vi. Set oneWeekDays to moveResult.[[Days]]. one_week_days = move_result.days; } } // 12. Return ? CreateDateDurationRecord(years, months, weeks, days). return DateDurationRecord::Create(isolate, result.years, result.months, result.weeks, result.days); } } // namespace // #sec-temporal.duration.compare MaybeHandle<Smi> JSTemporalDuration::Compare(Isolate* isolate, Handle<Object> one_obj, Handle<Object> two_obj, Handle<Object> options_obj) { const char* method_name = "Temporal.Duration.compare"; // 1. Set one to ? ToTemporalDuration(one). Handle<JSTemporalDuration> one; ASSIGN_RETURN_ON_EXCEPTION( isolate, one, temporal::ToTemporalDuration(isolate, one_obj, method_name), Smi); // 2. Set two to ? ToTemporalDuration(two). Handle<JSTemporalDuration> two; ASSIGN_RETURN_ON_EXCEPTION( isolate, two, temporal::ToTemporalDuration(isolate, two_obj, method_name), Smi); // 3. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), Smi); // 4. Let relativeTo be ? ToRelativeTemporalObject(options). Handle<Object> relative_to; ASSIGN_RETURN_ON_EXCEPTION( isolate, relative_to, ToRelativeTemporalObject(isolate, options, method_name), Smi); // 5. LetCalculateOffsetShift shift1 be ? CalculateOffsetShift(relativeTo, // one.[[Years]], one.[[Months]], one.[[Weeks]], one.[[Days]]). int64_t shift1; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, shift1, CalculateOffsetShift( isolate, relative_to, {Object::Number(one->years()), Object::Number(one->months()), Object::Number(one->weeks()), Object::Number(one->days())}, method_name), Handle<Smi>()); // 6. Let shift2 be ? CalculateOffsetShift(relativeTo, two.[[Years]], // two.[[Months]], two.[[Weeks]], two.[[Days]]). int64_t shift2; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, shift2, CalculateOffsetShift( isolate, relative_to, {Object::Number(two->years()), Object::Number(two->months()), Object::Number(two->weeks()), Object::Number(two->days())}, method_name), Handle<Smi>()); // 7. If any of one.[[Years]], two.[[Years]], one.[[Months]], two.[[Months]], // one.[[Weeks]], or two.[[Weeks]] are not 0, then double days1, days2; if (Object::Number(one->years()) != 0 || Object::Number(two->years()) != 0 || Object::Number(one->months()) != 0 || Object::Number(two->months()) != 0 || Object::Number(one->weeks()) != 0 || Object::Number(two->weeks()) != 0) { // a. Let unbalanceResult1 be ? UnbalanceDurationRelative(one.[[Years]], // one.[[Months]], one.[[Weeks]], one.[[Days]], "day", relativeTo). DateDurationRecord unbalance_result1; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, unbalance_result1, UnbalanceDurationRelative( isolate, {Object::Number(one->years()), Object::Number(one->months()), Object::Number(one->weeks()), Object::Number(one->days())}, Unit::kDay, relative_to, method_name), Handle<Smi>()); // b. Let unbalanceResult2 be ? UnbalanceDurationRelative(two.[[Years]], // two.[[Months]], two.[[Weeks]], two.[[Days]], "day", relativeTo). DateDurationRecord unbalance_result2; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, unbalance_result2, UnbalanceDurationRelative( isolate, {Object::Number(two->years()), Object::Number(two->months()), Object::Number(two->weeks()), Object::Number(two->days())}, Unit::kDay, relative_to, method_name), Handle<Smi>()); // c. Let days1 be unbalanceResult1.[[Days]]. days1 = unbalance_result1.days; // d. Let days2 be unbalanceResult2.[[Days]]. days2 = unbalance_result2.days; // 8. Else, } else { // a. Let days1 be one.[[Days]]. days1 = Object::Number(one->days()); // b. Let days2 be two.[[Days]]. days2 = Object::Number(two->days()); } // 9. Let ns1 be ! TotalDurationNanoseconds(days1, one.[[Hours]], // one.[[Minutes]], one.[[Seconds]], one.[[Milliseconds]], // one.[[Microseconds]], one.[[Nanoseconds]], shift1). Handle<BigInt> ns1 = TotalDurationNanoseconds( isolate, {days1, Object::Number(one->hours()), Object::Number(one->minutes()), Object::Number(one->seconds()), Object::Number(one->milliseconds()), Object::Number(one->microseconds()), Object::Number(one->nanoseconds())}, shift1); // 10. Let ns2 be ! TotalDurationNanoseconds(days2, two.[[Hours]], // two.[[Minutes]], two.[[Seconds]], two.[[Milliseconds]], // two.[[Microseconds]], two.[[Nanoseconds]], shift2). Handle<BigInt> ns2 = TotalDurationNanoseconds( isolate, {days2, Object::Number(two->hours()), Object::Number(two->minutes()), Object::Number(two->seconds()), Object::Number(two->milliseconds()), Object::Number(two->microseconds()), Object::Number(two->nanoseconds())}, shift2); switch (BigInt::CompareToBigInt(ns1, ns2)) { // 11. If ns1 > ns2, return 1𝔽. case ComparisonResult::kGreaterThan: return handle(Smi::FromInt(1), isolate); // 12. If ns1 < ns2, return -1𝔽. case ComparisonResult::kLessThan: return handle(Smi::FromInt(-1), isolate); // 13. Return +0𝔽. default: return handle(Smi::FromInt(0), isolate); } } // #sec-temporal.duration.from MaybeHandle<JSTemporalDuration> JSTemporalDuration::From(Isolate* isolate, Handle<Object> item) { // 1. If Type(item) is Object and item has an [[InitializedTemporalDuration]] // internal slot, then if (IsJSTemporalDuration(*item)) { // a. Return ? CreateTemporalDuration(item.[[Years]], item.[[Months]], // item.[[Weeks]], item.[[Days]], item.[[Hours]], item.[[Minutes]], // item.[[Seconds]], item.[[Milliseconds]], item.[[Microseconds]], // item.[[Nanoseconds]]). Handle<JSTemporalDuration> duration = Handle<JSTemporalDuration>::cast(item); return CreateTemporalDuration( isolate, {Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), {Object::Number(duration->days()), Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())}}); } // 2. Return ? ToTemporalDuration(item). return temporal::ToTemporalDuration(isolate, item, "Temporal.Duration.from"); } namespace { // #sec-temporal-maximumtemporaldurationroundingincrement struct Maximum { bool defined; double value; }; Maximum MaximumTemporalDurationRoundingIncrement(Unit unit); // #sec-temporal-totemporalroundingincrement Maybe<double> ToTemporalRoundingIncrement(Isolate* isolate, Handle<JSReceiver> normalized_options, double dividend, bool dividend_is_defined, bool inclusive); // #sec-temporal-moverelativezoneddatetime MaybeHandle<JSTemporalZonedDateTime> MoveRelativeZonedDateTime( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, const DateDurationRecord& duration, const char* method_name); // #sec-temporal-roundduration Maybe<DurationRecordWithRemainder> RoundDuration(Isolate* isolate, const DurationRecord& duration, double increment, Unit unit, RoundingMode rounding_mode, Handle<Object> relative_to, const char* method_name); } // namespace // #sec-temporal.duration.prototype.round MaybeHandle<JSTemporalDuration> JSTemporalDuration::Round( Isolate* isolate, Handle<JSTemporalDuration> duration, Handle<Object> round_to_obj) { const char* method_name = "Temporal.Duration.prototype.round"; Factory* factory = isolate->factory(); // 1. Let duration be the this value. // 2. Perform ? RequireInternalSlot(duration, // [[InitializedTemporalDuration]]). // 3. If roundTo is undefined, then if (IsUndefined(*round_to_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalDuration); } Handle<JSReceiver> round_to; // 4. If Type(roundTo) is String, then if (IsString(*round_to_obj)) { // a. Let paramString be roundTo. Handle<String> param_string = Handle<String>::cast(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), JSTemporalDuration); } // 6. Let smallestUnitPresent be true. bool smallest_unit_present = true; // 7. Let largestUnitPresent be true. bool largest_unit_present = true; // 8. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", datetime, // undefined). Unit smallest_unit; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, smallest_unit, GetTemporalUnit(isolate, round_to, "smallestUnit", UnitGroup::kDateTime, Unit::kNotPresent, false, method_name), Handle<JSTemporalDuration>()); // 9. If smallestUnit is undefined, then if (smallest_unit == Unit::kNotPresent) { // a. Set smallestUnitPresent to false. smallest_unit_present = false; // b. Set smallestUnit to "nanosecond". smallest_unit = Unit::kNanosecond; } // 10. Let defaultLargestUnit be ! // DefaultTemporalLargestUnit(duration.[[Years]], duration.[[Months]], // duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], // duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], // duration.[[Microseconds]]). Unit default_largest_unit = DefaultTemporalLargestUnit( {Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), {Object::Number(duration->days()), Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())}}); // 11. Set defaultLargestUnit to ! // LargerOfTwoTemporalUnits(defaultLargestUnit, smallestUnit). default_largest_unit = LargerOfTwoTemporalUnits(default_largest_unit, smallest_unit); // 12. Let largestUnit be ? GetTemporalUnit(roundTo, "largestUnit", datetime, // undefined, « "auto" »). Unit largest_unit; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, largest_unit, GetTemporalUnit(isolate, round_to, "largestUnit", UnitGroup::kDateTime, Unit::kNotPresent, false, method_name, Unit::kAuto), Handle<JSTemporalDuration>()); // 13. If largestUnit is undefined, then if (largest_unit == Unit::kNotPresent) { // a. Set largestUnitPresent to false. largest_unit_present = false; // b. Set largestUnit to defaultLargestUnit. largest_unit = default_largest_unit; // 14. Else if largestUnit is "auto", then } else if (largest_unit == Unit::kAuto) { // a. Set largestUnit to defaultLargestUnit. largest_unit = default_largest_unit; } // 15. If smallestUnitPresent is false and largestUnitPresent is false, then if (!smallest_unit_present && !largest_unit_present) { // a. Throw a RangeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalDuration); } // 16. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not // largestUnit, throw a RangeError exception. if (LargerOfTwoTemporalUnits(largest_unit, smallest_unit) != largest_unit) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalDuration); } // 17. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand"). RoundingMode rounding_mode; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_mode, ToTemporalRoundingMode(isolate, round_to, RoundingMode::kHalfExpand, method_name), Handle<JSTemporalDuration>()); // 18. Let maximum be ! // MaximumTemporalDurationRoundingIncrement(smallestUnit). Maximum maximum = MaximumTemporalDurationRoundingIncrement(smallest_unit); // 19. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo, // maximum, false). double rounding_increment; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_increment, ToTemporalRoundingIncrement(isolate, round_to, maximum.value, maximum.defined, false), Handle<JSTemporalDuration>()); // 20. Let relativeTo be ? ToRelativeTemporalObject(roundTo). Handle<Object> relative_to; ASSIGN_RETURN_ON_EXCEPTION( isolate, relative_to, ToRelativeTemporalObject(isolate, round_to, method_name), JSTemporalDuration); // 21. Let unbalanceResult be ? UnbalanceDurationRelative(duration.[[Years]], // duration.[[Months]], duration.[[Weeks]], duration.[[Days]], largestUnit, // relativeTo). DateDurationRecord unbalance_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, unbalance_result, UnbalanceDurationRelative( isolate, {Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), Object::Number(duration->days())}, largest_unit, relative_to, method_name), Handle<JSTemporalDuration>()); // 22. Let roundResult be (? RoundDuration(unbalanceResult.[[Years]], // unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], // unbalanceResult.[[Days]], duration.[[Hours]], duration.[[Minutes]], // duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], // duration.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, // relativeTo)).[[DurationRecord]]. DurationRecordWithRemainder round_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, round_result, RoundDuration(isolate, {unbalance_result.years, unbalance_result.months, unbalance_result.weeks, {unbalance_result.days, Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())}}, rounding_increment, smallest_unit, rounding_mode, relative_to, method_name), Handle<JSTemporalDuration>()); // 23. Let adjustResult be ? AdjustRoundedDurationDays(roundResult.[[Years]], // roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], // roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], // roundResult.[[Milliseconds]], roundResult.[[Microseconds]], // roundResult.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, // relativeTo). DurationRecord adjust_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, adjust_result, AdjustRoundedDurationDays(isolate, round_result.record, rounding_increment, smallest_unit, rounding_mode, relative_to, method_name), Handle<JSTemporalDuration>()); // 24. Let balanceResult be ? BalanceDurationRelative(adjustResult.[[Years]], // adjustResult.[[Months]], adjustResult.[[Weeks]], adjustResult.[[Days]], // largestUnit, relativeTo). DateDurationRecord balance_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, balance_result, BalanceDurationRelative( isolate, {adjust_result.years, adjust_result.months, adjust_result.weeks, adjust_result.time_duration.days}, largest_unit, relative_to, method_name), Handle<JSTemporalDuration>()); // 25. If Type(relativeTo) is Object and relativeTo has an // [[InitializedTemporalZonedDateTime]] internal slot, then if (IsJSTemporalZonedDateTime(*relative_to)) { // a. Set relativeTo to ? MoveRelativeZonedDateTime(relativeTo, // balanceResult.[[Years]], balanceResult.[[Months]], // balanceResult.[[Weeks]], 0). ASSIGN_RETURN_ON_EXCEPTION( isolate, relative_to, MoveRelativeZonedDateTime( isolate, Handle<JSTemporalZonedDateTime>::cast(relative_to), {balance_result.years, balance_result.months, balance_result.weeks, 0}, method_name), JSTemporalDuration); } // 26. Let result be ? BalanceDuration(balanceResult.[[Days]], // adjustResult.[[Hours]], adjustResult.[[Minutes]], adjustResult.[[Seconds]], // adjustResult.[[Milliseconds]], adjustResult.[[Microseconds]], // adjustResult.[[Nanoseconds]], largestUnit, relativeTo). TimeDurationRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, BalanceDuration(isolate, largest_unit, relative_to, {balance_result.days, adjust_result.time_duration.hours, adjust_result.time_duration.minutes, adjust_result.time_duration.seconds, adjust_result.time_duration.milliseconds, adjust_result.time_duration.microseconds, adjust_result.time_duration.nanoseconds}, method_name), Handle<JSTemporalDuration>()); // 27. Return ! CreateTemporalDuration(balanceResult.[[Years]], // balanceResult.[[Months]], balanceResult.[[Weeks]], result.[[Days]], // result.[[Hours]], result.[[Minutes]], result.[[Seconds]], // result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). return CreateTemporalDuration(isolate, {balance_result.years, balance_result.months, balance_result.weeks, result}) .ToHandleChecked(); } // #sec-temporal.duration.prototype.total MaybeHandle<Object> JSTemporalDuration::Total( Isolate* isolate, Handle<JSTemporalDuration> duration, Handle<Object> total_of_obj) { const char* method_name = "Temporal.Duration.prototype.total"; Factory* factory = isolate->factory(); // 1. Let duration be the this value. // 2. Perform ? RequireInternalSlot(duration, // [[InitializedTemporalDuration]]). // 3. If totalOf is undefined, throw a TypeError exception. if (IsUndefined(*total_of_obj, isolate)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Object); } Handle<JSReceiver> total_of; // 4. If Type(totalOf) is String, then if (IsString(*total_of_obj)) { // a. Let paramString be totalOf. Handle<String> param_string = Handle<String>::cast(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()); } else { // 5. Set totalOf to ? GetOptionsObject(totalOf). ASSIGN_RETURN_ON_EXCEPTION( isolate, total_of, GetOptionsObject(isolate, total_of_obj, method_name), Object); } // 6. Let relativeTo be ? ToRelativeTemporalObject(totalOf). Handle<Object> relative_to; ASSIGN_RETURN_ON_EXCEPTION( isolate, relative_to, ToRelativeTemporalObject(isolate, total_of, method_name), Object); // 7. Let unit be ? GetTemporalUnit(totalOf, "unit", datetime, required). Unit unit; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, unit, GetTemporalUnit(isolate, total_of, "unit", UnitGroup::kDateTime, Unit::kNotPresent, true, method_name), Handle<Object>()); // 8. Let unbalanceResult be ? UnbalanceDurationRelative(duration.[[Years]], // duration.[[Months]], duration.[[Weeks]], duration.[[Days]], unit, // relativeTo). DateDurationRecord unbalance_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, unbalance_result, UnbalanceDurationRelative( isolate, {Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), Object::Number(duration->days())}, unit, relative_to, method_name), Handle<Object>()); // 9. Let intermediate be undefined. Handle<Object> intermediate = factory->undefined_value(); // 8. If relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, // then if (IsJSTemporalZonedDateTime(*relative_to)) { // a. Set intermediate to ? MoveRelativeZonedDateTime(relativeTo, // unbalanceResult.[[Years]], unbalanceResult.[[Months]], // unbalanceResult.[[Weeks]], 0). ASSIGN_RETURN_ON_EXCEPTION( isolate, intermediate, MoveRelativeZonedDateTime( isolate, Handle<JSTemporalZonedDateTime>::cast(relative_to), {unbalance_result.years, unbalance_result.months, unbalance_result.weeks, 0}, method_name), Object); } // 11. Let balanceResult be ? // BalancePossiblyInfiniteDuration(unbalanceResult.[[Days]], // duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], // duration.[[Milliseconds]], duration.[[Microseconds]], // duration.[[Nanoseconds]], unit, intermediate). BalancePossiblyInfiniteDurationResult balance_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, balance_result, BalancePossiblyInfiniteDuration( isolate, unit, intermediate, {unbalance_result.days, Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())}, method_name), Handle<Object>()); // 12. If balanceResult is positive overflow, return +∞𝔽. if (balance_result.overflow == BalanceOverflow::kPositive) { return factory->infinity_value(); } // 13. If balanceResult is negative overflow, return -∞𝔽. if (balance_result.overflow == BalanceOverflow::kNegative) { return factory->minus_infinity_value(); } // 14. Assert: balanceResult is a Time Duration Record. DCHECK_EQ(balance_result.overflow, BalanceOverflow::kNone); // 15. Let roundRecord be ? RoundDuration(unbalanceResult.[[Years]], // unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], // balanceResult.[[Days]], balanceResult.[[Hours]], balanceResult.[[Minutes]], // balanceResult.[[Seconds]], balanceResult.[[Milliseconds]], // balanceResult.[[Microseconds]], balanceResult.[[Nanoseconds]], 1, unit, // "trunc", relativeTo). DurationRecordWithRemainder round_record; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, round_record, RoundDuration(isolate, {unbalance_result.years, unbalance_result.months, unbalance_result.weeks, balance_result.value}, 1, unit, RoundingMode::kTrunc, relative_to, method_name), Handle<Object>()); // 16. Let roundResult be roundRecord.[[DurationRecord]]. DurationRecord& round_result = round_record.record; double whole; switch (unit) { // 17. If unit is "year", then case Unit::kYear: // a. Let whole be roundResult.[[Years]]. whole = round_result.years; break; // 18. If unit is "month", then case Unit::kMonth: // a. Let whole be roundResult.[[Months]]. whole = round_result.months; break; // 19. If unit is "week", then case Unit::kWeek: // a. Let whole be roundResult.[[Weeks]]. whole = round_result.weeks; break; // 20. If unit is "day", then case Unit::kDay: // a. Let whole be roundResult.[[Days]]. whole = round_result.time_duration.days; break; // 21. If unit is "hour", then case Unit::kHour: // a. Let whole be roundResult.[[Hours]]. whole = round_result.time_duration.hours; break; // 22. If unit is "minute", then case Unit::kMinute: // a. Let whole be roundResult.[[Minutes]]. whole = round_result.time_duration.minutes; break; // 23. If unit is "second", then case Unit::kSecond: // a. Let whole be roundResult.[[Seconds]]. whole = round_result.time_duration.seconds; break; // 24. If unit is "millisecond", then case Unit::kMillisecond: // a. Let whole be roundResult.[[Milliseconds]]. whole = round_result.time_duration.milliseconds; break; // 25. If unit is "microsecond", then case Unit::kMicrosecond: // a. Let whole be roundResult.[[Microseconds]]. whole = round_result.time_duration.microseconds; break; // 26. If unit is "naoosecond", then case Unit::kNanosecond: // a. Let whole be roundResult.[[Nanoseconds]]. whole = round_result.time_duration.nanoseconds; break; default: UNREACHABLE(); } // 27. Return 𝔽(whole + roundRecord.[[Remainder]]). return factory->NewNumber(whole + round_record.remainder); } namespace temporal { // #sec-temporal-topartialduration Maybe<DurationRecord> ToPartialDuration( Isolate* isolate, Handle<Object> temporal_duration_like_obj, const DurationRecord& input) { // 1. If Type(temporalDurationLike) is not Object, then if (!IsJSReceiver(*temporal_duration_like_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Nothing<DurationRecord>()); } Handle<JSReceiver> temporal_duration_like = Handle<JSReceiver>::cast(temporal_duration_like_obj); // 2. Let result be a new partial Duration Record with each field set to // undefined. DurationRecord result = input; // 3. Let any be false. bool any = false; // Table 8: Duration Record Fields // #table-temporal-duration-record-fields // 4. For each row of Table 8, except the header row, in table order, do MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, any, IterateDurationRecordFieldsTable( isolate, temporal_duration_like, [](Isolate* isolate, Handle<JSReceiver> temporal_duration_like, Handle<String> prop, double* field) -> Maybe<bool> { bool not_undefined = false; // a. Let prop be the Property value of the current row. Handle<Object> val; // b. Let val be ? Get(temporalDurationLike, prop). ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, val, JSReceiver::GetProperty(isolate, temporal_duration_like, prop), Nothing<bool>()); // c. If val is not undefined, then if (!IsUndefined(*val)) { // i. Set any to true. not_undefined = true; // ii. Let val be 𝔽(? ToIntegerWithoutRounding(val)). // iii. Set result's field whose name is the Field Name value of // the current row to val. MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, *field, ToIntegerWithoutRounding(isolate, val), Nothing<bool>()); } return Just(not_undefined); }, &result), Nothing<DurationRecord>()); // 5. If any is false, then if (!any) { // a. Throw a TypeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Nothing<DurationRecord>()); } // 6. Return result. return Just(result); } } // namespace temporal // #sec-temporal.duration.prototype.with MaybeHandle<JSTemporalDuration> JSTemporalDuration::With( Isolate* isolate, Handle<JSTemporalDuration> duration, Handle<Object> temporal_duration_like) { DurationRecord partial; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, partial, temporal::ToPartialDuration( isolate, temporal_duration_like, {Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), {Object::Number(duration->days()), Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())}}), Handle<JSTemporalDuration>()); // 24. Return ? CreateTemporalDuration(years, months, weeks, days, hours, // minutes, seconds, milliseconds, microseconds, nanoseconds). return CreateTemporalDuration(isolate, partial); } // #sec-get-temporal.duration.prototype.sign MaybeHandle<Smi> JSTemporalDuration::Sign(Isolate* isolate, Handle<JSTemporalDuration> duration) { // 1. Let duration be the this value. // 2. Perform ? RequireInternalSlot(duration, // [[InitializedTemporalDuration]]). // 3. Return ! DurationSign(duration.[[Years]], duration.[[Months]], // duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], // duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], // duration.[[Microseconds]], duration.[[Nanoseconds]]). return handle(Smi::FromInt(DurationSign( isolate, {Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), {Object::Number(duration->days()), Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())}})), isolate); } // #sec-get-temporal.duration.prototype.blank MaybeHandle<Oddball> JSTemporalDuration::Blank( Isolate* isolate, Handle<JSTemporalDuration> duration) { // 1. Let duration be the this value. // 2. Perform ? RequireInternalSlot(duration, // [[InitializedTemporalDuration]]). // 3. Let sign be ! DurationSign(duration.[[Years]], duration.[[Months]], // duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], // duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], // duration.[[Microseconds]], duration.[[Nanoseconds]]). // 4. If sign = 0, return true. // 5. Return false. int32_t sign = DurationSign( isolate, {Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), {Object::Number(duration->days()), Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())}}); return isolate->factory()->ToBoolean(sign == 0); } namespace { // #sec-temporal-createnegateddurationrecord // see https://github.com/tc39/proposal-temporal/pull/2281 Maybe<DurationRecord> CreateNegatedDurationRecord( Isolate* isolate, const DurationRecord& duration) { return CreateDurationRecord( isolate, {-duration.years, -duration.months, -duration.weeks, {-duration.time_duration.days, -duration.time_duration.hours, -duration.time_duration.minutes, -duration.time_duration.seconds, -duration.time_duration.milliseconds, -duration.time_duration.microseconds, -duration.time_duration.nanoseconds}}); } // #sec-temporal-createnegatedtemporalduration MaybeHandle<JSTemporalDuration> CreateNegatedTemporalDuration( Isolate* isolate, Handle<JSTemporalDuration> duration) { TEMPORAL_ENTER_FUNC(); // 1. Assert: Type(duration) is Object. // 2. Assert: duration has an [[InitializedTemporalDuration]] internal slot. // 3. Return ! CreateTemporalDuration(−duration.[[Years]], // −duration.[[Months]], −duration.[[Weeks]], −duration.[[Days]], // −duration.[[Hours]], −duration.[[Minutes]], −duration.[[Seconds]], // −duration.[[Milliseconds]], −duration.[[Microseconds]], // −duration.[[Nanoseconds]]). return CreateTemporalDuration(isolate, {-Object::Number(duration->years()), -Object::Number(duration->months()), -Object::Number(duration->weeks()), {-Object::Number(duration->days()), -Object::Number(duration->hours()), -Object::Number(duration->minutes()), -Object::Number(duration->seconds()), -Object::Number(duration->milliseconds()), -Object::Number(duration->microseconds()), -Object::Number(duration->nanoseconds())}}) .ToHandleChecked(); } } // namespace // #sec-temporal.duration.prototype.negated MaybeHandle<JSTemporalDuration> JSTemporalDuration::Negated( Isolate* isolate, Handle<JSTemporalDuration> duration) { // Let duration be the this value. // 2. Perform ? RequireInternalSlot(duration, // [[InitializedTemporalDuration]]). // 3. Return ! CreateNegatedTemporalDuration(duration). return CreateNegatedTemporalDuration(isolate, duration).ToHandleChecked(); } // #sec-temporal.duration.prototype.abs MaybeHandle<JSTemporalDuration> JSTemporalDuration::Abs( Isolate* isolate, Handle<JSTemporalDuration> duration) { // 1. Let duration be the this value. // 2. Perform ? RequireInternalSlot(duration, // [[InitializedTemporalDuration]]). // 3. Return ? CreateTemporalDuration(abs(duration.[[Years]]), // abs(duration.[[Months]]), abs(duration.[[Weeks]]), abs(duration.[[Days]]), // abs(duration.[[Hours]]), abs(duration.[[Minutes]]), // abs(duration.[[Seconds]]), abs(duration.[[Milliseconds]]), // abs(duration.[[Microseconds]]), abs(duration.[[Nanoseconds]])). return CreateTemporalDuration( isolate, {std::abs(Object::Number(duration->years())), std::abs(Object::Number(duration->months())), std::abs(Object::Number(duration->weeks())), {std::abs(Object::Number(duration->days())), std::abs(Object::Number(duration->hours())), std::abs(Object::Number(duration->minutes())), std::abs(Object::Number(duration->seconds())), std::abs(Object::Number(duration->milliseconds())), std::abs(Object::Number(duration->microseconds())), std::abs(Object::Number(duration->nanoseconds()))}}); } namespace { // #sec-temporal-interpretisodatetimeoffset MaybeHandle<BigInt> InterpretISODateTimeOffset( Isolate* isolate, const DateTimeRecord& data, OffsetBehaviour offset_behaviour, int64_t offset_nanoseconds, Handle<JSReceiver> time_zone, Disambiguation disambiguation, Offset offset_option, MatchBehaviour match_behaviour, const char* method_name); // #sec-temporal-interprettemporaldatetimefields Maybe<temporal::DateTimeRecord> InterpretTemporalDateTimeFields( Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSReceiver> fields, Handle<Object> options, const char* method_name); // #sec-temporal-torelativetemporalobject MaybeHandle<Object> ToRelativeTemporalObject(Isolate* isolate, Handle<JSReceiver> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 1. Assert: Type(options) is Object. // 2. Let value be ? Get(options, "relativeTo"). Handle<Object> value_obj; ASSIGN_RETURN_ON_EXCEPTION( isolate, value_obj, JSReceiver::GetProperty(isolate, options, factory->relativeTo_string()), Object); // 3. If value is undefined, then if (IsUndefined(*value_obj)) { // a. Return value. return value_obj; } // 4. Let offsetBehaviour be option. OffsetBehaviour offset_behaviour = OffsetBehaviour::kOption; // 5. Let matchBehaviour be match exactly. MatchBehaviour match_behaviour = MatchBehaviour::kMatchExactly; Handle<Object> time_zone_obj = factory->undefined_value(); Handle<Object> offset_string_obj; temporal::DateTimeRecord result; Handle<JSReceiver> calendar; // 6. If Type(value) is Object, then if (IsJSReceiver(*value_obj)) { Handle<JSReceiver> value = Handle<JSReceiver>::cast(value_obj); // a. If value has either an [[InitializedTemporalDate]] or // [[InitializedTemporalZonedDateTime]] internal slot, then if (IsJSTemporalPlainDate(*value) || IsJSTemporalZonedDateTime(*value)) { // i. Return value. return value; } // b. If value has an [[InitializedTemporalDateTime]] internal slot, then if (IsJSTemporalPlainDateTime(*value)) { Handle<JSTemporalPlainDateTime> date_time_value = Handle<JSTemporalPlainDateTime>::cast(value); // i. Return ? CreateTemporalDateTime(value.[[ISOYear]], // value.[[ISOMonth]], value.[[ISODay]], // value.[[Calendar]]). return CreateTemporalDate( isolate, {date_time_value->iso_year(), date_time_value->iso_month(), date_time_value->iso_day()}, handle(date_time_value->calendar(), isolate)); } // c. Let calendar be ? GetTemporalCalendarWithISODefault(value). ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, GetTemporalCalendarWithISODefault(isolate, value, method_name), Object); // d. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", // "microsecond", "millisecond", "minute", "month", "monthCode", // "nanosecond", "second", "year" »). Handle<FixedArray> field_names = All10UnitsInFixedArray(isolate); ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, CalendarFields(isolate, calendar, field_names), Object); // e. Let fields be ? PrepareTemporalFields(value, fieldNames, «»). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, PrepareTemporalFields(isolate, value, field_names, RequiredFields::kNone), JSTemporalPlainDateTime); // f. Let dateOptions be ! OrdinaryObjectCreate(null). Handle<JSObject> date_options = factory->NewJSObjectWithNullProto(); // g. Perform ! CreateDataPropertyOrThrow(dateOptions, "overflow", // "constrain"). CHECK(JSReceiver::CreateDataProperty( isolate, date_options, factory->overflow_string(), factory->constrain_string(), Just(kThrowOnError)) .FromJust()); // h. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, // dateOptions). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, InterpretTemporalDateTimeFields(isolate, calendar, fields, date_options, method_name), Handle<Object>()); // i. Let offsetString be ? Get(value, "offset"). ASSIGN_RETURN_ON_EXCEPTION( isolate, offset_string_obj, JSReceiver::GetProperty(isolate, value, factory->offset_string()), Object); // j. Let timeZone be ? Get(value, "timeZone"). ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone_obj, JSReceiver::GetProperty(isolate, value, factory->timeZone_string()), Object); // k. If timeZone is not undefined, then if (!IsUndefined(*time_zone_obj)) { // i. Set timeZone to ? ToTemporalTimeZone(timeZone). Handle<JSReceiver> time_zone; ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone, temporal::ToTemporalTimeZone(isolate, time_zone_obj, method_name), Object); time_zone_obj = time_zone; } // l. If offsetString is undefined, then if (IsUndefined(*offset_string_obj)) { // i. Set offsetBehaviour to wall. offset_behaviour = OffsetBehaviour::kWall; } // 6. Else, } else { // a. Let string be ? ToString(value). Handle<String> string; ASSIGN_RETURN_ON_EXCEPTION(isolate, string, Object::ToString(isolate, value_obj), Object); DateTimeRecordWithCalendar parsed_result; // b. Let result be ? ParseTemporalRelativeToString(string). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, parsed_result, ParseTemporalRelativeToString(isolate, string), Handle<Object>()); result = {parsed_result.date, parsed_result.time}; // c. Let calendar be ? // ToTemporalCalendarWithISODefault(result.[[Calendar]]). ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, parsed_result.calendar, method_name), Object); // d. Let offsetString be result.[[TimeZone]].[[OffsetString]]. offset_string_obj = parsed_result.time_zone.offset_string; // e. Let timeZoneName be result.[[TimeZone]].[[Name]]. Handle<Object> time_zone_name_obj = parsed_result.time_zone.name; // f. If timeZoneName is undefined, then if (IsUndefined(*time_zone_name_obj)) { // i. Let timeZone be undefined. time_zone_obj = factory->undefined_value(); // g. Else, } else { // i. If ParseText(StringToCodePoints(timeZoneName), // TimeZoneNumericUTCOffset) is a List of errors, then DCHECK(IsString(*time_zone_name_obj)); Handle<String> time_zone_name = Handle<String>::cast(time_zone_name_obj); base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseTimeZoneNumericUTCOffset(isolate, time_zone_name); if (!parsed.has_value()) { // 1. If ! IsValidTimeZoneName(timeZoneName) is false, throw a // RangeError exception. if (!IsValidTimeZoneName(isolate, time_zone_name)) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Handle<Object>()); } // 2. Set timeZoneName to ! CanonicalizeTimeZoneName(timeZoneName). time_zone_name = CanonicalizeTimeZoneName(isolate, time_zone_name); } // ii. Let timeZone be ! CreateTemporalTimeZone(timeZoneName). Handle<JSTemporalTimeZone> time_zone = temporal::CreateTemporalTimeZone(isolate, time_zone_name) .ToHandleChecked(); time_zone_obj = time_zone; // iii. If result.[[TimeZone]].[[Z]] is true, then if (parsed_result.time_zone.z) { // 1. Set offsetBehaviour to exact. offset_behaviour = OffsetBehaviour::kExact; // iv. Else if offsetString is undefined, then } else if (IsUndefined(*offset_string_obj)) { // 1. Set offsetBehaviour to wall. offset_behaviour = OffsetBehaviour::kWall; } // v. Set matchBehaviour to match minutes. match_behaviour = MatchBehaviour::kMatchMinutes; } } // 8. If timeZone is undefined, then if (IsUndefined(*time_zone_obj)) { // a. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], // result.[[Day]], calendar). return CreateTemporalDate(isolate, result.date, calendar); } DCHECK(IsJSReceiver(*time_zone_obj)); Handle<JSReceiver> time_zone = Handle<JSReceiver>::cast(time_zone_obj); // 9. If offsetBehaviour is option, then int64_t offset_ns = 0; if (offset_behaviour == OffsetBehaviour::kOption) { // a. Set offsetString to ? ToString(offsetString). Handle<String> offset_string; ASSIGN_RETURN_ON_EXCEPTION(isolate, offset_string, Object::ToString(isolate, offset_string_obj), Object); // b. Let offsetNs be ? ParseTimeZoneOffsetString(offset_string). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_ns, ParseTimeZoneOffsetString(isolate, offset_string), Handle<Object>()); // 10. Else, } else { // a. Let offsetNs be 0. offset_ns = 0; } // 11. Let epochNanoseconds be ? InterpretISODateTimeOffset(result.[[Year]], // result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], // result.[[Nanosecond]], offsetBehaviour, offsetNs, timeZone, "compatible", // "reject", matchBehaviour). Handle<BigInt> epoch_nanoseconds; ASSIGN_RETURN_ON_EXCEPTION( isolate, epoch_nanoseconds, InterpretISODateTimeOffset(isolate, result, offset_behaviour, offset_ns, time_zone, Disambiguation::kCompatible, Offset::kReject, match_behaviour, method_name), Object); // 12. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, // calendar). return CreateTemporalZonedDateTime(isolate, epoch_nanoseconds, time_zone, calendar); } // #sec-temporal-defaulttemporallargestunit Unit DefaultTemporalLargestUnit(const DurationRecord& dur) { // 1. If years is not zero, return "year". if (dur.years != 0) return Unit::kYear; // 2. If months is not zero, return "month". if (dur.months != 0) return Unit::kMonth; // 3. If weeks is not zero, return "week". if (dur.weeks != 0) return Unit::kWeek; // 4. If days is not zero, return "day". if (dur.time_duration.days != 0) return Unit::kDay; // 5dur.. If hours is not zero, return "hour". if (dur.time_duration.hours != 0) return Unit::kHour; // 6. If minutes is not zero, return "minute". if (dur.time_duration.minutes != 0) return Unit::kMinute; // 7. If seconds is not zero, return "second". if (dur.time_duration.seconds != 0) return Unit::kSecond; // 8. If milliseconds is not zero, return "millisecond". if (dur.time_duration.milliseconds != 0) return Unit::kMillisecond; // 9. If microseconds is not zero, return "microsecond". if (dur.time_duration.microseconds != 0) return Unit::kMicrosecond; // 10. Return "nanosecond". return Unit::kNanosecond; } // #sec-temporal-differencezoneddatetime Maybe<DurationRecord> DifferenceZonedDateTime( Isolate* isolate, Handle<BigInt> ns1, Handle<BigInt> ns2, Handle<JSReceiver> time_zone, Handle<JSReceiver> calendar, Unit largest_unit, Handle<JSReceiver> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If ns1 is ns2, then if (BigInt::CompareToBigInt(ns1, ns2) == ComparisonResult::kEqual) { // a. Return ! CreateDurationRecord(0, 0, 0, 0, 0, 0, 0, 0, 0, 0). return Just(CreateDurationRecord(isolate, {0, 0, 0, {0, 0, 0, 0, 0, 0, 0}}) .ToChecked()); } // 2. Let startInstant be ! CreateTemporalInstant(ns1). Handle<JSTemporalInstant> start_instant = temporal::CreateTemporalInstant(isolate, ns1).ToHandleChecked(); // 3. Let startDateTime be ? // temporal::BuiltinTimeZoneGetPlainDateTimeFor(timeZone, startInstant, // calendar). Handle<JSTemporalPlainDateTime> start_date_time; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, start_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor( isolate, time_zone, start_instant, calendar, method_name), Nothing<DurationRecord>()); // 4. Let endInstant be ! CreateTemporalInstant(ns2). Handle<JSTemporalInstant> end_instant = temporal::CreateTemporalInstant(isolate, ns2).ToHandleChecked(); // 5. Let endDateTime be ? // temporal::BuiltinTimeZoneGetPlainDateTimeFor(timeZone, endInstant, // calendar). Handle<JSTemporalPlainDateTime> end_date_time; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, end_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor( isolate, time_zone, end_instant, calendar, method_name), Nothing<DurationRecord>()); // 6. Let dateDifference be ? DifferenceISODateTime(startDateTime.[[ISOYear]], // startDateTime.[[ISOMonth]], startDateTime.[[ISODay]], // startDateTime.[[ISOHour]], startDateTime.[[ISOMinute]], // startDateTime.[[ISOSecond]], startDateTime.[[ISOMillisecond]], // startDateTime.[[ISOMicrosecond]], startDateTime.[[ISONanosecond]], // endDateTime.[[ISOYear]], endDateTime.[[ISOMonth]], endDateTime.[[ISODay]], // endDateTime.[[ISOHour]], endDateTime.[[ISOMinute]], // endDateTime.[[ISOSecond]], endDateTime.[[ISOMillisecond]], // endDateTime.[[ISOMicrosecond]], endDateTime.[[ISONanosecond]], calendar, // largestUnit, options). DurationRecord date_difference; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_difference, DifferenceISODateTime( isolate, {{start_date_time->iso_year(), start_date_time->iso_month(), start_date_time->iso_day()}, {start_date_time->iso_hour(), start_date_time->iso_minute(), start_date_time->iso_second(), start_date_time->iso_millisecond(), start_date_time->iso_microsecond(), start_date_time->iso_nanosecond()}}, {{end_date_time->iso_year(), end_date_time->iso_month(), end_date_time->iso_day()}, {end_date_time->iso_hour(), end_date_time->iso_minute(), end_date_time->iso_second(), end_date_time->iso_millisecond(), end_date_time->iso_microsecond(), end_date_time->iso_nanosecond()}}, calendar, largest_unit, options, method_name), Nothing<DurationRecord>()); // 7. Let intermediateNs be ? AddZonedDateTime(ns1, timeZone, calendar, // dateDifference.[[Years]], dateDifference.[[Months]], // dateDifference.[[Weeks]], 0, 0, 0, 0, 0, 0, 0). Handle<BigInt> intermediate_ns; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, intermediate_ns, AddZonedDateTime(isolate, ns1, time_zone, calendar, {date_difference.years, date_difference.months, date_difference.weeks, {0, 0, 0, 0, 0, 0, 0}}, method_name), Nothing<DurationRecord>()); // 8. Let timeRemainderNs be ns2 − intermediateNs. Handle<BigInt> time_remainder_ns = BigInt::Subtract(isolate, ns2, intermediate_ns).ToHandleChecked(); // 9. Let intermediate be ? CreateTemporalZonedDateTime(intermediateNs, // timeZone, calendar). Handle<JSTemporalZonedDateTime> intermediate = CreateTemporalZonedDateTime(isolate, intermediate_ns, time_zone, calendar) .ToHandleChecked(); // 10. Let result be ? NanosecondsToDays(ℝ(timeRemainderNs), intermediate). NanosecondsToDaysResult result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, NanosecondsToDays(isolate, time_remainder_ns, intermediate, method_name), Nothing<DurationRecord>()); // 11. Let timeDifference be ! BalanceDuration(0, 0, 0, 0, 0, 0, // result.[[Nanoseconds]], "hour"). TimeDurationRecord time_difference = BalanceDuration(isolate, Unit::kHour, {0, 0, 0, 0, 0, 0, result.nanoseconds}, method_name) .ToChecked(); // 12. Return ! CreateDurationRecord(dateDifference.[[Years]], // dateDifference.[[Months]], dateDifference.[[Weeks]], result.[[Days]], // timeDifference.[[Hours]], timeDifference.[[Minutes]], // timeDifference.[[Seconds]], timeDifference.[[Milliseconds]], // timeDifference.[[Microseconds]], timeDifference.[[Nanoseconds]]). time_difference.days = result.days; return Just(CreateDurationRecord( isolate, {date_difference.years, date_difference.months, date_difference.weeks, time_difference}) .ToChecked()); } Maybe<DurationRecord> AddDuration(Isolate* isolate, const DurationRecord& dur1, const DurationRecord& dur2, Handle<Object> relative_to_obj, const char* method_name) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); DurationRecord result; // 1. Let largestUnit1 be ! DefaultTemporalLargestUnit(y1, mon1, w1, d1, h1, // min1, s1, ms1, mus1). Unit largest_unit1 = DefaultTemporalLargestUnit(dur1); // 2. Let largestUnit2 be ! DefaultTemporalLargestUnit(y2, mon2, w2, d2, h2, // min2, s2, ms2, mus2). Unit largest_unit2 = DefaultTemporalLargestUnit(dur2); // 3. Let largestUnit be ! LargerOfTwoTemporalUnits(largestUnit1, // largestUnit2). Unit largest_unit = LargerOfTwoTemporalUnits(largest_unit1, largest_unit2); // 5. If relativeTo is undefined, then if (IsUndefined(*relative_to_obj)) { // a. If largestUnit is one of "year", "month", or "week", then if (largest_unit == Unit::kYear || largest_unit == Unit::kMonth || largest_unit == Unit::kWeek) { // i. Throw a RangeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DurationRecord>()); } // b. Let result be ? BalanceDuration(d1 + d2, h1 + h2, min1 + min2, s1 + // s2, ms1 + ms2, mus1 + mus2, ns1 + ns2, largestUnit). // Note: We call a special version of BalanceDuration which add two duration // internally to avoid overflow the double. MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result.time_duration, BalanceDuration(isolate, largest_unit, dur1.time_duration, dur2.time_duration, method_name), Nothing<DurationRecord>()); // c. Return ! CreateDurationRecord(0, 0, 0, result.[[Days]], // result.[[Hours]], result.[[Minutes]], result.[[Seconds]], // result.[[Milliseconds]], result.[[Microseconds]], // result.[[Nanoseconds]]). return Just(CreateDurationRecord(isolate, {0, 0, 0, result.time_duration}) .ToChecked()); // 5. If relativeTo has an [[InitializedTemporalDate]] internal slot, then } else if (IsJSTemporalPlainDate(*relative_to_obj)) { // a. Let calendar be relativeTo.[[Calendar]]. Handle<JSTemporalPlainDate> relative_to = Handle<JSTemporalPlainDate>::cast(relative_to_obj); Handle<JSReceiver> calendar(relative_to->calendar(), isolate); // b. Let dateDuration1 be ? CreateTemporalDuration(y1, mon1, w1, d1, 0, 0, // 0, 0, 0, 0). Handle<JSTemporalDuration> date_duration1; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_duration1, CreateTemporalDuration(isolate, {dur1.years, dur1.months, dur1.weeks, {dur1.time_duration.days, 0, 0, 0, 0, 0, 0}}), Nothing<DurationRecord>()); // c. Let dateDuration2 be ? CreateTemporalDuration(y2, mon2, w2, d2, 0, 0, // 0, 0, 0, 0). Handle<JSTemporalDuration> date_duration2; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_duration2, CreateTemporalDuration(isolate, {dur2.years, dur2.months, dur2.weeks, {dur2.time_duration.days, 0, 0, 0, 0, 0, 0}}), Nothing<DurationRecord>()); // d. Let dateAdd be ? GetMethod(calendar, "dateAdd"). Handle<Object> date_add; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_add, Object::GetMethod(isolate, calendar, factory->dateAdd_string()), Nothing<DurationRecord>()); // e. Let intermediate be ? CalendarDateAdd(calendar, relativeTo, // dateDuration1, undefined, dateAdd). Handle<JSTemporalPlainDate> intermediate; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, intermediate, CalendarDateAdd(isolate, calendar, relative_to, date_duration1, factory->undefined_value(), date_add), Nothing<DurationRecord>()); // f. Let end be ? CalendarDateAdd(calendar, intermediate, dateDuration2, // undefined, dateAdd). Handle<JSTemporalPlainDate> end; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, end, CalendarDateAdd(isolate, calendar, intermediate, date_duration2, factory->undefined_value(), date_add), Nothing<DurationRecord>()); // g. Let dateLargestUnit be ! LargerOfTwoTemporalUnits("day", largestUnit). Unit date_largest_unit = LargerOfTwoTemporalUnits(Unit::kDay, largest_unit); // h. Let differenceOptions be ! OrdinaryObjectCreate(null). Handle<JSObject> difference_options = factory->NewJSObjectWithNullProto(); // i. Perform ! CreateDataPropertyOrThrow(differenceOptions, "largestUnit", // dateLargestUnit). CHECK(JSReceiver::CreateDataProperty( isolate, difference_options, factory->largestUnit_string(), UnitToString(isolate, date_largest_unit), Just(kThrowOnError)) .FromJust()); // j. Let dateDifference be ? CalendarDateUntil(calendar, relativeTo, end, // differenceOptions). Handle<JSTemporalDuration> date_difference; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_difference, CalendarDateUntil(isolate, calendar, relative_to, end, difference_options), Nothing<DurationRecord>()); // n. Let result be ? BalanceDuration(dateDifference.[[Days]], h1 + h2, min1 // + min2, s1 + s2, ms1 + ms2, mus1 + mus2, ns1 + ns2, largestUnit). // Note: We call a special version of BalanceDuration which add two duration // internally to avoid overflow the double. TimeDurationRecord time_dur1 = dur1.time_duration; time_dur1.days = Object::Number(date_difference->days()); TimeDurationRecord time_dur2 = dur2.time_duration; time_dur2.days = 0; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result.time_duration, BalanceDuration(isolate, largest_unit, time_dur1, time_dur2, method_name), Nothing<DurationRecord>()); // l. Return ! CreateDurationRecord(dateDifference.[[Years]], // dateDifference.[[Months]], dateDifference.[[Weeks]], result.[[Days]], // result.[[Hours]], result.[[Minutes]], result.[[Seconds]], // result.[[Milliseconds]], result.[[Microseconds]], // result.[[Nanoseconds]]). return Just(CreateDurationRecord(isolate, {Object::Number(date_difference->years()), Object::Number(date_difference->months()), Object::Number(date_difference->weeks()), result.time_duration}) .ToChecked()); } // 6. Assert: relativeTo has an [[InitializedTemporalZonedDateTime]] // internal slot. DCHECK(IsJSTemporalZonedDateTime(*relative_to_obj)); Handle<JSTemporalZonedDateTime> relative_to = Handle<JSTemporalZonedDateTime>::cast(relative_to_obj); // 7. Let timeZone be relativeTo.[[TimeZone]]. Handle<JSReceiver> time_zone(relative_to->time_zone(), isolate); // 8. Let calendar be relativeTo.[[Calendar]]. Handle<JSReceiver> calendar(relative_to->calendar(), isolate); // 9. Let intermediateNs be ? AddZonedDateTime(relativeTo.[[Nanoseconds]], // timeZone, calendar, y1, mon1, w1, d1, h1, min1, s1, ms1, mus1, ns1). Handle<BigInt> intermediate_ns; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, intermediate_ns, AddZonedDateTime(isolate, handle(relative_to->nanoseconds(), isolate), time_zone, calendar, dur1, method_name), Nothing<DurationRecord>()); // 10. Let endNs be ? AddZonedDateTime(intermediateNs, timeZone, calendar, // y2, mon2, w2, d2, h2, min2, s2, ms2, mus2, ns2). Handle<BigInt> end_ns; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, end_ns, AddZonedDateTime(isolate, intermediate_ns, time_zone, calendar, dur2, method_name), Nothing<DurationRecord>()); // 11. If largestUnit is not one of "year", "month", "week", or "day", then if (!(largest_unit == Unit::kYear || largest_unit == Unit::kMonth || largest_unit == Unit::kWeek || largest_unit == Unit::kDay)) { // a. Let result be ! DifferenceInstant(relativeTo.[[Nanoseconds]], endNs, // 1, *"nanosecond"*, largestUnit, *"halfExpand"*). result.time_duration = DifferenceInstant(isolate, handle(relative_to->nanoseconds(), isolate), end_ns, 1, Unit::kNanosecond, largest_unit, RoundingMode::kHalfExpand, method_name); // b. Return ! CreateDurationRecord(0, 0, 0, 0, result.[[Hours]], // result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], // result.[[Microseconds]], result.[[Nanoseconds]]). result.time_duration.days = 0; return Just(CreateDurationRecord(isolate, {0, 0, 0, result.time_duration}) .ToChecked()); } // 12. Return ? DifferenceZonedDateTime(relativeTo.[[Nanoseconds]], endNs, // timeZone, calendar, largestUnit, OrdinaryObjectCreate(null)). return DifferenceZonedDateTime( isolate, handle(relative_to->nanoseconds(), isolate), end_ns, time_zone, calendar, largest_unit, factory->NewJSObjectWithNullProto(), method_name); } MaybeHandle<JSTemporalDuration> AddDurationToOrSubtractDurationFromDuration( Isolate* isolate, Arithmetic operation, Handle<JSTemporalDuration> duration, Handle<Object> other_obj, Handle<Object> options_obj, const char* method_name) { // 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1. double sign = operation == Arithmetic::kSubtract ? -1.0 : 1.0; // 2. Set other to ? ToTemporalDurationRecord(other). DurationRecord other; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, other, temporal::ToTemporalDurationRecord(isolate, other_obj, method_name), Handle<JSTemporalDuration>()); // 3. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalDuration); // 4. Let relativeTo be ? ToRelativeTemporalObject(options). Handle<Object> relative_to; ASSIGN_RETURN_ON_EXCEPTION( isolate, relative_to, ToRelativeTemporalObject(isolate, options, method_name), JSTemporalDuration); // 5. Let result be ? AddDuration(duration.[[Years]], duration.[[Months]], // duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], // duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], // duration.[[Microseconds]], duration.[[Nanoseconds]], sign × // other.[[Years]], sign × other.[[Months]], sign × other.[[Weeks]], sign × // other.[[Days]], sign × other.[[Hours]], sign × other.[[Minutes]], sign × // other.[[Seconds]], sign × other.[[Milliseconds]], sign × // other.[[Microseconds]], sign × other.[[Nanoseconds]], relativeTo). DurationRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, AddDuration( isolate, {Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), {Object::Number(duration->days()), Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())}}, {sign * other.years, sign * other.months, sign * other.weeks, {sign * other.time_duration.days, sign * other.time_duration.hours, sign * other.time_duration.minutes, sign * other.time_duration.seconds, sign * other.time_duration.milliseconds, sign * other.time_duration.microseconds, sign * other.time_duration.nanoseconds}}, relative_to, method_name), Handle<JSTemporalDuration>()); // 6. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], // result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], // result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], // result.[[Nanoseconds]]). return CreateTemporalDuration(isolate, result).ToHandleChecked(); } } // namespace // #sec-temporal.duration.prototype.add MaybeHandle<JSTemporalDuration> JSTemporalDuration::Add( Isolate* isolate, Handle<JSTemporalDuration> duration, Handle<Object> other, Handle<Object> options) { return AddDurationToOrSubtractDurationFromDuration( isolate, Arithmetic::kAdd, duration, other, options, "Temporal.Duration.prototype.add"); } // #sec-temporal.duration.prototype.subtract MaybeHandle<JSTemporalDuration> JSTemporalDuration::Subtract( Isolate* isolate, Handle<JSTemporalDuration> duration, Handle<Object> other, Handle<Object> options) { return AddDurationToOrSubtractDurationFromDuration( isolate, Arithmetic::kSubtract, duration, other, options, "Temporal.Duration.prototype.subtract"); } // #sec-temporal.duration.prototype.tojson MaybeHandle<String> JSTemporalDuration::ToJSON( Isolate* isolate, Handle<JSTemporalDuration> duration) { // 1. Let duration be the this value. // 2. Perform ? RequireInternalSlot(duration, // [[InitializedTemporalDuration]]). // 3. Return ! TemporalDurationToString(duration.[[Years]], // duration.[[Months]], duration.[[Weeks]], duration.[[Days]], // duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], // duration.[[Milliseconds]], duration.[[Microseconds]], // duration.[[Nanoseconds]], "auto"). DurationRecord dur = { Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), {Object::Number(duration->days()), Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())}}; return TemporalDurationToString(isolate, dur, Precision::kAuto); } // #sec-temporal.duration.prototype.tolocalestring MaybeHandle<String> JSTemporalDuration::ToLocaleString( Isolate* isolate, Handle<JSTemporalDuration> duration, Handle<Object> locales, Handle<Object> options) { // 1. Let duration be the this value. // 2. Perform ? RequireInternalSlot(duration, // [[InitializedTemporalDuration]]). // 3. Return ! TemporalDurationToString(duration.[[Years]], // duration.[[Months]], duration.[[Weeks]], duration.[[Days]], // duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], // duration.[[Milliseconds]], duration.[[Microseconds]], // duration.[[Nanoseconds]], "auto"). DurationRecord dur = { Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), {Object::Number(duration->days()), Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())}}; // TODO(ftang) Implement #sup-temporal.duration.prototype.tolocalestring return TemporalDurationToString(isolate, dur, Precision::kAuto); } namespace { // #sec-temporal-moverelativezoneddatetime MaybeHandle<JSTemporalZonedDateTime> MoveRelativeZonedDateTime( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, const DateDurationRecord& duration, const char* method_name) { // 1. Let intermediateNs be ? AddZonedDateTime(zonedDateTime.[[Nanoseconds]], // zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], years, months, // weeks, days, 0, 0, 0, 0, 0, 0). Handle<BigInt> intermediate_ns; ASSIGN_RETURN_ON_EXCEPTION( isolate, intermediate_ns, AddZonedDateTime(isolate, handle(zoned_date_time->nanoseconds(), isolate), handle(zoned_date_time->time_zone(), isolate), handle(zoned_date_time->calendar(), isolate), {duration.years, duration.months, duration.weeks, {duration.days, 0, 0, 0, 0, 0, 0}}, method_name), JSTemporalZonedDateTime); // 2. Return ! CreateTemporalZonedDateTime(intermediateNs, // zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]]). return CreateTemporalZonedDateTime( isolate, intermediate_ns, handle(zoned_date_time->time_zone(), isolate), handle(zoned_date_time->calendar(), isolate)) .ToHandleChecked(); } // #sec-temporal-daysuntil double DaysUntil(Isolate* isolate, Handle<JSTemporalPlainDate> earlier, Handle<JSTemporalPlainDate> later, const char* method_name) { // 1. Let epochDays1 be MakeDay(𝔽(earlier.[[ISOYear]]), 𝔽(earlier.[[ISOMonth]] // - 1), 𝔽(earlier.[[ISODay]])). double epoch_days1 = MakeDay(earlier->iso_year(), earlier->iso_month() - 1, earlier->iso_day()); // 2. Assert: epochDays1 is finite. // 3. Let epochDays2 be MakeDay(𝔽(later.[[ISOYear]]), 𝔽(later.[[ISOMonth]] - // 1), 𝔽(later.[[ISODay]])). double epoch_days2 = MakeDay(later->iso_year(), later->iso_month() - 1, later->iso_day()); // 4. Assert: epochDays2 is finite. // 5. Return ℝ(epochDays2) - ℝ(epochDays1). return epoch_days2 - epoch_days1; } // #sec-temporal-moverelativedate Maybe<MoveRelativeDateResult> MoveRelativeDate( Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSTemporalPlainDate> relative_to, Handle<JSTemporalDuration> duration, const char* method_name) { // 1. Let newDate be ? CalendarDateAdd(calendar, relativeTo, duration). Handle<JSTemporalPlainDate> new_date; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, new_date, CalendarDateAdd(isolate, calendar, relative_to, duration), Nothing<MoveRelativeDateResult>()); // 2. Let days be DaysUntil(relativeTo, newDate). double days = DaysUntil(isolate, relative_to, new_date, method_name); // 3. Return the Record { [[RelativeTo]]: newDate, [[Days]]: days }. return Just(MoveRelativeDateResult({new_date, days})); } // #sec-temporal-roundduration Maybe<DurationRecordWithRemainder> RoundDuration(Isolate* isolate, const DurationRecord& duration, double increment, Unit unit, RoundingMode rounding_mode, Handle<Object> relative_to, const char* method_name) { TEMPORAL_ENTER_FUNC(); // optional argument relativeTo (undefined, a Temporal.PlainDate, or a // Temporal.ZonedDateTime) DCHECK(IsUndefined(*relative_to) || IsJSTemporalPlainDate(*relative_to) || IsJSTemporalZonedDateTime(*relative_to)); Factory* factory = isolate->factory(); DurationRecordWithRemainder result; result.record = duration; // 2. If unit is "year", "month", or "week", and relativeTo is undefined, then if ((unit == Unit::kYear || unit == Unit::kMonth || unit == Unit::kWeek) && IsUndefined(*relative_to)) { // a. Throw a RangeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DurationRecordWithRemainder>()); } // 3. Let zonedRelativeTo be undefined. Handle<Object> zoned_relative_to = isolate->factory()->undefined_value(); Handle<JSReceiver> calendar; // 5. If relativeTo is not undefined, then if (!IsUndefined(*relative_to)) { // a. If relativeTo has an [[InitializedTemporalZonedDateTime]] internal // slot, then if (IsJSTemporalZonedDateTime(*relative_to)) { // i. Set zonedRelativeTo to relativeTo. zoned_relative_to = relative_to; // ii. Set relativeTo to ? ToTemporalDate(relativeTo). Handle<JSTemporalPlainDate> date; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date, ToTemporalDate(isolate, relative_to, method_name), Nothing<DurationRecordWithRemainder>()); relative_to = date; // b. Else, } else { // i. Assert: relativeTo has an [[InitializedTemporalDate]] internal // slot. DCHECK(IsJSTemporalPlainDate(*relative_to)); } // c. Let calendar be relativeTo.[[Calendar]]. calendar = Handle<JSReceiver>( Handle<JSTemporalPlainDate>::cast(relative_to)->calendar(), isolate); // 5. Else, } else { // a. NOTE: calendar will not be used below. } double fractional_seconds = 0; // 6. If unit is one of "year", "month", "week", or "day", then if (unit == Unit::kYear || unit == Unit::kMonth || unit == Unit::kWeek || unit == Unit::kDay) { // a. Let nanoseconds be ! TotalDurationNanoseconds(0, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds, 0). TimeDurationRecord time_duration = duration.time_duration; time_duration.days = 0; Handle<BigInt> nanoseconds = TotalDurationNanoseconds(isolate, time_duration, 0); // b. Let intermediate be undefined. Handle<Object> intermediate = isolate->factory()->undefined_value(); // c. If zonedRelativeTo is not undefined, then if (!IsUndefined(*zoned_relative_to)) { DCHECK(IsJSTemporalZonedDateTime(*zoned_relative_to)); // i. Let intermediate be ? MoveRelativeZonedDateTime(zonedRelativeTo, // years, months, weeks, days). ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, intermediate, MoveRelativeZonedDateTime( isolate, Handle<JSTemporalZonedDateTime>::cast(zoned_relative_to), {duration.years, duration.months, duration.weeks, duration.time_duration.days}, method_name), Nothing<DurationRecordWithRemainder>()); } // d. Let result be ? NanosecondsToDays(nanoseconds, intermediate). NanosecondsToDaysResult to_days_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, to_days_result, NanosecondsToDays(isolate, nanoseconds, intermediate, method_name), Nothing<DurationRecordWithRemainder>()); // e. Set days to days + result.[[Days]] + result.[[Nanoseconds]] / // result.[[DayLength]]. result.record.time_duration.days += to_days_result.days + // https://github.com/tc39/proposal-temporal/issues/2366 std::round(to_days_result.nanoseconds / to_days_result.day_length); // f. Set hours, minutes, seconds, milliseconds, microseconds, and // nanoseconds to 0. result.record.time_duration.hours = result.record.time_duration.minutes = result.record.time_duration.seconds = result.record.time_duration.milliseconds = result.record.time_duration.microseconds = result.record.time_duration.nanoseconds = 0; // 7. Else, } else { // a. Let fractionalSeconds be nanoseconds × 10^−9 + microseconds × 10^−6 + // milliseconds × 10^−3 + seconds. fractional_seconds = result.record.time_duration.nanoseconds * 1e-9 + result.record.time_duration.microseconds * 1e-6 + result.record.time_duration.milliseconds * 1e-3 + result.record.time_duration.seconds; } // 8. Let remainder be undefined. result.remainder = -1; // use -1 for undefined now. switch (unit) { // 9. If unit is "year", then case Unit::kYear: { // a. Let yearsDuration be ! CreateTemporalDuration(years, 0, 0, 0, 0, 0, // 0, 0, 0, 0). Handle<JSTemporalDuration> years_duration = CreateTemporalDuration(isolate, {duration.years, 0, 0, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // b. Let dateAdd be ? GetMethod(calendar, "dateAdd"). Handle<Object> date_add; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_add, Object::GetMethod(isolate, calendar, factory->dateAdd_string()), Nothing<DurationRecordWithRemainder>()); // c. Let yearsLater be ? CalendarDateAdd(calendar, relativeTo, // yearsDuration, undefined, dateAdd). Handle<JSTemporalPlainDate> years_later; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, years_later, CalendarDateAdd(isolate, calendar, relative_to, years_duration, isolate->factory()->undefined_value(), date_add), Nothing<DurationRecordWithRemainder>()); // d. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, // weeks, 0, 0, 0, 0, 0, 0, 0). Handle<JSTemporalDuration> years_months_weeks = CreateTemporalDuration(isolate, {duration.years, duration.months, duration.weeks, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // e. Let yearsMonthsWeeksLater be ? CalendarDateAdd(calendar, relativeTo, // yearsMonthsWeeks, undefined, dateAdd). Handle<JSTemporalPlainDate> years_months_weeks_later; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, years_months_weeks_later, CalendarDateAdd(isolate, calendar, relative_to, years_months_weeks, isolate->factory()->undefined_value(), date_add), Nothing<DurationRecordWithRemainder>()); // f. Let monthsWeeksInDays be DaysUntil(yearsLater, // yearsMonthsWeeksLater). double months_weeks_in_days = DaysUntil( isolate, years_later, years_months_weeks_later, method_name); // g. Set relativeTo to yearsLater. relative_to = years_later; // h. Let days be days + monthsWeeksInDays. result.record.time_duration.days += months_weeks_in_days; // i. Let daysDuration be ? CreateTemporalDuration(0, 0, 0, days, 0, 0, 0, // 0, 0, 0). Handle<JSTemporalDuration> days_duration; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, days_duration, CreateTemporalDuration( isolate, {0, 0, 0, {result.record.time_duration.days, 0, 0, 0, 0, 0, 0}}), Nothing<DurationRecordWithRemainder>()); // j. Let daysLater be ? CalendarDateAdd(calendar, relativeTo, // daysDuration, undefined, dateAdd). Handle<JSTemporalPlainDate> days_later; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, days_later, CalendarDateAdd(isolate, calendar, relative_to, days_duration, isolate->factory()->undefined_value(), date_add), Nothing<DurationRecordWithRemainder>()); // k. Let untilOptions be OrdinaryObjectCreate(null). Handle<JSObject> until_options = factory->NewJSObjectWithNullProto(); // l. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", // "year"). CHECK(JSReceiver::CreateDataProperty( isolate, until_options, factory->largestUnit_string(), factory->year_string(), Just(kThrowOnError)) .FromJust()); // m. Let timePassed be ? CalendarDateUntil(calendar, relativeTo, // daysLater, untilOptions). Handle<JSTemporalDuration> time_passed; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, time_passed, CalendarDateUntil(isolate, calendar, relative_to, days_later, until_options), Nothing<DurationRecordWithRemainder>()); // n. Let yearsPassed be timePassed.[[Years]]. double years_passed = Object::Number(time_passed->years()); // o. Set years to years + yearsPassed. result.record.years += years_passed; // p. Let oldRelativeTo be relativeTo. Handle<Object> old_relative_to = relative_to; // q. Let yearsDuration be ? CreateTemporalDuration(yearsPassed, 0, 0, 0, // 0, 0, 0, 0, 0, 0). years_duration = CreateTemporalDuration( isolate, {years_passed, 0, 0, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // r. Set relativeTo to ? CalendarDateAdd(calendar, relativeTo, // yearsDuration, undefined, dateAdd). Handle<JSTemporalPlainDate> years_added; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, years_added, CalendarDateAdd(isolate, calendar, relative_to, years_duration, isolate->factory()->undefined_value(), date_add), Nothing<DurationRecordWithRemainder>()); relative_to = years_added; // s. Let daysPassed be DaysUntil(oldRelativeTo, relativeTo). DCHECK(IsJSTemporalPlainDate(*old_relative_to)); DCHECK(IsJSTemporalPlainDate(*relative_to)); double days_passed = DaysUntil( isolate, Handle<JSTemporalPlainDate>::cast(old_relative_to), Handle<JSTemporalPlainDate>::cast(relative_to), method_name); // t. Set days to days - daysPassed. result.record.time_duration.days -= days_passed; // u. If days < 0, let sign be -1; else, let sign be 1. double sign = result.record.time_duration.days < 0 ? -1 : 1; // v. Let oneYear be ! CreateTemporalDuration(sign, 0, 0, 0, 0, 0, 0, 0, // 0, 0). Handle<JSTemporalDuration> one_year = CreateTemporalDuration(isolate, {sign, 0, 0, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // w. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneYear). MoveRelativeDateResult move_result; DCHECK(IsJSTemporalPlainDate(*relative_to)); MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, Handle<JSTemporalPlainDate>::cast(relative_to), one_year, method_name), Nothing<DurationRecordWithRemainder>()); // x. Let oneYearDays be moveResult.[[Days]]. double one_year_days = move_result.days; // y. Let fractionalYears be years + days / abs(oneYearDays). double fractional_years = result.record.years + result.record.time_duration.days / std::abs(one_year_days); // z. Set years to RoundNumberToIncrement(fractionalYears, increment, // roundingMode). result.record.years = RoundNumberToIncrement(isolate, fractional_years, increment, rounding_mode); // aa. Set remainder to fractionalYears - years. result.remainder = fractional_years - result.record.years; // ab. Set months, weeks, and days to 0. result.record.months = result.record.weeks = result.record.time_duration.days = 0; } break; // 10. Else if unit is "month", then case Unit::kMonth: { // a. Let yearsMonths be ! CreateTemporalDuration(years, months, 0, 0, 0, // 0, 0, 0, 0, 0). Handle<JSTemporalDuration> years_months = CreateTemporalDuration( isolate, {duration.years, duration.months, 0, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // b. Let dateAdd be ? GetMethod(calendar, "dateAdd"). Handle<Object> date_add; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_add, Object::GetMethod(isolate, calendar, factory->dateAdd_string()), Nothing<DurationRecordWithRemainder>()); // c. Let yearsMonthsLater be ? CalendarDateAdd(calendar, relativeTo, // yearsMonths, undefined, dateAdd). Handle<JSTemporalPlainDate> years_months_later; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, years_months_later, CalendarDateAdd(isolate, calendar, relative_to, years_months, isolate->factory()->undefined_value(), date_add), Nothing<DurationRecordWithRemainder>()); // d. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, // weeks, 0, 0, 0, 0, 0, 0, 0). Handle<JSTemporalDuration> years_months_weeks = CreateTemporalDuration(isolate, {duration.years, duration.months, duration.weeks, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // e. Let yearsMonthsWeeksLater be ? CalendarDateAdd(calendar, relativeTo, // yearsMonthsWeeks, undefined, dateAdd). Handle<JSTemporalPlainDate> years_months_weeks_later; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, years_months_weeks_later, CalendarDateAdd(isolate, calendar, relative_to, years_months_weeks, isolate->factory()->undefined_value(), date_add), Nothing<DurationRecordWithRemainder>()); // f. Let weeksInDays be DaysUntil(yearsMonthsLater, // yearsMonthsWeeksLater). double weeks_in_days = DaysUntil(isolate, years_months_later, years_months_weeks_later, method_name); // g. Set relativeTo to yearsMonthsLater. relative_to = years_months_later; // h. Let days be days + weeksInDays. result.record.time_duration.days += weeks_in_days; // i. If days < 0, let sign be -1; else, let sign be 1. double sign = result.record.time_duration.days < 0 ? -1 : 1; // j. Let oneMonth be ! CreateTemporalDuration(0, sign, 0, 0, 0, 0, 0, 0, // 0, 0). Handle<JSTemporalDuration> one_month = CreateTemporalDuration(isolate, {0, sign, 0, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // k. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, // oneMonth). MoveRelativeDateResult move_result; DCHECK(IsJSTemporalPlainDate(*relative_to)); MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, Handle<JSTemporalPlainDate>::cast(relative_to), one_month, method_name), Nothing<DurationRecordWithRemainder>()); // l. Set relativeTo to moveResult.[[RelativeTo]]. relative_to = move_result.relative_to; // m. Let oneMonthDays be moveResult.[[Days]]. double one_month_days = move_result.days; // n. Repeat, while abs(days) ≥ abs(oneMonthDays), while (std::abs(result.record.time_duration.days) >= std::abs(one_month_days)) { // i. Set months to months + sign. result.record.months += sign; // ii. Set days to days - oneMonthDays. result.record.time_duration.days -= one_month_days; // iii. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, // oneMonth). DCHECK(IsJSTemporalPlainDate(*relative_to)); MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, Handle<JSTemporalPlainDate>::cast(relative_to), one_month, method_name), Nothing<DurationRecordWithRemainder>()); // iv. Set relativeTo to moveResult.[[RelativeTo]]. relative_to = move_result.relative_to; // v. Set oneMonthDays to moveResult.[[Days]]. one_month_days = move_result.days; } // o. Let fractionalMonths be months + days / abs(oneMonthDays). double fractional_months = result.record.months + result.record.time_duration.days / std::abs(one_month_days); // p. Set months to RoundNumberToIncrement(fractionalMonths, increment, // roundingMode). result.record.months = RoundNumberToIncrement(isolate, fractional_months, increment, rounding_mode); // q. Set remainder to fractionalMonths - months. result.remainder = fractional_months - result.record.months; // r. Set weeks and days to 0. result.record.weeks = result.record.time_duration.days = 0; } break; // 11. Else if unit is "week", then case Unit::kWeek: { // a. If days < 0, let sign be -1; else, let sign be 1. double sign = result.record.time_duration.days < 0 ? -1 : 1; // b. Let oneWeek be ! CreateTemporalDuration(0, 0, sign, 0, 0, 0, 0, 0, // 0, 0). Handle<JSTemporalDuration> one_week = CreateTemporalDuration(isolate, {0, 0, sign, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // c. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneWeek). MoveRelativeDateResult move_result; DCHECK(IsJSTemporalPlainDate(*relative_to)); MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, Handle<JSTemporalPlainDate>::cast(relative_to), one_week, method_name), Nothing<DurationRecordWithRemainder>()); // d. Set relativeTo to moveResult.[[RelativeTo]]. relative_to = move_result.relative_to; // e. Let oneWeekDays be moveResult.[[Days]]. double one_week_days = move_result.days; // f. Repeat, while abs(days) ≥ abs(oneWeekDays), while (std::abs(result.record.time_duration.days) >= std::abs(one_week_days)) { // i. Set weeks to weeks + sign. result.record.weeks += sign; // ii. Set days to days - oneWeekDays. result.record.time_duration.days -= one_week_days; // iii. Set moveResult to ? MoveRelativeDate(calendar, relativeTo, // oneWeek). DCHECK(IsJSTemporalPlainDate(*relative_to)); MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, move_result, MoveRelativeDate(isolate, calendar, Handle<JSTemporalPlainDate>::cast(relative_to), one_week, method_name), Nothing<DurationRecordWithRemainder>()); // iv. Set relativeTo to moveResult.[[RelativeTo]]. relative_to = move_result.relative_to; // v. Set oneWeekDays to moveResult.[[Days]]. one_week_days = move_result.days; } // g. Let fractionalWeeks be weeks + days / abs(oneWeekDays). double fractional_weeks = result.record.weeks + result.record.time_duration.days / std::abs(one_week_days); // h. Set weeks to RoundNumberToIncrement(fractionalWeeks, increment, // roundingMode). result.record.weeks = RoundNumberToIncrement(isolate, fractional_weeks, increment, rounding_mode); // i. Set remainder to fractionalWeeks - weeks. result.remainder = fractional_weeks - result.record.weeks; // j. Set days to 0. result.record.time_duration.days = 0; } break; // 12. Else if unit is "day", then case Unit::kDay: { // a. Let fractionalDays be days. double fractional_days = result.record.time_duration.days; // b. Set days to ! RoundNumberToIncrement(days, increment, roundingMode). result.record.time_duration.days = RoundNumberToIncrement( isolate, result.record.time_duration.days, increment, rounding_mode); // c. Set remainder to fractionalDays - days. result.remainder = fractional_days - result.record.time_duration.days; } break; // 13. Else if unit is "hour", then case Unit::kHour: { // a. Let fractionalHours be (fractionalSeconds / 60 + minutes) / 60 + // hours. double fractional_hours = (fractional_seconds / 60.0 + duration.time_duration.minutes) / 60.0 + duration.time_duration.hours; // b. Set hours to ! RoundNumberToIncrement(fractionalHours, increment, // roundingMode). result.record.time_duration.hours = RoundNumberToIncrement( isolate, fractional_hours, increment, rounding_mode); // c. Set remainder to fractionalHours - hours. result.remainder = fractional_hours - result.record.time_duration.hours; // d. Set minutes, seconds, milliseconds, microseconds, and nanoseconds to // 0. result.record.time_duration.minutes = result.record.time_duration.seconds = result.record.time_duration.milliseconds = result.record.time_duration.microseconds = result.record.time_duration.nanoseconds = 0; } break; // 14. Else if unit is "minute", then case Unit::kMinute: { // a. Let fractionalMinutes be fractionalSeconds / 60 + minutes. double fractional_minutes = fractional_seconds / 60.0 + duration.time_duration.minutes; // b. Set minutes to ! RoundNumberToIncrement(fractionalMinutes, // increment, roundingMode). result.record.time_duration.minutes = RoundNumberToIncrement( isolate, fractional_minutes, increment, rounding_mode); // c. Set remainder to fractionalMinutes - minutes. result.remainder = fractional_minutes - result.record.time_duration.minutes; // d. Set seconds, milliseconds, microseconds, and nanoseconds to 0. result.record.time_duration.seconds = result.record.time_duration.milliseconds = result.record.time_duration.microseconds = result.record.time_duration.nanoseconds = 0; } break; // 15. Else if unit is "second", then case Unit::kSecond: { // a. Set seconds to ! RoundNumberToIncrement(fractionalSeconds, // increment, roundingMode). result.record.time_duration.seconds = RoundNumberToIncrement( isolate, fractional_seconds, increment, rounding_mode); // b. Set remainder to fractionalSeconds - seconds. result.remainder = fractional_seconds - result.record.time_duration.seconds; // c. Set milliseconds, microseconds, and nanoseconds to 0. result.record.time_duration.milliseconds = result.record.time_duration.microseconds = result.record.time_duration.nanoseconds = 0; } break; // 16. Else if unit is "millisecond", then case Unit::kMillisecond: { // a. Let fractionalMilliseconds be nanoseconds × 10^−6 + microseconds × // 10^−3 + milliseconds. double fractional_milliseconds = duration.time_duration.nanoseconds * 1e-6 + duration.time_duration.microseconds * 1e-3 + duration.time_duration.milliseconds; // b. Set milliseconds to ! RoundNumberToIncrement(fractionalMilliseconds, // increment, roundingMode). result.record.time_duration.milliseconds = RoundNumberToIncrement( isolate, fractional_milliseconds, increment, rounding_mode); // c. Set remainder to fractionalMilliseconds - milliseconds. result.remainder = fractional_milliseconds - result.record.time_duration.milliseconds; // d. Set microseconds and nanoseconds to 0. result.record.time_duration.microseconds = result.record.time_duration.nanoseconds = 0; } break; // 17. Else if unit is "microsecond", then case Unit::kMicrosecond: { // a. Let fractionalMicroseconds be nanoseconds × 10−3 + microseconds. double fractional_microseconds = duration.time_duration.nanoseconds * 1e-3 + duration.time_duration.microseconds; // b. Set microseconds to ! RoundNumberToIncrement(fractionalMicroseconds, // increment, roundingMode). result.record.time_duration.microseconds = RoundNumberToIncrement( isolate, fractional_microseconds, increment, rounding_mode); // c. Set remainder to fractionalMicroseconds - microseconds. result.remainder = fractional_microseconds - result.record.time_duration.microseconds; // d. Set nanoseconds to 0. result.record.time_duration.nanoseconds = 0; } break; // 18. Else, default: { // a. Assert: unit is "nanosecond". DCHECK_EQ(unit, Unit::kNanosecond); // b. Set remainder to nanoseconds. result.remainder = result.record.time_duration.nanoseconds; // c. Set nanoseconds to ! RoundNumberToIncrement(nanoseconds, increment, // roundingMode). result.record.time_duration.nanoseconds = RoundNumberToIncrement( isolate, result.record.time_duration.nanoseconds, increment, rounding_mode); // d. Set remainder to remainder − nanoseconds. result.remainder -= result.record.time_duration.nanoseconds; } break; } // 19. Let duration be ? CreateDurationRecord(years, months, weeks, days, // hours, minutes, seconds, milliseconds, microseconds, nanoseconds). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result.record, CreateDurationRecord(isolate, result.record), Nothing<DurationRecordWithRemainder>()); return Just(result); } Maybe<DurationRecordWithRemainder> RoundDuration(Isolate* isolate, const DurationRecord& duration, double increment, Unit unit, RoundingMode rounding_mode, const char* method_name) { // 1. If relativeTo is not present, set relativeTo to undefined. return RoundDuration(isolate, duration, increment, unit, rounding_mode, isolate->factory()->undefined_value(), method_name); } // #sec-temporal-tosecondsstringprecision struct StringPrecision { Precision precision; Unit unit; double increment; }; // #sec-temporal-tosecondsstringprecision Maybe<StringPrecision> ToSecondsStringPrecision( Isolate* isolate, Handle<JSReceiver> normalized_options, const char* method_name); } // namespace // #sec-temporal.duration.prototype.tostring MaybeHandle<String> JSTemporalDuration::ToString( Isolate* isolate, Handle<JSTemporalDuration> duration, Handle<Object> options_obj) { const char* method_name = "Temporal.Duration.prototype.toString"; // 3. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), String); // 4. Let precision be ? ToSecondsStringPrecision(options). StringPrecision precision; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, precision, ToSecondsStringPrecision(isolate, options, method_name), Handle<String>()); // 5. If precision.[[Unit]] is "minute", throw a RangeError exception. if (precision.unit == Unit::kMinute) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), String); } // 6. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). RoundingMode rounding_mode; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_mode, ToTemporalRoundingMode(isolate, options, RoundingMode::kTrunc, method_name), Handle<String>()); // 7. Let result be ? RoundDuration(duration.[[Years]], duration.[[Months]], // duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], // duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], // duration.[[Microseconds]], duration.[[Nanoseconds]], // precision.[[Increment]], precision.[[Unit]], roundingMode). DurationRecord dur = { Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), {Object::Number(duration->days()), Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())}}; DurationRecordWithRemainder result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, RoundDuration(isolate, dur, precision.increment, precision.unit, rounding_mode, method_name), Handle<String>()); // 8. Return ! TemporalDurationToString(result.[[Years]], result.[[Months]], // result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], // result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], // result.[[Nanoseconds]], precision.[[Precision]]). return TemporalDurationToString(isolate, result.record, precision.precision); } // #sec-temporal.calendar MaybeHandle<JSTemporalCalendar> JSTemporalCalendar::Constructor( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<Object> identifier_obj) { // 1. If NewTarget is undefined, then if (IsUndefined(*new_target, isolate)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, isolate->factory()->NewStringFromStaticChars( "Temporal.Calendar")), JSTemporalCalendar); } // 2. Set identifier to ? ToString(identifier). Handle<String> identifier; ASSIGN_RETURN_ON_EXCEPTION(isolate, identifier, Object::ToString(isolate, identifier_obj), JSTemporalCalendar); // 3. If ! IsBuiltinCalendar(id) is false, then if (!IsBuiltinCalendar(isolate, identifier)) { // a. Throw a RangeError exception. THROW_NEW_ERROR( isolate, NewRangeError(MessageTemplate::kInvalidCalendar, identifier), JSTemporalCalendar); } return CreateTemporalCalendar(isolate, target, new_target, identifier); } namespace { // #sec-temporal-toisodayofyear int32_t ToISODayOfYear(Isolate* isolate, const DateRecord& date) { TEMPORAL_ENTER_FUNC(); // 1. Assert: IsValidISODate(year, month, day) is *true*. DCHECK(IsValidISODate(isolate, date)); // 2. Let _epochDays_ be MakeDay(𝔽(year), 𝔽(month - 1), 𝔽(day)). // 3. Assert: _epochDays_ is finite. // 4. Return ℝ(DayWithinYear(MakeDate(_epochDays_, *+0*<sub>𝔽</sub>))) + 1. // Note: In ISO 8601, Jan: month=1, Dec: month=12, // In DateCache API, Jan: month=0, Dec: month=11 so we need to - 1 for month. return date.day + isolate->date_cache()->DaysFromYearMonth(date.year, date.month - 1) - isolate->date_cache()->DaysFromYearMonth(date.year, 0); } bool IsPlainDatePlainDateTimeOrPlainYearMonth( Handle<Object> temporal_date_like) { return IsJSTemporalPlainDate(*temporal_date_like) || IsJSTemporalPlainDateTime(*temporal_date_like) || IsJSTemporalPlainYearMonth(*temporal_date_like); } // #sec-temporal-toisodayofweek int32_t ToISODayOfWeek(Isolate* isolate, const DateRecord& date) { TEMPORAL_ENTER_FUNC(); // 1. Assert: IsValidISODate(year, month, day) is *true*. DCHECK(IsValidISODate(isolate, date)); // 2. Let _epochDays_ be MakeDay(𝔽(year), 𝔽(month - 1), 𝔽(day)). // Note: "- 1" after "date.day" came from the MakeyDay AO in // "9. Return Day(t) + dt - 1𝔽." int32_t epoch_days = isolate->date_cache()->DaysFromYearMonth(date.year, date.month - 1) + date.day - 1; // 3. Assert: _epochDays_ is finite. // 4. Let _dayOfWeek_ be WeekDay(MakeDate(_epochDays_, *+0*<sub>𝔽</sub>)). int32_t weekday = isolate->date_cache()->Weekday(epoch_days); // 5. If _dayOfWeek_ = *+0*<sub>𝔽</sub>, return 7. // Note: In ISO 8601, Jan: month=1, Dec: month=12. // In DateCache API, Jan: month=0, Dec: month=11 so we need to - 1 for month. // Weekday() expect "the number of days since the epoch" as input and the // value of day is 1-based so we need to minus 1 to calculate "the number of // days" because the number of days on the epoch (1970/1/1) should be 0, // not 1 // Note: In ISO 8601, Sun: weekday=7 Mon: weekday=1 // In DateCache API, Sun: weekday=0 Mon: weekday=1 // 6. Return ℝ(_dayOfWeek_). return weekday == 0 ? 7 : weekday; } // #sec-temporal-regulateisodate Maybe<DateRecord> RegulateISODate(Isolate* isolate, ShowOverflow overflow, const DateRecord& date) { TEMPORAL_ENTER_FUNC(); // 1. Assert: year, month, and day are integers. // 2. Assert: overflow is either "constrain" or "reject". switch (overflow) { // 3. If overflow is "reject", then case ShowOverflow::kReject: // a. If ! IsValidISODate(year, month, day) is false, throw a RangeError // exception. if (!IsValidISODate(isolate, date)) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateRecord>()); } // b. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day // }. return Just(date); // 4. If overflow is "constrain", then case ShowOverflow::kConstrain: DateRecord result(date); // a. Set month to ! ConstrainToRange(month, 1, 12). result.month = std::max(std::min(result.month, 12), 1); // b. Set day to ! ConstrainToRange(day, 1, ! ISODaysInMonth(year, // month)). result.day = std::max(std::min(result.day, ISODaysInMonth(isolate, result.year, result.month)), 1); // c. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day // }. return Just(result); } } // #sec-temporal-regulateisoyearmonth Maybe<int32_t> RegulateISOYearMonth(Isolate* isolate, ShowOverflow overflow, int32_t month) { // 1. Assert: year and month are integers. // 2. Assert: overflow is either "constrain" or "reject". switch (overflow) { // 3. If overflow is "constrain", then case ShowOverflow::kConstrain: // a. Return ! ConstrainISOYearMonth(year, month). return Just(std::max(std::min(month, 12), 1)); // 4. If overflow is "reject", then case ShowOverflow::kReject: // a. If ! IsValidISOMonth(month) is false, throw a RangeError exception. if (month < 1 || 12 < month) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<int32_t>()); } // b. Return the new Record { [[Year]]: year, [[Month]]: month }. return Just(month); default: UNREACHABLE(); } } // #sec-temporal-resolveisomonth Maybe<int32_t> ResolveISOMonth(Isolate* isolate, Handle<JSReceiver> fields) { Factory* factory = isolate->factory(); // 1. Let month be ! Get(fields, "month"). Handle<Object> month_obj = JSReceiver::GetProperty(isolate, fields, factory->month_string()) .ToHandleChecked(); // 2. Let monthCode be ! Get(fields, "monthCode"). Handle<Object> month_code_obj = JSReceiver::GetProperty(isolate, fields, factory->monthCode_string()) .ToHandleChecked(); // 3. If monthCode is undefined, then if (IsUndefined(*month_code_obj, isolate)) { // a. If month is undefined, throw a TypeError exception. if (IsUndefined(*month_obj, isolate)) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Nothing<int32_t>()); } // b. Return month. // Note: In Temporal spec, "month" in fields is always converted by // ToPositiveInteger inside PrepareTemporalFields before calling // ResolveISOMonth. Therefore the month_obj is always a positive integer. DCHECK(IsSmi(*month_obj) || IsHeapNumber(*month_obj)); return Just(FastD2I(Object::Number(*month_obj))); } // 4. Assert: Type(monthCode) is String. DCHECK(IsString(*month_code_obj)); Handle<String> month_code; ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, month_code, Object::ToString(isolate, month_code_obj), Nothing<int32_t>()); // 5. Let monthLength be the length of monthCode. // 6. If monthLength is not 3, throw a RangeError exception. if (month_code->length() != 3) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kPropertyValueOutOfRange, factory->monthCode_string()), Nothing<int32_t>()); } // 7. Let numberPart be the substring of monthCode from 1. // 8. Set numberPart to ! ToIntegerOrInfinity(numberPart). // 9. If numberPart < 1 or numberPart > 12, throw a RangeError exception. uint16_t m0 = month_code->Get(0); uint16_t m1 = month_code->Get(1); uint16_t m2 = month_code->Get(2); if (!((m0 == 'M') && ((m1 == '0' && '1' <= m2 && m2 <= '9') || (m1 == '1' && '0' <= m2 && m2 <= '2')))) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kPropertyValueOutOfRange, factory->monthCode_string()), Nothing<int32_t>()); } int32_t number_part = 10 * static_cast<int32_t>(m1 - '0') + static_cast<int32_t>(m2 - '0'); // 10. If month is not undefined, and month ≠ numberPart, then // 11. If ! SameValueNonNumeric(monthCode, ! BuildISOMonthCode(numberPart)) is // false, then a. Throw a RangeError exception. // Note: In Temporal spec, "month" in fields is always converted by // ToPositiveInteger inside PrepareTemporalFields before calling // ResolveISOMonth. Therefore the month_obj is always a positive integer. if (!IsUndefined(*month_obj) && FastD2I(Object::Number(*month_obj)) != number_part) { // a. Throw a RangeError exception. THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kPropertyValueOutOfRange, factory->month_string()), Nothing<int32_t>()); } // 12. Return numberPart. return Just(number_part); } // #sec-temporal-isodatefromfields Maybe<DateRecord> ISODateFromFields(Isolate* isolate, Handle<JSReceiver> fields, Handle<JSReceiver> options, const char* method_name) { Factory* factory = isolate->factory(); // 1. Assert: Type(fields) is Object. // 2. Set fields to ? PrepareTemporalFields(fields, « "day", "month", // "monthCode", "year" », «"year", "day"»). Handle<FixedArray> field_names = DayMonthMonthCodeYearInFixedArray(isolate); ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, fields, PrepareTemporalFields(isolate, fields, field_names, RequiredFields::kYearAndDay), Nothing<DateRecord>()); // 3. Let overflow be ? ToTemporalOverflow(options). ShowOverflow overflow; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, overflow, ToTemporalOverflow(isolate, options, method_name), Nothing<DateRecord>()); // 4. Let year be ! Get(fields, "year"). Handle<Object> year_obj = JSReceiver::GetProperty(isolate, fields, factory->year_string()) .ToHandleChecked(); // 5. Assert: Type(year) is Number. // Note: "year" in fields is always converted by // ToIntegerThrowOnInfinity inside the PrepareTemporalFields above. // Therefore the year_obj is always an integer. DCHECK(IsSmi(*year_obj) || IsHeapNumber(*year_obj)); // 6. Let month be ? ResolveISOMonth(fields). int32_t month; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, month, ResolveISOMonth(isolate, fields), Nothing<DateRecord>()); // 7. Let day be ! Get(fields, "day"). Handle<Object> day_obj = JSReceiver::GetProperty(isolate, fields, factory->day_string()) .ToHandleChecked(); // 8. Assert: Type(day) is Number. // Note: "day" in fields is always converted by // ToIntegerThrowOnInfinity inside the PrepareTemporalFields above. // Therefore the day_obj is always an integer. DCHECK(IsSmi(*day_obj) || IsHeapNumber(*day_obj)); // 9. Return ? RegulateISODate(year, month, day, overflow). return RegulateISODate(isolate, overflow, {FastD2I(Object::Number(*year_obj)), month, FastD2I(Object::Number(*day_obj))}); } // #sec-temporal-addisodate Maybe<DateRecord> AddISODate(Isolate* isolate, const DateRecord& date, const DateDurationRecord& duration, ShowOverflow overflow) { TEMPORAL_ENTER_FUNC(); // 1. Assert: year, month, day, years, months, weeks, and days are integers. // 2. Assert: overflow is either "constrain" or "reject". DCHECK(overflow == ShowOverflow::kConstrain || overflow == ShowOverflow::kReject); // 3. Let intermediate be ! BalanceISOYearMonth(year + years, month + months). DateRecord intermediate = date; intermediate.year += static_cast<int32_t>(duration.years); intermediate.month += static_cast<int32_t>(duration.months); BalanceISOYearMonth(isolate, &intermediate.year, &intermediate.month); // 4. Let intermediate be ? RegulateISODate(intermediate.[[Year]], // intermediate.[[Month]], day, overflow). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, intermediate, RegulateISODate(isolate, overflow, intermediate), Nothing<DateRecord>()); // 5. Set days to days + 7 × weeks. // 6. Let d be intermediate.[[Day]] + days. intermediate.day += duration.days + 7 * duration.weeks; // 7. Return BalanceISODate(intermediate.[[Year]], intermediate.[[Month]], d). return Just(BalanceISODate(isolate, intermediate)); } // #sec-temporal-differenceisodate Maybe<DateDurationRecord> DifferenceISODate(Isolate* isolate, const DateRecord& date1, const DateRecord& date2, Unit largest_unit, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. Assert: largestUnit is one of "year", "month", "week", or "day". DCHECK(largest_unit == Unit::kYear || largest_unit == Unit::kMonth || largest_unit == Unit::kWeek || largest_unit == Unit::kDay); // 2. If largestUnit is "year" or "month", then switch (largest_unit) { case Unit::kYear: case Unit::kMonth: { // a. Let sign be -(! CompareISODate(y1, m1, d1, y2, m2, d2)). int32_t sign = -CompareISODate(date1, date2); // b. If sign is 0, return ! CreateDateDurationRecord(0, 0, 0, 0). if (sign == 0) { return DateDurationRecord::Create(isolate, 0, 0, 0, 0); } // c. Let start be the new Record { [[Year]]: y1, [[Month]]: m1, [[Day]]: // d1 // }. DateRecord start = date1; // d. Let end be the new Record { [[Year]]: y2, [[Month]]: m2, [[Day]]: // d2 }. DateRecord end = date2; // e. Let years be end.[[Year]] − start.[[Year]]. double years = end.year - start.year; // f. Let mid be ! AddISODate(y1, m1, d1, years, 0, 0, 0, "constrain"). DateRecord mid; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, mid, AddISODate(isolate, date1, {years, 0, 0, 0}, ShowOverflow::kConstrain), Nothing<DateDurationRecord>()); // g. Let midSign be -(! CompareISODate(mid.[[Year]], mid.[[Month]], // mid.[[Day]], y2, m2, d2)). int32_t mid_sign = -CompareISODate(mid, date2); // h. If midSign is 0, then if (mid_sign == 0) { // i. If largestUnit is "year", return ! CreateDateDurationRecord(years, // 0, 0, 0). if (largest_unit == Unit::kYear) { return DateDurationRecord::Create(isolate, years, 0, 0, 0); } // ii. Return ! CreateDateDurationRecord(0, years × 12, 0, 0). return DateDurationRecord::Create(isolate, 0, years * 12, 0, 0); } // i. Let months be end.[[Month]] − start.[[Month]]. double months = end.month - start.month; // j. If midSign is not equal to sign, then if (mid_sign != sign) { // i. Set years to years - sign. years -= sign; // ii. Set months to months + sign × 12. months += sign * 12; } // k. Set mid be ! AddISODate(y1, m1, d1, years, months, 0, 0, // "constrain"). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, mid, AddISODate(isolate, date1, {years, months, 0, 0}, ShowOverflow::kConstrain), Nothing<DateDurationRecord>()); // l. Let midSign be -(! CompareISODate(mid.[[Year]], mid.[[Month]], // mid.[[Day]], y2, m2, d2)). mid_sign = -CompareISODate(mid, date2); // m. If midSign is 0, then if (mid_sign == 0) { // 1. i. If largestUnit is "year", return ! // CreateDateDurationRecord(years, months, 0, 0). if (largest_unit == Unit::kYear) { return DateDurationRecord::Create(isolate, years, months, 0, 0); } // ii. Return ! CreateDateDurationRecord(0, months + years × 12, 0, 0). return DateDurationRecord::Create(isolate, 0, months + years * 12, 0, 0); } // n. If midSign is not equal to sign, then if (mid_sign != sign) { // i. Set months to months - sign. months -= sign; // ii. If months is equal to -sign, then if (months == -sign) { // 1. Set years to years - sign. years -= sign; // 2. Set months to 11 × sign. months = 11 * sign; } // iii. Set mid be ! AddISODate(y1, m1, d1, years, months, 0, 0, // "constrain"). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, mid, AddISODate(isolate, date1, {years, months, 0, 0}, ShowOverflow::kConstrain), Nothing<DateDurationRecord>()); // iv. Let midSign be -(! CompareISODate(mid.[[Year]], mid.[[Month]], // mid.[[Day]], y2, m2, d2)). mid_sign = -CompareISODate(mid, date2); } // o. Let days be 0. double days = 0; // p. If mid.[[Month]] = end.[[Month]], then if (mid.month == end.month) { // i. Assert: mid.[[Year]] = end.[[Year]]. DCHECK_EQ(mid.year, end.year); // ii. Set days to end.[[Day]] - mid.[[Day]]. days = end.day - mid.day; } else if (sign < 0) { // q. Else if sign < 0, set days to -mid.[[Day]] - (! // ISODaysInMonth(end.[[Year]], end.[[Month]]) - end.[[Day]]). days = -mid.day - (ISODaysInMonth(isolate, end.year, end.month) - end.day); } else { // r. Else, set days to end.[[Day]] + (! ISODaysInMonth(mid.[[Year]], // mid.[[Month]]) - mid.[[Day]]). days = end.day + (ISODaysInMonth(isolate, mid.year, mid.month) - mid.day); } // s. If largestUnit is "month", then if (largest_unit == Unit::kMonth) { // i. Set months to months + years × 12. months += years * 12; // ii. Set years to 0. years = 0; } // t. Return ! CreateDateDurationRecord(years, months, 0, days). return DateDurationRecord::Create(isolate, years, months, 0, days); } // 3. If largestUnit is "day" or "week", then case Unit::kDay: case Unit::kWeek: { DateRecord smaller, greater; // a. If ! CompareISODate(y1, m1, d1, y2, m2, d2) < 0, then int32_t sign; if (CompareISODate(date1, date2) < 0) { // i. Let smaller be the Record { [[Year]]: y1, [[Month]]: m1, [[Day]]: // d1 // }. smaller = date1; // ii. Let greater be the Record { [[Year]]: y2, [[Month]]: m2, [[Day]]: // d2 // }. greater = date2; // iii. Let sign be 1. sign = 1; } else { // b. Else, // i. Let smaller be the new Record { [[Year]]: y2, [[Month]]: m2, // [[Day]]: d2 }. smaller = date2; // ii. Let greater be the new Record { [[Year]]: y1, [[Month]]: m1, // [[Day]]: d1 }. greater = date1; // iii. Let sign be −1. sign = -1; } // c. Let days be ! ToISODayOfYear(greater.[[Year]], greater.[[Month]], // greater.[[Day]]) − ! ToISODayOfYear(smaller.[[Year]], // smaller.[[Month]], smaller.[[Day]]). int32_t days = ToISODayOfYear(isolate, greater) - ToISODayOfYear(isolate, smaller); // d. Let year be smaller.[[Year]]. // e. Repeat, while year < greater.[[Year]], for (int32_t year = smaller.year; year < greater.year; year++) { // i. Set days to days + ! ISODaysInYear(year). // ii. Set year to year + 1. days += ISODaysInYear(isolate, year); } // f. Let weeks be 0. int32_t weeks = 0; // g. If largestUnit is "week", then if (largest_unit == Unit::kWeek) { // i. Set weeks to floor(days / 7). weeks = days / 7; // ii. Set days to days mod 7. days = days % 7; } // h. Return ! CreateDateDurationRecord(0, 0, weeks × sign, days × sign). return DateDurationRecord::Create(isolate, 0, 0, weeks * sign, days * sign); } default: UNREACHABLE(); } } // #sec-temporal-isoyearmonthfromfields Maybe<DateRecord> ISOYearMonthFromFields(Isolate* isolate, Handle<JSReceiver> fields, Handle<JSReceiver> options, const char* method_name) { Factory* factory = isolate->factory(); // 1. Assert: Type(fields) is Object. // 2. Set fields to ? PrepareTemporalFields(fields, « "month", "monthCode", // "year" », «»). Handle<FixedArray> field_names = factory->NewFixedArray(3); field_names->set(0, ReadOnlyRoots(isolate).month_string()); field_names->set(1, ReadOnlyRoots(isolate).monthCode_string()); field_names->set(2, ReadOnlyRoots(isolate).year_string()); ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, fields, PrepareTemporalFields(isolate, fields, field_names, RequiredFields::kNone), Nothing<DateRecord>()); // 3. Let overflow be ? ToTemporalOverflow(options). ShowOverflow overflow; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, overflow, ToTemporalOverflow(isolate, options, method_name), Nothing<DateRecord>()); // 4. Let year be ! Get(fields, "year"). Handle<Object> year_obj = JSReceiver::GetProperty(isolate, fields, factory->year_string()) .ToHandleChecked(); // 5. If year is undefined, throw a TypeError exception. if (IsUndefined(*year_obj, isolate)) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Nothing<DateRecord>()); } DateRecord result; result.year = FastD2I(floor(Object::Number(*year_obj))); // 6. Let month be ? ResolveISOMonth(fields). int32_t month; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, month, ResolveISOMonth(isolate, fields), Nothing<DateRecord>()); // 7. Let result be ? RegulateISOYearMonth(year, month, overflow). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result.month, RegulateISOYearMonth(isolate, overflow, month), Nothing<DateRecord>()); // 8. Return the new Record { [[Year]]: result.[[Year]], [[Month]]: // result.[[Month]], [[ReferenceISODay]]: 1 }. result.day = 1; return Just(result); } // #sec-temporal-toisoweekofyear int32_t ToISOWeekOfYear(Isolate* isolate, const DateRecord& date) { TEMPORAL_ENTER_FUNC(); // 1. Assert: IsValidISODate(year, month, day) is *true*. DCHECK(IsValidISODate(isolate, date)); // 2. Let wednesday be 3. constexpr int32_t kWednesday = 3; // 3. Let thursday_ be 4. constexpr int32_t kThursday = 4; // 4. Let friday be 5. constexpr int32_t kFriday = 5; // 5. Let saturday be 6. constexpr int32_t kSaturday = 6; // 6. Let daysInWeek be 7. constexpr int32_t kDaysInWeek = 7; // 7. Let maxWeekNumber be 53. constexpr int32_t kMaxWeekNumber = 53; // 8. Let dayOfYear be ToISODayOfYear(year, month, day). int32_t day_of_year = ToISODayOfYear(isolate, date); // 9. Let dayOfWeek be ToISODayOfWeek(year, month, day). int32_t day_of_week = ToISODayOfWeek(isolate, date); // 10. Let week be floor((dayOfYear + daysInWeek - dayOfWeek + wednesday ) / // daysInWeek). int32_t week = (day_of_year + kDaysInWeek - day_of_week + kWednesday) / kDaysInWeek; // 11. If week < 1, then if (week < 1) { // a. NOTE: This is the last week of the previous year. // b. Let dayOfJan1st be ToISODayOfWeek(year, 1, 1). int32_t day_of_jan_1st = ToISODayOfWeek(isolate, {date.year, 1, 1}); // c. If dayOfJan1st is friday, then if (day_of_jan_1st == kFriday) { // a. Return maxWeekNumber. return kMaxWeekNumber; } // d. If dayOfJan1st is saturday, and InLeapYear(TimeFromYear(𝔽(year - 1))) // is *1*<sub>𝔽</sub>, then if (day_of_jan_1st == kSaturday && IsISOLeapYear(isolate, date.year - 1)) { // i. Return maxWeekNumber. return kMaxWeekNumber; } // e. Return maxWeekNumber - 1. return kMaxWeekNumber - 1; } // 12. If week is maxWeekNumber, then if (week == kMaxWeekNumber) { // a. Let daysInYear be DaysInYear(𝔽(year)). int32_t days_in_year = ISODaysInYear(isolate, date.year); // b. Let daysLaterInYear be daysInYear - dayOfYear. int32_t days_later_in_year = days_in_year - day_of_year; // c. Let daysAfterThursday be thursday - dayOfWeek. int32_t days_after_thursday = kThursday - day_of_week; // d. If daysLaterInYear < daysAfterThursday, then if (days_later_in_year < days_after_thursday) { // 1. Return 1. return 1; } } // 13. Return week. return week; } } // namespace // #sec-temporal.calendar.prototype.dateadd MaybeHandle<JSTemporalPlainDate> JSTemporalCalendar::DateAdd( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> date_obj, Handle<Object> duration_obj, Handle<Object> options_obj) { const char* method_name = "Temporal.Calendar.prototype.dateAdd"; // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. Set date to ? ToTemporalDate(date). Handle<JSTemporalPlainDate> date; ASSIGN_RETURN_ON_EXCEPTION(isolate, date, ToTemporalDate(isolate, date_obj, method_name), JSTemporalPlainDate); // 5. Set duration to ? ToTemporalDuration(duration). Handle<JSTemporalDuration> duration; ASSIGN_RETURN_ON_EXCEPTION( isolate, duration, temporal::ToTemporalDuration(isolate, duration_obj, method_name), JSTemporalPlainDate); // 6. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainDate); // 7. Let overflow be ? ToTemporalOverflow(options). ShowOverflow overflow; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, overflow, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainDate>()); // 8. Let balanceResult be ? BalanceDuration(duration.[[Days]], // duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], // duration.[[Milliseconds]], duration.[[Microseconds]], // duration.[[Nanoseconds]], "day"). TimeDurationRecord balance_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, balance_result, BalanceDuration( isolate, Unit::kDay, {Object::Number(duration->days()), Object::Number(duration->hours()), Object::Number(duration->minutes()), Object::Number(duration->seconds()), Object::Number(duration->milliseconds()), Object::Number(duration->microseconds()), Object::Number(duration->nanoseconds())}, method_name), Handle<JSTemporalPlainDate>()); DateRecord result; // If calendar.[[Identifier]] is "iso8601", then if (calendar->calendar_index() == 0) { // 9. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], // date.[[ISODay]], duration.[[Years]], duration.[[Months]], // duration.[[Weeks]], balanceResult.[[Days]], overflow). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, AddISODate(isolate, {date->iso_year(), date->iso_month(), date->iso_day()}, {Object::Number(duration->years()), Object::Number(duration->months()), Object::Number(duration->weeks()), balance_result.days}, overflow), Handle<JSTemporalPlainDate>()); } else { #ifdef V8_INTL_SUPPORT // TODO(ftang) add code for other calendar. UNIMPLEMENTED(); #else // V8_INTL_SUPPORT UNREACHABLE(); #endif // V8_INTL_SUPPORT } // 10. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], // result.[[Day]], calendar). return CreateTemporalDate(isolate, result, calendar); } // #sec-temporal.calendar.prototype.daysinyear MaybeHandle<Smi> JSTemporalCalendar::DaysInYear( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. If Type(temporalDateLike) is not Object or temporalDateLike does not // have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]] or // [[InitializedTemporalYearMonth]] internal slot, then if (!IsPlainDatePlainDateTimeOrPlainYearMonth(temporal_date_like)) { // a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_like, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.daysInYear"), Smi); } // a. Let daysInYear be ! ISODaysInYear(temporalDateLike.[[ISOYear]]). int32_t year; if (IsJSTemporalPlainDate(*temporal_date_like)) { year = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_year(); } else if (IsJSTemporalPlainDateTime(*temporal_date_like)) { year = Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_year(); } else { DCHECK(IsJSTemporalPlainYearMonth(*temporal_date_like)); year = Handle<JSTemporalPlainYearMonth>::cast(temporal_date_like)->iso_year(); } int32_t days_in_year = ISODaysInYear(isolate, year); // 6. Return 𝔽(daysInYear). return handle(Smi::FromInt(days_in_year), isolate); } // #sec-temporal.calendar.prototype.daysinmonth MaybeHandle<Smi> JSTemporalCalendar::DaysInMonth( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1 Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. If Type(temporalDateLike) is not Object or temporalDateLike does not // have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]] or // [[InitializedTemporalYearMonth]] internal slot, then if (!IsPlainDatePlainDateTimeOrPlainYearMonth(temporal_date_like)) { // a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_like, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.daysInMonth"), Smi); } // 5. Return 𝔽(! ISODaysInMonth(temporalDateLike.[[ISOYear]], // temporalDateLike.[[ISOMonth]])). int32_t year; int32_t month; if (IsJSTemporalPlainDate(*temporal_date_like)) { year = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_year(); month = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_month(); } else if (IsJSTemporalPlainDateTime(*temporal_date_like)) { year = Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_year(); month = Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_month(); } else { DCHECK(IsJSTemporalPlainYearMonth(*temporal_date_like)); year = Handle<JSTemporalPlainYearMonth>::cast(temporal_date_like)->iso_year(); month = Handle<JSTemporalPlainYearMonth>::cast(temporal_date_like)->iso_month(); } return handle(Smi::FromInt(ISODaysInMonth(isolate, year, month)), isolate); } // #sec-temporal.calendar.prototype.year MaybeHandle<Smi> JSTemporalCalendar::Year(Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. If Type(temporalDateLike) is not Object or temporalDateLike does not // have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], // or [[InitializedTemporalYearMonth]] // internal slot, then if (!IsPlainDatePlainDateTimeOrPlainYearMonth(temporal_date_like)) { // a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_like, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.year"), Smi); } // a. Let year be ! ISOYear(temporalDateLike). int32_t year; if (IsJSTemporalPlainDate(*temporal_date_like)) { year = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_year(); } else if (IsJSTemporalPlainDateTime(*temporal_date_like)) { year = Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_year(); } else { DCHECK(IsJSTemporalPlainYearMonth(*temporal_date_like)); year = Handle<JSTemporalPlainYearMonth>::cast(temporal_date_like)->iso_year(); } // 6. Return 𝔽(year). return handle(Smi::FromInt(year), isolate); } // #sec-temporal.calendar.prototype.dayofyear MaybeHandle<Smi> JSTemporalCalendar::DayOfYear( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. Let temporalDate be ? ToTemporalDate(temporalDateLike). Handle<JSTemporalPlainDate> temporal_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.dayOfYear"), Smi); // a. Let value be ! ToISODayOfYear(temporalDate.[[ISOYear]], // temporalDate.[[ISOMonth]], temporalDate.[[ISODay]]). int32_t value = ToISODayOfYear( isolate, {temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()}); return handle(Smi::FromInt(value), isolate); } // #sec-temporal.calendar.prototype.dayofweek MaybeHandle<Smi> JSTemporalCalendar::DayOfWeek( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. Let temporalDate be ? ToTemporalDate(temporalDateLike). Handle<JSTemporalPlainDate> temporal_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.dayOfWeek"), Smi); // a. Let value be ! ToISODayOfWeek(temporalDate.[[ISOYear]], // temporalDate.[[ISOMonth]], temporalDate.[[ISODay]]). int32_t value = ToISODayOfWeek( isolate, {temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()}); return handle(Smi::FromInt(value), isolate); } // #sec-temporal.calendar.prototype.monthsinyear MaybeHandle<Smi> JSTemporalCalendar::MonthsInYear( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. If Type(temporalDateLike) is not Object or temporalDateLike does not // have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or // [[InitializedTemporalYearMonth]] internal slot, then if (!IsPlainDatePlainDateTimeOrPlainYearMonth(temporal_date_like)) { // a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_like, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.monthsInYear"), Smi); } // a. a. Let monthsInYear be 12. int32_t months_in_year = 12; // 6. Return 𝔽(monthsInYear). return handle(Smi::FromInt(months_in_year), isolate); } // #sec-temporal.calendar.prototype.inleapyear MaybeHandle<Oddball> JSTemporalCalendar::InLeapYear( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. If Type(temporalDateLike) is not Object or temporalDateLike does not // have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or // [[InitializedTemporalYearMonth]] internal slot, then if (!IsPlainDatePlainDateTimeOrPlainYearMonth(temporal_date_like)) { // a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_like, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.inLeapYear"), Oddball); } // a. Let inLeapYear be ! IsISOLeapYear(temporalDateLike.[[ISOYear]]). int32_t year; if (IsJSTemporalPlainDate(*temporal_date_like)) { year = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_year(); } else if (IsJSTemporalPlainDateTime(*temporal_date_like)) { year = Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_year(); } else { DCHECK(IsJSTemporalPlainYearMonth(*temporal_date_like)); year = Handle<JSTemporalPlainYearMonth>::cast(temporal_date_like)->iso_year(); } return isolate->factory()->ToBoolean(IsISOLeapYear(isolate, year)); } // #sec-temporal.calendar.prototype.daysinweek MaybeHandle<Smi> JSTemporalCalendar::DaysInWeek( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. Perform ? ToTemporalDate(temporalDateLike). Handle<JSTemporalPlainDate> date; ASSIGN_RETURN_ON_EXCEPTION( isolate, date, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.daysInWeek"), Smi); // 5. Return 7𝔽. return handle(Smi::FromInt(7), isolate); } // #sec-temporal.calendar.prototype.datefromfields MaybeHandle<JSTemporalPlainDate> JSTemporalCalendar::DateFromFields( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> fields_obj, Handle<Object> options_obj) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. If Type(fields) is not Object, throw a TypeError exception. const char* method_name = "Temporal.Calendar.prototype.dateFromFields"; if (!IsJSReceiver(*fields_obj)) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCalledOnNonObject, isolate->factory()->NewStringFromAsciiChecked( method_name)), JSTemporalPlainDate); } Handle<JSReceiver> fields = Handle<JSReceiver>::cast(fields_obj); // 5. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainDate); if (calendar->calendar_index() == 0) { // 6. Let result be ? ISODateFromFields(fields, options). DateRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ISODateFromFields(isolate, fields, options, method_name), Handle<JSTemporalPlainDate>()); // 7. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], // result.[[Day]], calendar). return CreateTemporalDate(isolate, result, calendar); } // TODO(ftang) add intl implementation inside #ifdef V8_INTL_SUPPORT UNREACHABLE(); } // #sec-temporal.calendar.prototype.mergefields MaybeHandle<JSReceiver> JSTemporalCalendar::MergeFields( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> fields_obj, Handle<Object> additional_fields_obj) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. Set fields to ? ToObject(fields). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION(isolate, fields, Object::ToObject(isolate, fields_obj), JSReceiver); // 5. Set additionalFields to ? ToObject(additionalFields). Handle<JSReceiver> additional_fields; ASSIGN_RETURN_ON_EXCEPTION(isolate, additional_fields, Object::ToObject(isolate, additional_fields_obj), JSReceiver); // 5. If calendar.[[Identifier]] is "iso8601", then if (calendar->calendar_index() == 0) { // a. Return ? DefaultMergeFields(fields, additionalFields). return DefaultMergeFields(isolate, fields, additional_fields); } #ifdef V8_INTL_SUPPORT // TODO(ftang) add Intl code. #endif // V8_INTL_SUPPORT UNREACHABLE(); } // #sec-temporal.calendar.prototype.dateuntil MaybeHandle<JSTemporalDuration> JSTemporalCalendar::DateUntil( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> one_obj, Handle<Object> two_obj, Handle<Object> options_obj) { const char* method_name = "Temporal.Calendar.prototype.dateUntil"; // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. Set one to ? ToTemporalDate(one). Handle<JSTemporalPlainDate> one; ASSIGN_RETURN_ON_EXCEPTION(isolate, one, ToTemporalDate(isolate, one_obj, method_name), JSTemporalDuration); // 5. Set two to ? ToTemporalDate(two). Handle<JSTemporalPlainDate> two; ASSIGN_RETURN_ON_EXCEPTION(isolate, two, ToTemporalDate(isolate, two_obj, method_name), JSTemporalDuration); // 6. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalDuration); // 7. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", date, // "auto"). Unit largest_unit; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, largest_unit, GetTemporalUnit(isolate, options, "largestUnit", UnitGroup::kDate, Unit::kAuto, false, method_name), Handle<JSTemporalDuration>()); // 8. If largestUnit is "auto", set largestUnit to "day". if (largest_unit == Unit::kAuto) largest_unit = Unit::kDay; // 9. Let result be ! DifferenceISODate(one.[[ISOYear]], one.[[ISOMonth]], // one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]], // largestUnit). DateDurationRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, DifferenceISODate(isolate, {one->iso_year(), one->iso_month(), one->iso_day()}, {two->iso_year(), two->iso_month(), two->iso_day()}, largest_unit, method_name), Handle<JSTemporalDuration>()); // 10. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], // result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0). return CreateTemporalDuration(isolate, {result.years, result.months, result.weeks, {result.days, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); } // #sec-temporal.calendar.prototype.day MaybeHandle<Smi> JSTemporalCalendar::Day(Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. If Type(temporalDateLike) is not Object or temporalDateLike does not // have an [[InitializedTemporalDate]] or [[InitializedTemporalMonthDay]] // internal slot, then if (!(IsJSTemporalPlainDate(*temporal_date_like) || IsJSTemporalPlainDateTime(*temporal_date_like) || IsJSTemporalPlainMonthDay(*temporal_date_like))) { // a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_like, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.day"), Smi); } // 5. Let day be ! ISODay(temporalDateLike). int32_t day; if (IsJSTemporalPlainDate(*temporal_date_like)) { day = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_day(); } else if (IsJSTemporalPlainDateTime(*temporal_date_like)) { day = Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_day(); } else { DCHECK(IsJSTemporalPlainMonthDay(*temporal_date_like)); day = Handle<JSTemporalPlainMonthDay>::cast(temporal_date_like)->iso_day(); } // 6. Return 𝔽(day). return handle(Smi::FromInt(day), isolate); } // #sec-temporal.calendar.prototype.monthcode MaybeHandle<String> JSTemporalCalendar::MonthCode( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. If Type(temporalDateLike) is not Object or temporalDateLike does not // have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], // [[InitializedTemporalMonthDay]], or // [[InitializedTemporalYearMonth]] internal slot, then if (!(IsPlainDatePlainDateTimeOrPlainYearMonth(temporal_date_like) || IsJSTemporalPlainMonthDay(*temporal_date_like))) { // a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_like, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.monthCode"), String); } // 5. Return ! ISOMonthCode(temporalDateLike). int32_t month; if (IsJSTemporalPlainDate(*temporal_date_like)) { month = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_month(); } else if (IsJSTemporalPlainDateTime(*temporal_date_like)) { month = Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_month(); } else if (IsJSTemporalPlainMonthDay(*temporal_date_like)) { month = Handle<JSTemporalPlainMonthDay>::cast(temporal_date_like)->iso_month(); } else { DCHECK(IsJSTemporalPlainYearMonth(*temporal_date_like)); month = Handle<JSTemporalPlainYearMonth>::cast(temporal_date_like)->iso_month(); } IncrementalStringBuilder builder(isolate); builder.AppendCharacter('M'); if (month < 10) { builder.AppendCharacter('0'); } builder.AppendInt(month); return builder.Finish(); } // #sec-temporal.calendar.prototype.month MaybeHandle<Smi> JSTemporalCalendar::Month(Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 4. If Type(temporalDateLike) is Object and temporalDateLike has an // [[InitializedTemporalMonthDay]] internal slot, then if (IsJSTemporalPlainMonthDay(*temporal_date_like)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Smi); } // 5. If Type(temporalDateLike) is not Object or temporalDateLike does not // have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], // or [[InitializedTemporalYearMonth]] // internal slot, then if (!IsPlainDatePlainDateTimeOrPlainYearMonth(temporal_date_like)) { // a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_like, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.month"), Smi); } // 6. Return ! ISOMonth(temporalDateLike). int32_t month; if (IsJSTemporalPlainDate(*temporal_date_like)) { month = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_month(); } else if (IsJSTemporalPlainDateTime(*temporal_date_like)) { month = Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_month(); } else { DCHECK(IsJSTemporalPlainYearMonth(*temporal_date_like)); month = Handle<JSTemporalPlainYearMonth>::cast(temporal_date_like)->iso_month(); } // 7. Return 𝔽(month). return handle(Smi::FromInt(month), isolate); } // #sec-temporal.calendar.prototype.monthdayfromfields MaybeHandle<JSTemporalPlainMonthDay> JSTemporalCalendar::MonthDayFromFields( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> fields_obj, Handle<Object> options_obj) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". const char* method_name = "Temporal.Calendar.prototype.monthDayFromFields"; // 4. If Type(fields) is not Object, throw a TypeError exception. if (!IsJSReceiver(*fields_obj)) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCalledOnNonObject, isolate->factory()->NewStringFromAsciiChecked( method_name)), JSTemporalPlainMonthDay); } Handle<JSReceiver> fields = Handle<JSReceiver>::cast(fields_obj); // 5. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainMonthDay); // 6. Let result be ? ISOMonthDayFromFields(fields, options). if (calendar->calendar_index() == 0) { DateRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ISOMonthDayFromFields(isolate, fields, options, method_name), Handle<JSTemporalPlainMonthDay>()); // 7. Return ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], // calendar, result.[[ReferenceISOYear]]). return CreateTemporalMonthDay(isolate, result.month, result.day, calendar, result.year); } // TODO(ftang) add intl code inside #ifdef V8_INTL_SUPPORT UNREACHABLE(); } // #sec-temporal.calendar.prototype.yearmonthfromfields MaybeHandle<JSTemporalPlainYearMonth> JSTemporalCalendar::YearMonthFromFields( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> fields_obj, Handle<Object> options_obj) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". const char* method_name = "Temporal.Calendar.prototype.yearMonthFromFields"; // 4. If Type(fields) is not Object, throw a TypeError exception. if (!IsJSReceiver(*fields_obj)) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCalledOnNonObject, isolate->factory()->NewStringFromAsciiChecked( method_name)), JSTemporalPlainYearMonth); } Handle<JSReceiver> fields = Handle<JSReceiver>::cast(fields_obj); // 5. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainYearMonth); // 6. Let result be ? ISOYearMonthFromFields(fields, options). if (calendar->calendar_index() == 0) { DateRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ISOYearMonthFromFields(isolate, fields, options, method_name), Handle<JSTemporalPlainYearMonth>()); // 7. Return ? CreateTemporalYearMonth(result.[[Year]], result.[[Month]], // calendar, result.[[ReferenceISODay]]). return CreateTemporalYearMonth(isolate, result.year, result.month, calendar, result.day); } // TODO(ftang) add intl code inside #ifdef V8_INTL_SUPPORT UNREACHABLE(); } #ifdef V8_INTL_SUPPORT // #sup-temporal.calendar.prototype.era MaybeHandle<Object> JSTemporalCalendar::Era(Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. If Type(temporalDateLike) is not Object or temporalDateLike does not // have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], // or [[InitializedTemporalYearMonth]] // internal slot, then if (!IsPlainDatePlainDateTimeOrPlainYearMonth(temporal_date_like)) { // a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_like, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.era"), Object); } // 4. If calendar.[[Identifier]] is "iso8601", then if (calendar->calendar_index() == 0) { // a. Return undefined. return isolate->factory()->undefined_value(); } UNIMPLEMENTED(); // TODO(ftang) implement other calendars // 5. Return ! CalendarDateEra(calendar.[[Identifier]], temporalDateLike). } // #sup-temporal.calendar.prototype.erayear MaybeHandle<Object> JSTemporalCalendar::EraYear( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. If Type(temporalDateLike) is not Object or temporalDateLike does not // have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], // or [[InitializedTemporalYearMonth]] // internal slot, then if (!IsPlainDatePlainDateTimeOrPlainYearMonth(temporal_date_like)) { // a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_like, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.eraYear"), Object); } // 4. If calendar.[[Identifier]] is "iso8601", then if (calendar->calendar_index() == 0) { // a. Return undefined. return isolate->factory()->undefined_value(); } UNIMPLEMENTED(); // TODO(ftang) implement other calendars // 5. Let eraYear be ! CalendarDateEraYear(calendar.[[Identifier]], // temporalDateLike). // 6. If eraYear is undefined, then // a. Return undefined. // 7. Return 𝔽(eraYear). } #endif // V8_INTL_SUPPORT // #sec-temporal.calendar.prototype.weekofyear MaybeHandle<Smi> JSTemporalCalendar::WeekOfYear( Isolate* isolate, Handle<JSTemporalCalendar> calendar, Handle<Object> temporal_date_like) { // 1. Let calendar be the this value. // 2. Perform ? RequireInternalSlot(calendar, // [[InitializedTemporalCalendar]]). // 3. Assert: calendar.[[Identifier]] is "iso8601". // 4. Let temporalDate be ? ToTemporalDate(temporalDateLike). Handle<JSTemporalPlainDate> temporal_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date, ToTemporalDate(isolate, temporal_date_like, "Temporal.Calendar.prototype.weekOfYear"), Smi); // a. Let value be ! ToISOWeekOfYear(temporalDate.[[ISOYear]], // temporalDate.[[ISOMonth]], temporalDate.[[ISODay]]). int32_t value = ToISOWeekOfYear( isolate, {temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()}); return handle(Smi::FromInt(value), isolate); } // #sec-temporal.calendar.prototype.tostring MaybeHandle<String> JSTemporalCalendar::ToString( Isolate* isolate, Handle<JSTemporalCalendar> calendar, const char* method_name) { return CalendarIdentifier(isolate, calendar->calendar_index()); } // #sec-temporal.now.timezone MaybeHandle<JSTemporalTimeZone> JSTemporalTimeZone::Now(Isolate* isolate) { return SystemTimeZone(isolate); } // #sec-temporal.timezone MaybeHandle<JSTemporalTimeZone> JSTemporalTimeZone::Constructor( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<Object> identifier_obj) { // 1. If NewTarget is undefined, then if (IsUndefined(*new_target, isolate)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, isolate->factory()->NewStringFromAsciiChecked( "Temporal.TimeZone")), JSTemporalTimeZone); } // 2. Set identifier to ? ToString(identifier). Handle<String> identifier; ASSIGN_RETURN_ON_EXCEPTION(isolate, identifier, Object::ToString(isolate, identifier_obj), JSTemporalTimeZone); Handle<String> canonical; // 3. If identifier satisfies the syntax of a TimeZoneNumericUTCOffset // (see 13.33), then if (IsValidTimeZoneNumericUTCOffsetString(isolate, identifier)) { // a. Let offsetNanoseconds be ? ParseTimeZoneOffsetString(identifier). int64_t offset_nanoseconds; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_nanoseconds, ParseTimeZoneOffsetString(isolate, identifier), Handle<JSTemporalTimeZone>()); // b. Let canonical be ! FormatTimeZoneOffsetString(offsetNanoseconds). canonical = FormatTimeZoneOffsetString(isolate, offset_nanoseconds); } else { // 4. Else, // a. If ! IsValidTimeZoneName(identifier) is false, then if (!IsValidTimeZoneName(isolate, identifier)) { // i. Throw a RangeError exception. THROW_NEW_ERROR( isolate, NewRangeError(MessageTemplate::kInvalidTimeZone, identifier), JSTemporalTimeZone); } // b. Let canonical be ! CanonicalizeTimeZoneName(identifier). canonical = CanonicalizeTimeZoneName(isolate, identifier); } // 5. Return ? CreateTemporalTimeZone(canonical, NewTarget). return CreateTemporalTimeZone(isolate, target, new_target, canonical); } namespace { MaybeHandle<JSTemporalPlainDateTime> ToTemporalDateTime( Isolate* isolate, Handle<Object> item_obj, Handle<Object> options, const char* method_name); MaybeHandle<JSTemporalPlainDateTime> ToTemporalDateTime( Isolate* isolate, Handle<Object> item_obj, const char* method_name) { // 1. If options is not present, set options to undefined. return ToTemporalDateTime(isolate, item_obj, isolate->factory()->undefined_value(), method_name); } } // namespace // #sec-temporal.timezone.prototype.getinstantfor MaybeHandle<JSTemporalInstant> JSTemporalTimeZone::GetInstantFor( Isolate* isolate, Handle<JSTemporalTimeZone> time_zone, Handle<Object> date_time_obj, Handle<Object> options_obj) { const char* method_name = "Temporal.TimeZone.prototype.getInstantFor"; // 1. Let timeZone be the this value. // 2. Perform ? RequireInternalSlot(timeZone, // [[InitializedTemporalTimeZone]]). // 3. Set dateTime to ? ToTemporalDateTime(dateTime). Handle<JSTemporalPlainDateTime> date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, date_time, ToTemporalDateTime(isolate, date_time_obj, method_name), JSTemporalInstant); // 4. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalInstant); // 5. Let disambiguation be ? ToTemporalDisambiguation(options). Disambiguation disambiguation; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, disambiguation, ToTemporalDisambiguation(isolate, options, method_name), Handle<JSTemporalInstant>()); // 6. Return ? BuiltinTimeZoneGetInstantFor(timeZone, dateTime, // disambiguation). return BuiltinTimeZoneGetInstantFor(isolate, time_zone, date_time, disambiguation, method_name); } namespace { #ifdef V8_INTL_SUPPORT Handle<Object> GetIANATimeZoneTransition(Isolate* isolate, Handle<BigInt> nanoseconds, int32_t time_zone_index, Intl::Transition transition) { if (time_zone_index == JSTemporalTimeZone::kUTCTimeZoneIndex) { return isolate->factory()->null_value(); } return Intl::GetTimeZoneOffsetTransitionNanoseconds(isolate, time_zone_index, nanoseconds, transition); } // #sec-temporal-getianatimezonenexttransition Handle<Object> GetIANATimeZoneNextTransition(Isolate* isolate, Handle<BigInt> nanoseconds, int32_t time_zone_index) { return GetIANATimeZoneTransition(isolate, nanoseconds, time_zone_index, Intl::Transition::kNext); } // #sec-temporal-getianatimezoneprevioustransition Handle<Object> GetIANATimeZonePreviousTransition(Isolate* isolate, Handle<BigInt> nanoseconds, int32_t time_zone_index) { return GetIANATimeZoneTransition(isolate, nanoseconds, time_zone_index, Intl::Transition::kPrevious); } Handle<Object> GetIANATimeZoneOffsetNanoseconds(Isolate* isolate, Handle<BigInt> nanoseconds, int32_t time_zone_index) { if (time_zone_index == JSTemporalTimeZone::kUTCTimeZoneIndex) { return handle(Smi::zero(), isolate); } return isolate->factory()->NewNumberFromInt64( Intl::GetTimeZoneOffsetNanoseconds(isolate, time_zone_index, nanoseconds)); } #else // V8_INTL_SUPPORT // #sec-temporal-getianatimezonenexttransition Handle<Object> GetIANATimeZoneNextTransition(Isolate* isolate, Handle<BigInt>, int32_t) { return isolate->factory()->null_value(); } // #sec-temporal-getianatimezoneprevioustransition Handle<Object> GetIANATimeZonePreviousTransition(Isolate* isolate, Handle<BigInt>, int32_t) { return isolate->factory()->null_value(); } Handle<Object> GetIANATimeZoneOffsetNanoseconds(Isolate* isolate, Handle<BigInt>, int32_t time_zone_index) { DCHECK_EQ(time_zone_index, JSTemporalTimeZone::kUTCTimeZoneIndex); return handle(Smi::zero(), isolate); } #endif // V8_INTL_SUPPORT } // namespace // #sec-temporal.timezone.prototype.getplaindatetimefor MaybeHandle<JSTemporalPlainDateTime> JSTemporalTimeZone::GetPlainDateTimeFor( Isolate* isolate, Handle<JSTemporalTimeZone> time_zone, Handle<Object> instant_obj, Handle<Object> calendar_like) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.TimeZone.prototype.getPlainDateTimeFor"; // 1. 1. Let timeZone be the this value. // 2. Perform ? RequireInternalSlot(timeZone, // [[InitializedTemporalTimeZone]]). // 3. Set instant to ? ToTemporalInstant(instant). Handle<JSTemporalInstant> instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, instant, ToTemporalInstant(isolate, instant_obj, method_name), JSTemporalPlainDateTime); // 4. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, calendar_like, method_name), JSTemporalPlainDateTime); // 5. Return ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, // calendar). return temporal::BuiltinTimeZoneGetPlainDateTimeFor( isolate, time_zone, instant, calendar, method_name); } // template for shared code of Temporal.TimeZone.prototype.getNextTransition and // Temporal.TimeZone.prototype.getPreviousTransition template <Handle<Object> (*iana_func)(Isolate*, Handle<BigInt>, int32_t)> MaybeHandle<Object> GetTransition(Isolate* isolate, Handle<JSTemporalTimeZone> time_zone, Handle<Object> starting_point_obj, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. Let timeZone be the this value. // 2. Perform ? RequireInternalSlot(timeZone, // [[InitializedTemporalTimeZone]]). // 3. Set startingPoint to ? ToTemporalInstant(startingPoint). Handle<JSTemporalInstant> starting_point; ASSIGN_RETURN_ON_EXCEPTION( isolate, starting_point, ToTemporalInstant(isolate, starting_point_obj, method_name), Object); // 4. If timeZone.[[OffsetNanoseconds]] is not undefined, return null. if (time_zone->is_offset()) { return isolate->factory()->null_value(); } // 5. Let transition be ? // GetIANATimeZoneNextTransition(startingPoint.[[Nanoseconds]], // timeZone.[[Identifier]]). Handle<Object> transition_obj = iana_func(isolate, handle(starting_point->nanoseconds(), isolate), time_zone->time_zone_index()); // 6. If transition is null, return null. if (IsNull(*transition_obj)) { return isolate->factory()->null_value(); } DCHECK(IsBigInt(*transition_obj)); Handle<BigInt> transition = Handle<BigInt>::cast(transition_obj); // 7. Return ! CreateTemporalInstant(transition). return temporal::CreateTemporalInstant(isolate, transition).ToHandleChecked(); } // #sec-temporal.timezone.prototype.getnexttransition MaybeHandle<Object> JSTemporalTimeZone::GetNextTransition( Isolate* isolate, Handle<JSTemporalTimeZone> time_zone, Handle<Object> starting_point_obj) { return GetTransition<GetIANATimeZoneNextTransition>( isolate, time_zone, starting_point_obj, "Temporal.TimeZone.prototype.getNextTransition"); } // #sec-temporal.timezone.prototype.getprevioustransition MaybeHandle<Object> JSTemporalTimeZone::GetPreviousTransition( Isolate* isolate, Handle<JSTemporalTimeZone> time_zone, Handle<Object> starting_point_obj) { return GetTransition<GetIANATimeZonePreviousTransition>( isolate, time_zone, starting_point_obj, "Temporal.TimeZone.prototype.getPreviousTransition"); } // #sec-temporal.timezone.prototype.getpossibleinstantsfor // #sec-temporal-getianatimezoneepochvalue MaybeHandle<JSArray> GetIANATimeZoneEpochValueAsArrayOfInstantForUTC( Isolate* isolate, const DateTimeRecord& date_time) { Factory* factory = isolate->factory(); // 6. Let possibleInstants be a new empty List. Handle<BigInt> epoch_nanoseconds = GetEpochFromISOParts(isolate, date_time); Handle<FixedArray> fixed_array = factory->NewFixedArray(1); // 7. For each value epochNanoseconds in possibleEpochNanoseconds, do // a. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a // RangeError exception. if (!IsValidEpochNanoseconds(isolate, epoch_nanoseconds)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSArray); } // b. Let instant be ! CreateTemporalInstant(epochNanoseconds). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant(isolate, epoch_nanoseconds) .ToHandleChecked(); // c. Append instant to possibleInstants. fixed_array->set(0, *instant); // 8. Return ! CreateArrayFromList(possibleInstants). return factory->NewJSArrayWithElements(fixed_array); } #ifdef V8_INTL_SUPPORT MaybeHandle<JSArray> GetIANATimeZoneEpochValueAsArrayOfInstant( Isolate* isolate, int32_t time_zone_index, const DateTimeRecord& date_time) { Factory* factory = isolate->factory(); if (time_zone_index == JSTemporalTimeZone::kUTCTimeZoneIndex) { return GetIANATimeZoneEpochValueAsArrayOfInstantForUTC(isolate, date_time); } // For TimeZone other than UTC, call ICU indirectly from Intl Handle<BigInt> nanoseconds_in_local_time = GetEpochFromISOParts(isolate, date_time); std::vector<Handle<BigInt>> possible_offset = Intl::GetTimeZonePossibleOffsetNanoseconds(isolate, time_zone_index, nanoseconds_in_local_time); int32_t array_length = static_cast<int32_t>(possible_offset.size()); Handle<FixedArray> fixed_array = factory->NewFixedArray(array_length); for (int32_t i = 0; i < array_length; i++) { Handle<BigInt> epoch_nanoseconds = BigInt::Subtract(isolate, nanoseconds_in_local_time, possible_offset[i]) .ToHandleChecked(); // a. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a // RangeError exception. if (!IsValidEpochNanoseconds(isolate, epoch_nanoseconds)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSArray); } // b. Let instant be ! CreateTemporalInstant(epochNanoseconds). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant(isolate, epoch_nanoseconds) .ToHandleChecked(); // b. Append instant to possibleInstants. fixed_array->set(i, *(instant)); } // 8. Return ! CreateArrayFromList(possibleInstants). return factory->NewJSArrayWithElements(fixed_array); } #else // V8_INTL_SUPPORT MaybeHandle<JSArray> GetIANATimeZoneEpochValueAsArrayOfInstant( Isolate* isolate, int32_t time_zone_index, const DateTimeRecord& date_time) { DCHECK_EQ(time_zone_index, JSTemporalTimeZone::kUTCTimeZoneIndex); return GetIANATimeZoneEpochValueAsArrayOfInstantForUTC(isolate, date_time); } #endif // V8_INTL_SUPPORT // #sec-temporal.timezone.prototype.getpossibleinstantsfor MaybeHandle<JSArray> JSTemporalTimeZone::GetPossibleInstantsFor( Isolate* isolate, Handle<JSTemporalTimeZone> time_zone, Handle<Object> date_time_obj) { Factory* factory = isolate->factory(); // 1. Let timeZone be the this value. // 2. Perform ? RequireInternalSlot(timeZone, // [[InitializedTemporalTimezone]]). // 3. Set dateTime to ? ToTemporalDateTime(dateTime). Handle<JSTemporalPlainDateTime> date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, date_time, ToTemporalDateTime(isolate, date_time_obj, "Temporal.TimeZone.prototype.getPossibleInstantsFor"), JSArray); DateTimeRecord date_time_record = { {date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}; // 4. If timeZone.[[OffsetNanoseconds]] is not undefined, then if (time_zone->is_offset()) { // a. Let epochNanoseconds be ! GetEpochFromISOParts(dateTime.[[ISOYear]], // dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], // dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], // dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], // dateTime.[[ISONanosecond]]). Handle<BigInt> epoch_nanoseconds = GetEpochFromISOParts(isolate, date_time_record); // b. Let possibleEpochNanoseconds be « epochNanoseconds - // ℤ(timeZone.[[OffsetNanoseconds]]) ». epoch_nanoseconds = BigInt::Subtract( isolate, epoch_nanoseconds, BigInt::FromInt64(isolate, time_zone->offset_nanoseconds())) .ToHandleChecked(); // The following is the step 7 and 8 for the case of step 4 under the if // block. // a. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a // RangeError exception. if (!IsValidEpochNanoseconds(isolate, epoch_nanoseconds)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSArray); } // b. Let instant be ! CreateTemporalInstant(epochNanoseconds). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant(isolate, epoch_nanoseconds) .ToHandleChecked(); // c. Return ! CreateArrayFromList(« instant »). Handle<FixedArray> fixed_array = factory->NewFixedArray(1); fixed_array->set(0, *instant); return factory->NewJSArrayWithElements(fixed_array); } // 5. Let possibleEpochNanoseconds be ? // GetIANATimeZoneEpochValue(timeZone.[[Identifier]], dateTime.[[ISOYear]], // dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], // dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], // dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], // dateTime.[[ISONanosecond]]). // ... Step 5-8 put into GetIANATimeZoneEpochValueAsArrayOfInstant // 8. Return ! CreateArrayFromList(possibleInstants). return GetIANATimeZoneEpochValueAsArrayOfInstant( isolate, time_zone->time_zone_index(), date_time_record); } // #sec-temporal.timezone.prototype.getoffsetnanosecondsfor MaybeHandle<Object> JSTemporalTimeZone::GetOffsetNanosecondsFor( Isolate* isolate, Handle<JSTemporalTimeZone> time_zone, Handle<Object> instant_obj) { TEMPORAL_ENTER_FUNC(); // 1. Let timeZone be the this value. // 2. Perform ? RequireInternalSlot(timeZone, // [[InitializedTemporalTimeZone]]). // 3. Set instant to ? ToTemporalInstant(instant). Handle<JSTemporalInstant> instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, instant, ToTemporalInstant(isolate, instant_obj, "Temporal.TimeZone.prototype.getOffsetNanosecondsFor"), Smi); // 4. If timeZone.[[OffsetNanoseconds]] is not undefined, return // timeZone.[[OffsetNanoseconds]]. if (time_zone->is_offset()) { return isolate->factory()->NewNumberFromInt64( time_zone->offset_nanoseconds()); } // 5. Return ! GetIANATimeZoneOffsetNanoseconds(instant.[[Nanoseconds]], // timeZone.[[Identifier]]). return GetIANATimeZoneOffsetNanoseconds( isolate, handle(instant->nanoseconds(), isolate), time_zone->time_zone_index()); } // #sec-temporal.timezone.prototype.getoffsetstringfor MaybeHandle<String> JSTemporalTimeZone::GetOffsetStringFor( Isolate* isolate, Handle<JSTemporalTimeZone> time_zone, Handle<Object> instant_obj) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.TimeZone.prototype.getOffsetStringFor"; // 1. Let timeZone be the this value. // 2. Perform ? RequireInternalSlot(timeZone, // [[InitializedTemporalTimeZone]]). // 3. Set instant to ? ToTemporalInstant(instant). Handle<JSTemporalInstant> instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, instant, ToTemporalInstant(isolate, instant_obj, method_name), String); // 4. Return ? BuiltinTimeZoneGetOffsetStringFor(timeZone, instant). return BuiltinTimeZoneGetOffsetStringFor(isolate, time_zone, instant, method_name); } // #sec-temporal.timezone.prototype.tostring MaybeHandle<Object> JSTemporalTimeZone::ToString( Isolate* isolate, Handle<JSTemporalTimeZone> time_zone, const char* method_name) { return time_zone->id(isolate); } int32_t JSTemporalTimeZone::time_zone_index() const { DCHECK(is_offset() == false); return offset_milliseconds_or_time_zone_index(); } int64_t JSTemporalTimeZone::offset_nanoseconds() const { TEMPORAL_ENTER_FUNC(); DCHECK(is_offset()); return static_cast<int64_t>(offset_milliseconds()) * 1000000 + static_cast<int64_t>(offset_sub_milliseconds()); } void JSTemporalTimeZone::set_offset_nanoseconds(int64_t ns) { this->set_offset_milliseconds(static_cast<int32_t>(ns / 1000000)); this->set_offset_sub_milliseconds(static_cast<int32_t>(ns % 1000000)); } MaybeHandle<String> JSTemporalTimeZone::id(Isolate* isolate) const { if (is_offset()) { return FormatTimeZoneOffsetString(isolate, offset_nanoseconds()); } #ifdef V8_INTL_SUPPORT std::string id = Intl::TimeZoneIdFromIndex(offset_milliseconds_or_time_zone_index()); return isolate->factory()->NewStringFromAsciiChecked(id.c_str()); #else // V8_INTL_SUPPORT DCHECK_EQ(kUTCTimeZoneIndex, offset_milliseconds_or_time_zone_index()); return isolate->factory()->UTC_string(); #endif // V8_INTL_SUPPORT } MaybeHandle<JSTemporalPlainDate> JSTemporalPlainDate::Constructor( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<Object> iso_year_obj, Handle<Object> iso_month_obj, Handle<Object> iso_day_obj, Handle<Object> calendar_like) { const char* method_name = "Temporal.PlainDate"; // 1. If NewTarget is undefined, throw a TypeError exception. if (IsUndefined(*new_target)) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, isolate->factory()->NewStringFromAsciiChecked( method_name)), JSTemporalPlainDate); } #define TO_INT_THROW_ON_INFTY(name, T) \ int32_t name; \ { \ Handle<Object> number_##name; \ /* x. Let name be ? ToIntegerThrowOnInfinity(name). */ \ ASSIGN_RETURN_ON_EXCEPTION(isolate, number_##name, \ ToIntegerThrowOnInfinity(isolate, name##_obj), \ T); \ name = NumberToInt32(*number_##name); \ } TO_INT_THROW_ON_INFTY(iso_year, JSTemporalPlainDate); TO_INT_THROW_ON_INFTY(iso_month, JSTemporalPlainDate); TO_INT_THROW_ON_INFTY(iso_day, JSTemporalPlainDate); // 8. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, calendar_like, method_name), JSTemporalPlainDate); // 9. Return ? CreateTemporalDate(y, m, d, calendar, NewTarget). return CreateTemporalDate(isolate, target, new_target, {iso_year, iso_month, iso_day}, calendar); } // #sec-temporal.plaindate.compare MaybeHandle<Smi> JSTemporalPlainDate::Compare(Isolate* isolate, Handle<Object> one_obj, Handle<Object> two_obj) { const char* method_name = "Temporal.PlainDate.compare"; // 1. Set one to ? ToTemporalDate(one). Handle<JSTemporalPlainDate> one; ASSIGN_RETURN_ON_EXCEPTION( isolate, one, ToTemporalDate(isolate, one_obj, method_name), Smi); // 2. Set two to ? ToTemporalDate(two). Handle<JSTemporalPlainDate> two; ASSIGN_RETURN_ON_EXCEPTION( isolate, two, ToTemporalDate(isolate, two_obj, method_name), Smi); // 3. Return 𝔽(! CompareISODate(one.[[ISOYear]], one.[[ISOMonth]], // one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]])). return Handle<Smi>(Smi::FromInt(CompareISODate( {one->iso_year(), one->iso_month(), one->iso_day()}, {two->iso_year(), two->iso_month(), two->iso_day()})), isolate); } // #sec-temporal.plaindate.prototype.equals MaybeHandle<Oddball> JSTemporalPlainDate::Equals( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date, Handle<Object> other_obj) { Factory* factory = isolate->factory(); // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(temporalDate, // [[InitializedTemporalDate]]). // 3. Set other to ? ToTemporalDate(other). Handle<JSTemporalPlainDate> other; ASSIGN_RETURN_ON_EXCEPTION( isolate, other, ToTemporalDate(isolate, other_obj, "Temporal.PlainDate.prototype.equals"), Oddball); // 4. If temporalDate.[[ISOYear]] ≠ other.[[ISOYear]], return false. if (temporal_date->iso_year() != other->iso_year()) { return factory->false_value(); } // 5. If temporalDate.[[ISOMonth]] ≠ other.[[ISOMonth]], return false. if (temporal_date->iso_month() != other->iso_month()) { return factory->false_value(); } // 6. If temporalDate.[[ISODay]] ≠ other.[[ISODay]], return false. if (temporal_date->iso_day() != other->iso_day()) { return factory->false_value(); } // 7. Return ? CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]). return CalendarEquals(isolate, handle(temporal_date->calendar(), isolate), handle(other->calendar(), isolate)); } // #sec-temporal.plaindate.prototype.withcalendar MaybeHandle<JSTemporalPlainDate> JSTemporalPlainDate::WithCalendar( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date, Handle<Object> calendar_like) { const char* method_name = "Temporal.PlainDate.prototype.withCalendar"; // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(temporalDate, // [[InitializedTemporalDate]]). // 3. Let calendar be ? ToTemporalCalendar(calendar). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, temporal::ToTemporalCalendar(isolate, calendar_like, method_name), JSTemporalPlainDate); // 4. Return ? CreateTemporalDate(temporalDate.[[ISOYear]], // temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], calendar). return CreateTemporalDate( isolate, {temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()}, calendar); } // Template for common code shared by // Temporal.PlainDate(Timne)?.prototype.toPlain(YearMonth|MonthDay) // #sec-temporal.plaindate.prototype.toplainmonthday // #sec-temporal.plaindate.prototype.toplainyearmonth // #sec-temporal.plaindatetime.prototype.toplainmonthday // #sec-temporal.plaindatetime.prototype.toplainyearmonth template <typename T, typename R, MaybeHandle<R> (*from_fields)(Isolate*, Handle<JSReceiver>, Handle<JSReceiver>, Handle<Object>)> MaybeHandle<R> ToPlain(Isolate* isolate, Handle<T> t, Handle<String> f1, Handle<String> f2) { Factory* factory = isolate->factory(); // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(t, [[InitializedTemporalDate]]). // 3. Let calendar be t.[[Calendar]]. Handle<JSReceiver> calendar(t->calendar(), isolate); // 4. Let fieldNames be ? CalendarFields(calendar, « f1 , f2 »). Handle<FixedArray> field_names = factory->NewFixedArray(2); field_names->set(0, *f1); field_names->set(1, *f2); ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, CalendarFields(isolate, calendar, field_names), R); // 5. Let fields be ? PrepareTemporalFields(t, fieldNames, «»). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, PrepareTemporalFields(isolate, t, field_names, RequiredFields::kNone), R); // 6. Return ? FromFields(calendar, fields). return from_fields(isolate, calendar, fields, isolate->factory()->undefined_value()); } // #sec-temporal.plaindate.prototype.toplainyearmonth MaybeHandle<JSTemporalPlainYearMonth> JSTemporalPlainDate::ToPlainYearMonth( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date) { return ToPlain<JSTemporalPlainDate, JSTemporalPlainYearMonth, YearMonthFromFields>(isolate, temporal_date, isolate->factory()->monthCode_string(), isolate->factory()->year_string()); } // #sec-temporal.plaindate.prototype.toplainmonthday MaybeHandle<JSTemporalPlainMonthDay> JSTemporalPlainDate::ToPlainMonthDay( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date) { return ToPlain<JSTemporalPlainDate, JSTemporalPlainMonthDay, MonthDayFromFields>(isolate, temporal_date, isolate->factory()->day_string(), isolate->factory()->monthCode_string()); } // #sec-temporal.plaindate.prototype.toplaindatetime MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainDate::ToPlainDateTime( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date, Handle<Object> temporal_time_obj) { // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(temporalDate, // [[InitializedTemporalDate]]). // 3. If temporalTime is undefined, then if (IsUndefined(*temporal_time_obj)) { // a. Return ? CreateTemporalDateTime(temporalDate.[[ISOYear]], // temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], 0, 0, 0, 0, 0, 0, // temporalDate.[[Calendar]]). return temporal::CreateTemporalDateTime( isolate, {{temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()}, {0, 0, 0, 0, 0, 0}}, Handle<JSReceiver>(temporal_date->calendar(), isolate)); } // 4. Set temporalTime to ? ToTemporalTime(temporalTime). Handle<JSTemporalPlainTime> temporal_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_time, temporal::ToTemporalTime(isolate, temporal_time_obj, "Temporal.PlainDate.prototype.toPlainDateTime"), JSTemporalPlainDateTime); // 5. Return ? CreateTemporalDateTime(temporalDate.[[ISOYear]], // temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], // temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], // temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], // temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], // temporalDate.[[Calendar]]). return temporal::CreateTemporalDateTime( isolate, {{temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()}, {temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond()}}, Handle<JSReceiver>(temporal_date->calendar(), isolate)); } namespace { // #sec-temporal-rejectobjectwithcalendarortimezone Maybe<bool> RejectObjectWithCalendarOrTimeZone(Isolate* isolate, Handle<JSReceiver> object) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 1. Assert: Type(object) is Object. // 2. If object has an [[InitializedTemporalDate]], // [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], // [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or // [[InitializedTemporalZonedDateTime]] internal slot, then if (IsJSTemporalPlainDate(*object) || IsJSTemporalPlainDateTime(*object) || IsJSTemporalPlainMonthDay(*object) || IsJSTemporalPlainTime(*object) || IsJSTemporalPlainYearMonth(*object) || IsJSTemporalZonedDateTime(*object)) { // a. Throw a TypeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Nothing<bool>()); } // 3. Let calendarProperty be ? Get(object, "calendar"). Handle<Object> calendar_property; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, calendar_property, JSReceiver::GetProperty(isolate, object, factory->calendar_string()), Nothing<bool>()); // 4. If calendarProperty is not undefined, then if (!IsUndefined(*calendar_property)) { // a. Throw a TypeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Nothing<bool>()); } // 5. Let timeZoneProperty be ? Get(object, "timeZone"). Handle<Object> time_zone_property; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, time_zone_property, JSReceiver::GetProperty(isolate, object, factory->timeZone_string()), Nothing<bool>()); // 6. If timeZoneProperty is not undefined, then if (!IsUndefined(*time_zone_property)) { // a. Throw a TypeError exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), Nothing<bool>()); } return Just(true); } // #sec-temporal-calendarmergefields MaybeHandle<JSReceiver> CalendarMergeFields( Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSReceiver> fields, Handle<JSReceiver> additional_fields) { // 1. Let mergeFields be ? GetMethod(calendar, "mergeFields"). Handle<Object> merge_fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, merge_fields, Object::GetMethod(isolate, calendar, isolate->factory()->mergeFields_string()), JSReceiver); // 2. If mergeFields is undefined, then if (IsUndefined(*merge_fields)) { // a. Return ? DefaultMergeFields(fields, additionalFields). return DefaultMergeFields(isolate, fields, additional_fields); } // 3. Return ? Call(mergeFields, calendar, « fields, additionalFields »). Handle<Object> argv[] = {fields, additional_fields}; Handle<Object> result; ASSIGN_RETURN_ON_EXCEPTION( isolate, result, Execution::Call(isolate, merge_fields, calendar, 2, argv), JSReceiver); // 4. If Type(result) is not Object, throw a TypeError exception. if (!IsJSReceiver(*result)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalDuration); } return Handle<JSReceiver>::cast(result); } // Common code shared by Temporal.Plain(Date|YearMonth|MonthDay).prototype.with template <typename T, MaybeHandle<T> (*from_fields_func)( Isolate*, Handle<JSReceiver>, Handle<JSReceiver>, Handle<Object>)> MaybeHandle<T> PlainDateOrYearMonthOrMonthDayWith( Isolate* isolate, Handle<T> temporal, Handle<Object> temporal_like_obj, Handle<Object> options_obj, Handle<FixedArray> field_names, const char* method_name) { // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(temporalDate, // [[InitializedTemporalXXX]]). // 3. If Type(temporalXXXLike) is not Object, then if (!IsJSReceiver(*temporal_like_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), T); } Handle<JSReceiver> temporal_like = Handle<JSReceiver>::cast(temporal_like_obj); // 4. Perform ? RejectObjectWithCalendarOrTimeZone(temporalXXXLike). MAYBE_RETURN(RejectObjectWithCalendarOrTimeZone(isolate, temporal_like), Handle<T>()); // 5. Let calendar be temporalXXX.[[Calendar]]. Handle<JSReceiver> calendar(temporal->calendar(), isolate); // 6. Let fieldNames be ? CalendarFields(calendar, fieldNames). ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, CalendarFields(isolate, calendar, field_names), T); // 7. Let partialDate be ? PreparePartialTemporalFields(temporalXXXLike, // fieldNames). Handle<JSReceiver> partial_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, partial_date, PreparePartialTemporalFields(isolate, temporal_like, field_names), T); // 8. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), T); // 9. Let fields be ? PrepareTemporalFields(temporalXXX, fieldNames, «»). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, PrepareTemporalFields(isolate, temporal, field_names, RequiredFields::kNone), T); // 10. Set fields to ? CalendarMergeFields(calendar, fields, partialDate). ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, CalendarMergeFields(isolate, calendar, fields, partial_date), T); // 11. Set fields to ? PrepareTemporalFields(fields, fieldNames, «»). ASSIGN_RETURN_ON_EXCEPTION(isolate, fields, PrepareTemporalFields(isolate, fields, field_names, RequiredFields::kNone), T); // 12. Return ? XxxFromFields(calendar, fields, options). return from_fields_func(isolate, calendar, fields, options); } } // namespace // #sec-temporal.plaindate.prototype.with MaybeHandle<JSTemporalPlainDate> JSTemporalPlainDate::With( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date, Handle<Object> temporal_date_like_obj, Handle<Object> options_obj) { // 6. Let fieldNames be ? CalendarFields(calendar, « "day", "month", // "monthCode", "year" »). Handle<FixedArray> field_names = DayMonthMonthCodeYearInFixedArray(isolate); return PlainDateOrYearMonthOrMonthDayWith<JSTemporalPlainDate, DateFromFields>( isolate, temporal_date, temporal_date_like_obj, options_obj, field_names, "Temporal.PlainDate.prototype.with"); } // #sec-temporal.plaindate.prototype.tozoneddatetime MaybeHandle<JSTemporalZonedDateTime> JSTemporalPlainDate::ToZonedDateTime( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date, Handle<Object> item_obj) { const char* method_name = "Temporal.PlainDate.prototype.toZonedDateTime"; Factory* factory = isolate->factory(); // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(temporalDate, // [[InitializedTemporalDate]]). // 3. If Type(item) is Object, then Handle<JSReceiver> time_zone; Handle<Object> temporal_time_obj; if (IsJSReceiver(*item_obj)) { Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); // a. Let timeZoneLike be ? Get(item, "timeZone"). Handle<Object> time_zone_like; ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone_like, JSReceiver::GetProperty(isolate, item, factory->timeZone_string()), JSTemporalZonedDateTime); // b. If timeZoneLike is undefined, then if (IsUndefined(*time_zone_like)) { // i. Let timeZone be ? ToTemporalTimeZone(item). ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone, temporal::ToTemporalTimeZone(isolate, item, method_name), JSTemporalZonedDateTime); // ii. Let temporalTime be undefined. temporal_time_obj = factory->undefined_value(); // c. Else, } else { // i. Let timeZone be ? ToTemporalTimeZone(timeZoneLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone, temporal::ToTemporalTimeZone(isolate, time_zone_like, method_name), JSTemporalZonedDateTime); // ii. Let temporalTime be ? Get(item, "plainTime"). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_time_obj, JSReceiver::GetProperty(isolate, item, factory->plainTime_string()), JSTemporalZonedDateTime); } // 4. Else, } else { // a. Let timeZone be ? ToTemporalTimeZone(item). ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone, temporal::ToTemporalTimeZone(isolate, item_obj, method_name), JSTemporalZonedDateTime); // b. Let temporalTime be undefined. temporal_time_obj = factory->undefined_value(); } // 5. If temporalTime is undefined, then Handle<JSTemporalPlainDateTime> temporal_date_time; Handle<JSReceiver> calendar(temporal_date->calendar(), isolate); if (IsUndefined(*temporal_time_obj)) { // a. Let temporalDateTime be ? // CreateTemporalDateTime(temporalDate.[[ISOYear]], // temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], 0, 0, 0, 0, 0, 0, // temporalDate.[[Calendar]]). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_time, temporal::CreateTemporalDateTime( isolate, {{temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()}, {0, 0, 0, 0, 0, 0}}, calendar), JSTemporalZonedDateTime); // 6. Else, } else { Handle<JSTemporalPlainTime> temporal_time; // a. Set temporalTime to ? ToTemporalTime(temporalTime). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_time, temporal::ToTemporalTime(isolate, temporal_time_obj, method_name), JSTemporalZonedDateTime); // b. Let temporalDateTime be ? // CreateTemporalDateTime(temporalDate.[[ISOYear]], // temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], // temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], // temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], // temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], // temporalDate.[[Calendar]]). ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_time, temporal::CreateTemporalDateTime( isolate, {{temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()}, {temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond()}}, calendar), JSTemporalZonedDateTime); } // 7. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, // temporalDateTime, "compatible"). Handle<JSTemporalInstant> instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, instant, BuiltinTimeZoneGetInstantFor(isolate, time_zone, temporal_date_time, Disambiguation::kCompatible, method_name), JSTemporalZonedDateTime); // 8. Return ? CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, // temporalDate.[[Calendar]]). return CreateTemporalZonedDateTime( isolate, handle(instant->nanoseconds(), isolate), time_zone, calendar); } // #sec-temporal.plaindate.prototype.add MaybeHandle<JSTemporalPlainDate> JSTemporalPlainDate::Add( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date, Handle<Object> temporal_duration_like, Handle<Object> options_obj) { const char* method_name = "Temporal.PlainDate.prototype.add"; // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(temporalDate, // [[InitializedTemporalDate]]). // 3. Let duration be ? ToTemporalDuration(temporalDurationLike). Handle<JSTemporalDuration> duration; ASSIGN_RETURN_ON_EXCEPTION(isolate, duration, temporal::ToTemporalDuration( isolate, temporal_duration_like, method_name), JSTemporalPlainDate); // 4. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainDate); // 5. Return ? CalendarDateAdd(temporalDate.[[Calendar]], temporalDate, // duration, options). return CalendarDateAdd(isolate, handle(temporal_date->calendar(), isolate), temporal_date, duration, options); } // #sec-temporal.plaindate.prototype.subtract MaybeHandle<JSTemporalPlainDate> JSTemporalPlainDate::Subtract( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date, Handle<Object> temporal_duration_like, Handle<Object> options_obj) { const char* method_name = "Temporal.PlainDate.prototype.subtract"; // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(temporalDate, // [[InitializedTemporalDate]]). // 3. Let duration be ? ToTemporalDuration(temporalDurationLike). Handle<JSTemporalDuration> duration; ASSIGN_RETURN_ON_EXCEPTION(isolate, duration, temporal::ToTemporalDuration( isolate, temporal_duration_like, method_name), JSTemporalPlainDate); // 4. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainDate); // 5. Let negatedDuration be ! CreateNegatedTemporalDuration(duration). Handle<JSTemporalDuration> negated_duration = CreateNegatedTemporalDuration(isolate, duration).ToHandleChecked(); // 6. Return ? CalendarDateAdd(temporalDate.[[Calendar]], temporalDate, // negatedDuration, options). return CalendarDateAdd(isolate, handle(temporal_date->calendar(), isolate), temporal_date, negated_duration, options); } namespace { // #sec-temporal-differencetemporalplandate MaybeHandle<JSTemporalDuration> DifferenceTemporalPlainDate( Isolate* isolate, TimePreposition operation, Handle<JSTemporalPlainDate> temporal_date, Handle<Object> other_obj, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If operation is since, let sign be -1. Otherwise, let sign be 1. double sign = operation == TimePreposition::kSince ? -1 : 1; // 2. Set other to ? ToTemporalDate(other). Handle<JSTemporalPlainDate> other; ASSIGN_RETURN_ON_EXCEPTION(isolate, other, ToTemporalDate(isolate, other_obj, method_name), JSTemporalDuration); // 3. If ? CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]) is // false, throw a RangeError exception. bool calendar_equals; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, calendar_equals, CalendarEqualsBool(isolate, handle(temporal_date->calendar(), isolate), handle(other->calendar(), isolate)), Handle<JSTemporalDuration>()); if (!calendar_equals) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalDuration); } // 4. Let settings be ? GetDifferenceSettings(operation, options, date, « », // "day", "day"). DifferenceSettings settings; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, settings, GetDifferenceSettings(isolate, operation, options, UnitGroup::kDate, DisallowedUnitsInDifferenceSettings::kNone, Unit::kDay, Unit::kDay, method_name), Handle<JSTemporalDuration>()); // 5. Let untilOptions be ? MergeLargestUnitOption(settings.[[Options]], // settings.[[LargestUnit]]). Handle<JSReceiver> until_options; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, until_options, MergeLargestUnitOption(isolate, settings.options, settings.largest_unit), Handle<JSTemporalDuration>()); // 6. Let result be ? CalendarDateUntil(temporalDate.[[Calendar]], // temporalDate, other, untilOptions). Handle<JSTemporalDuration> result; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, CalendarDateUntil(isolate, handle(temporal_date->calendar(), isolate), temporal_date, other, until_options), Handle<JSTemporalDuration>()); // 7. If settings.[[SmallestUnit]] is not "day" or // settings.[[RoundingIncrement]] ≠ 1, then if (settings.smallest_unit != Unit::kDay || settings.rounding_increment != 1) { // a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], // result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0, // settings.[[RoundingIncrement]], settings.[[SmallestUnit]], // settings.[[RoundingMode]], temporalDate)).[[DurationRecord]]. DurationRecordWithRemainder round_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, round_result, RoundDuration(isolate, {Object::Number(result->years()), Object::Number(result->months()), Object::Number(result->weeks()), {Object::Number(result->days()), 0, 0, 0, 0, 0, 0}}, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, temporal_date, method_name), Handle<JSTemporalDuration>()); // 8. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × // result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], 0, 0, // 0, 0, 0, 0). round_result.record.years *= sign; round_result.record.months *= sign; round_result.record.weeks *= sign; round_result.record.time_duration.days *= sign; round_result.record.time_duration.hours = round_result.record.time_duration.minutes = round_result.record.time_duration.seconds = round_result.record.time_duration.milliseconds = round_result.record.time_duration.microseconds = round_result.record.time_duration.nanoseconds = 0; return CreateTemporalDuration(isolate, round_result.record) .ToHandleChecked(); } // 8. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × // result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], 0, 0, // 0, 0, 0, 0). return CreateTemporalDuration( isolate, {sign * Object::Number(result->years()), sign * Object::Number(result->months()), sign * Object::Number(result->weeks()), {sign * Object::Number(result->days()), 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); } } // namespace // #sec-temporal.plaindate.prototype.until MaybeHandle<JSTemporalDuration> JSTemporalPlainDate::Until( Isolate* isolate, Handle<JSTemporalPlainDate> handle, Handle<Object> other, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return DifferenceTemporalPlainDate(isolate, TimePreposition::kUntil, handle, other, options, "Temporal.PlainDate.prototype.until"); } // #sec-temporal.plaindate.prototype.since MaybeHandle<JSTemporalDuration> JSTemporalPlainDate::Since( Isolate* isolate, Handle<JSTemporalPlainDate> handle, Handle<Object> other, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return DifferenceTemporalPlainDate(isolate, TimePreposition::kSince, handle, other, options, "Temporal.PlainDate.prototype.since"); } // #sec-temporal.now.plaindate MaybeHandle<JSTemporalPlainDate> JSTemporalPlainDate::Now( Isolate* isolate, Handle<Object> calendar_like, Handle<Object> temporal_time_zone_like) { const char* method_name = "Temporal.Now.plainDate"; // 1. Let dateTime be ? SystemDateTime(temporalTimeZoneLike, calendarLike). Handle<JSTemporalPlainDateTime> date_time; ASSIGN_RETURN_ON_EXCEPTION(isolate, date_time, SystemDateTime(isolate, temporal_time_zone_like, calendar_like, method_name), JSTemporalPlainDate); // 2. Return ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], // dateTime.[[ISODay]], dateTime.[[Calendar]]). return CreateTemporalDate(isolate, {date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, Handle<JSReceiver>(date_time->calendar(), isolate)) .ToHandleChecked(); } // #sec-temporal.now.plaindateiso MaybeHandle<JSTemporalPlainDate> JSTemporalPlainDate::NowISO( Isolate* isolate, Handle<Object> temporal_time_zone_like) { const char* method_name = "Temporal.Now.plainDateISO"; // 1. Let calendar be ! GetISO8601Calendar(). Handle<JSReceiver> calendar = temporal::GetISO8601Calendar(isolate); // 2. Let dateTime be ? SystemDateTime(temporalTimeZoneLike, calendar). Handle<JSTemporalPlainDateTime> date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, date_time, SystemDateTime(isolate, temporal_time_zone_like, calendar, method_name), JSTemporalPlainDate); // 3. Return ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], // dateTime.[[ISODay]], dateTime.[[Calendar]]). return CreateTemporalDate(isolate, {date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, Handle<JSReceiver>(date_time->calendar(), isolate)) .ToHandleChecked(); } // #sec-temporal.plaindate.from MaybeHandle<JSTemporalPlainDate> JSTemporalPlainDate::From( Isolate* isolate, Handle<Object> item, Handle<Object> options_obj) { const char* method_name = "Temporal.PlainDate.from"; // 1. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainDate); // 2. If Type(item) is Object and item has an [[InitializedTemporalDate]] // internal slot, then if (IsJSTemporalPlainDate(*item)) { // a. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainDate>()); // b. Return ? CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], // item.[[ISODay]], item.[[Calendar]]). Handle<JSTemporalPlainDate> date = Handle<JSTemporalPlainDate>::cast(item); return CreateTemporalDate( isolate, {date->iso_year(), date->iso_month(), date->iso_day()}, Handle<JSReceiver>(date->calendar(), isolate)); } // 3. Return ? ToTemporalDate(item, options). return ToTemporalDate(isolate, item, options, method_name); } #define DEFINE_INT_FIELD(obj, str, field, item) \ CHECK(JSReceiver::CreateDataProperty( \ isolate, obj, factory->str##_string(), \ Handle<Smi>(Smi::FromInt(item->field()), isolate), \ Just(kThrowOnError)) \ .FromJust()); // #sec-temporal.plaindate.prototype.getisofields MaybeHandle<JSReceiver> JSTemporalPlainDate::GetISOFields( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date) { Factory* factory = isolate->factory(); // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(temporalDate, // [[InitializedTemporalDate]]). // 3. Let fields be ! OrdinaryObjectCreate(%Object.prototype%). Handle<JSObject> fields = isolate->factory()->NewJSObject(isolate->object_function()); // 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", // temporalDate.[[Calendar]]). CHECK(JSReceiver::CreateDataProperty( isolate, fields, factory->calendar_string(), Handle<JSReceiver>(temporal_date->calendar(), isolate), Just(kThrowOnError)) .FromJust()); // 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", // 𝔽(temporalDate.[[ISODay]])). // 6. Perform ! CreateDataPropertyOrThrow(fields, "isoMonth", // 𝔽(temporalDate.[[ISOMonth]])). // 7. Perform ! CreateDataPropertyOrThrow(fields, "isoYear", // 𝔽(temporalDate.[[ISOYear]])). DEFINE_INT_FIELD(fields, isoDay, iso_day, temporal_date) DEFINE_INT_FIELD(fields, isoMonth, iso_month, temporal_date) DEFINE_INT_FIELD(fields, isoYear, iso_year, temporal_date) // 8. Return fields. return fields; } // #sec-temporal.plaindate.prototype.tojson MaybeHandle<String> JSTemporalPlainDate::ToJSON( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date) { // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(temporalDate, // [[InitializedTemporalDate]]). // 3. Return ? TemporalDateToString(temporalDate, "auto"). return TemporalDateToString(isolate, temporal_date, ShowCalendar::kAuto); } namespace { // #sec-temporal-toshowcalendaroption Maybe<ShowCalendar> ToShowCalendarOption(Isolate* isolate, Handle<JSReceiver> options, const char* method_name) { // 1. Return ? GetOption(normalizedOptions, "calendarName", « String », « // "auto", "always", "never" », "auto"). return GetStringOption<ShowCalendar>( isolate, options, "calendarName", method_name, {"auto", "always", "never"}, {ShowCalendar::kAuto, ShowCalendar::kAlways, ShowCalendar::kNever}, ShowCalendar::kAuto); } template <typename T, MaybeHandle<String> (*F)(Isolate*, Handle<T>, ShowCalendar)> MaybeHandle<String> TemporalToString(Isolate* isolate, Handle<T> temporal, Handle<Object> options_obj, const char* method_name) { // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(temporalDate, // [[InitializedTemporalDate]]). // 3. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), String); // 4. Let showCalendar be ? ToShowCalendarOption(options). ShowCalendar show_calendar; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, show_calendar, ToShowCalendarOption(isolate, options, method_name), Handle<String>()); // 5. Return ? TemporalDateToString(temporalDate, showCalendar). return F(isolate, temporal, show_calendar); } } // namespace // #sec-temporal.plaindate.prototype.tostring MaybeHandle<String> JSTemporalPlainDate::ToString( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date, Handle<Object> options) { return TemporalToString<JSTemporalPlainDate, TemporalDateToString>( isolate, temporal_date, options, "Temporal.PlainDate.prototype.toString"); } // #sup-temporal.plaindate.prototype.tolocalestring MaybeHandle<String> JSTemporalPlainDate::ToLocaleString( Isolate* isolate, Handle<JSTemporalPlainDate> temporal_date, Handle<Object> locales, Handle<Object> options) { #ifdef V8_INTL_SUPPORT return JSDateTimeFormat::TemporalToLocaleString( isolate, temporal_date, locales, options, "Temporal.PlainDate.prototype.toLocaleString"); #else // V8_INTL_SUPPORT return TemporalDateToString(isolate, temporal_date, ShowCalendar::kAuto); #endif // V8_INTL_SUPPORT } // #sec-temporal-createtemporaldatetime MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::Constructor( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<Object> iso_year_obj, Handle<Object> iso_month_obj, Handle<Object> iso_day_obj, Handle<Object> hour_obj, Handle<Object> minute_obj, Handle<Object> second_obj, Handle<Object> millisecond_obj, Handle<Object> microsecond_obj, Handle<Object> nanosecond_obj, Handle<Object> calendar_like) { const char* method_name = "Temporal.PlainDateTime"; // 1. If NewTarget is undefined, throw a TypeError exception. if (IsUndefined(*new_target)) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, isolate->factory()->NewStringFromAsciiChecked( method_name)), JSTemporalPlainDateTime); } TO_INT_THROW_ON_INFTY(iso_year, JSTemporalPlainDateTime); TO_INT_THROW_ON_INFTY(iso_month, JSTemporalPlainDateTime); TO_INT_THROW_ON_INFTY(iso_day, JSTemporalPlainDateTime); TO_INT_THROW_ON_INFTY(hour, JSTemporalPlainDateTime); TO_INT_THROW_ON_INFTY(minute, JSTemporalPlainDateTime); TO_INT_THROW_ON_INFTY(second, JSTemporalPlainDateTime); TO_INT_THROW_ON_INFTY(millisecond, JSTemporalPlainDateTime); TO_INT_THROW_ON_INFTY(microsecond, JSTemporalPlainDateTime); TO_INT_THROW_ON_INFTY(nanosecond, JSTemporalPlainDateTime); // 20. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, calendar_like, method_name), JSTemporalPlainDateTime); // 21. Return ? CreateTemporalDateTime(isoYear, isoMonth, isoDay, hour, // minute, second, millisecond, microsecond, nanosecond, calendar, NewTarget). return CreateTemporalDateTime( isolate, target, new_target, {{iso_year, iso_month, iso_day}, {hour, minute, second, millisecond, microsecond, nanosecond}}, calendar); } namespace { // #sec-temporal-interprettemporaldatetimefields Maybe<temporal::DateTimeRecord> InterpretTemporalDateTimeFields( Isolate* isolate, Handle<JSReceiver> calendar, Handle<JSReceiver> fields, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. Let timeResult be ? ToTemporalTimeRecord(fields). TimeRecord time_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, time_result, ToTemporalTimeRecord(isolate, fields, method_name), Nothing<temporal::DateTimeRecord>()); // 2. Let temporalDate be ? DateFromFields(calendar, fields, options). Handle<JSTemporalPlainDate> temporal_date; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, temporal_date, DateFromFields(isolate, calendar, fields, options), Nothing<temporal::DateTimeRecord>()); // 3. Let overflow be ? ToTemporalOverflow(options). ShowOverflow overflow; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, overflow, ToTemporalOverflow(isolate, options, method_name), Nothing<temporal::DateTimeRecord>()); // 4. Let timeResult be ? RegulateTime(timeResult.[[Hour]], // timeResult.[[Minute]], timeResult.[[Second]], timeResult.[[Millisecond]], // timeResult.[[Microsecond]], timeResult.[[Nanosecond]], overflow). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, time_result, temporal::RegulateTime(isolate, time_result, overflow), Nothing<temporal::DateTimeRecord>()); // 5. Return the new Record { [[Year]]: temporalDate.[[ISOYear]], [[Month]]: // temporalDate.[[ISOMonth]], [[Day]]: temporalDate.[[ISODay]], [[Hour]]: // timeResult.[[Hour]], [[Minute]]: timeResult.[[Minute]], [[Second]]: // timeResult.[[Second]], [[Millisecond]]: timeResult.[[Millisecond]], // [[Microsecond]]: timeResult.[[Microsecond]], [[Nanosecond]]: // timeResult.[[Nanosecond]] }. temporal::DateTimeRecord result = { {temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()}, time_result}; return Just(result); } // #sec-temporal-parsetemporaldatetimestring Maybe<DateTimeRecordWithCalendar> ParseTemporalDateTimeString( Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); // 1. Assert: Type(isoString) is String. // 2. If isoString does not satisfy the syntax of a TemporalDateTimeString // (see 13.33), then base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseTemporalDateTimeString(isolate, iso_string); if (!parsed.has_value()) { // a. Throw a *RangeError* exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateTimeRecordWithCalendar>()); } // 3. If _isoString_ contains a |UTCDesignator|, then if (parsed->utc_designator) { // a. Throw a *RangeError* exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateTimeRecordWithCalendar>()); } // 3. Let result be ? ParseISODateTime(isoString). // 4. Return result. return ParseISODateTime(isolate, iso_string, *parsed); } // #sec-temporal-totemporaldatetime MaybeHandle<JSTemporalPlainDateTime> ToTemporalDateTime( Isolate* isolate, Handle<Object> item_obj, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 2. Assert: Type(options) is Object or Undefined. DCHECK(IsJSReceiver(*options) || IsUndefined(*options)); Handle<JSReceiver> calendar; temporal::DateTimeRecord result; // 2. If Type(item) is Object, then if (IsJSReceiver(*item_obj)) { Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); // a. If item has an [[InitializedTemporalDateTime]] internal slot, then // i. Return item. if (IsJSTemporalPlainDateTime(*item)) { return Handle<JSTemporalPlainDateTime>::cast(item); } // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, // then if (IsJSTemporalZonedDateTime(*item)) { // i. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainDateTime>()); // ii. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]). Handle<JSTemporalZonedDateTime> zoned_date_time = Handle<JSTemporalZonedDateTime>::cast(item); Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); // iii. Return ? // temporal::BuiltinTimeZoneGetPlainDateTimeFor(item.[[TimeZone]], // instant, item.[[Calendar]]). return temporal::BuiltinTimeZoneGetPlainDateTimeFor( isolate, handle(zoned_date_time->time_zone(), isolate), instant, handle(zoned_date_time->calendar(), isolate), method_name); } // c. If item has an [[InitializedTemporalDate]] internal slot, then if (IsJSTemporalPlainDate(*item)) { // i. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainDateTime>()); // ii. Return ? CreateTemporalDateTime(item.[[ISOYear]], // item.[[ISOMonth]], item.[[ISODay]], 0, 0, 0, 0, 0, 0, // item.[[Calendar]]). Handle<JSTemporalPlainDate> date = Handle<JSTemporalPlainDate>::cast(item); return temporal::CreateTemporalDateTime( isolate, {{date->iso_year(), date->iso_month(), date->iso_day()}, {0, 0, 0, 0, 0, 0}}, handle(date->calendar(), isolate)); } // d. Let calendar be ? GetTemporalCalendarWithISODefault(item). ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, GetTemporalCalendarWithISODefault(isolate, item, method_name), JSTemporalPlainDateTime); // e. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", // "microsecond", "millisecond", "minute", "month", "monthCode", // "nanosecond", "second", "year" »). Handle<FixedArray> field_names = All10UnitsInFixedArray(isolate); ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, CalendarFields(isolate, calendar, field_names), JSTemporalPlainDateTime); // f. Let fields be ? PrepareTemporalFields(item, // PrepareTemporalFields(item, fieldNames, «»). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION(isolate, fields, PrepareTemporalFields(isolate, item, field_names, RequiredFields::kNone), JSTemporalPlainDateTime); // g. Let result be ? // InterpretTemporalDateTimeFields(calendar, fields, options). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, InterpretTemporalDateTimeFields(isolate, calendar, fields, options, method_name), Handle<JSTemporalPlainDateTime>()); } else { // 3. Else, // a. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainDateTime>()); // b. Let string be ? ToString(item). Handle<String> string; ASSIGN_RETURN_ON_EXCEPTION(isolate, string, Object::ToString(isolate, item_obj), JSTemporalPlainDateTime); // c. Let result be ? ParseTemporalDateTimeString(string). DateTimeRecordWithCalendar parsed_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, parsed_result, ParseTemporalDateTimeString(isolate, string), Handle<JSTemporalPlainDateTime>()); result = {parsed_result.date, parsed_result.time}; // d. Assert: ! IsValidISODate(result.[[Year]], result.[[Month]], // result.[[Day]]) is true. DCHECK(IsValidISODate(isolate, result.date)); // e. Assert: ! IsValidTime(result.[[Hour]], // result.[[Minute]], result.[[Second]], result.[[Millisecond]], // result.[[Microsecond]], result.[[Nanosecond]]) is true. DCHECK(IsValidTime(isolate, result.time)); // f. Let calendar // be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]). ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, parsed_result.calendar, method_name), JSTemporalPlainDateTime); } // 4. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], // result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], // result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], // calendar). return temporal::CreateTemporalDateTime(isolate, {result.date, result.time}, calendar); } } // namespace // #sec-temporal.plaindatetime.from MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::From( Isolate* isolate, Handle<Object> item, Handle<Object> options_obj) { const char* method_name = "Temporal.PlainDateTime.from"; // 1. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainDateTime); // 2. If Type(item) is Object and item has an [[InitializedTemporalDateTime]] // internal slot, then if (IsJSTemporalPlainDateTime(*item)) { // a. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainDateTime>()); // b. Return ? CreateTemporalDateTime(item.[[ISYear]], item.[[ISOMonth]], // item.[[ISODay]], item.[[ISOHour]], item.[[ISOMinute]], // item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]], // item.[[ISONanosecond]], item.[[Calendar]]). Handle<JSTemporalPlainDateTime> date_time = Handle<JSTemporalPlainDateTime>::cast(item); return temporal::CreateTemporalDateTime( isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}, handle(date_time->calendar(), isolate)); } // 3. Return ? ToTemporalDateTime(item, options). return ToTemporalDateTime(isolate, item, options, method_name); } // #sec-temporal.plaindatetime.compare MaybeHandle<Smi> JSTemporalPlainDateTime::Compare(Isolate* isolate, Handle<Object> one_obj, Handle<Object> two_obj) { const char* method_name = "Temporal.PlainDateTime.compare"; // 1. Set one to ? ToTemporalDateTime(one). Handle<JSTemporalPlainDateTime> one; ASSIGN_RETURN_ON_EXCEPTION( isolate, one, ToTemporalDateTime(isolate, one_obj, method_name), Smi); // 2. Set two to ? ToTemporalDateTime(two). Handle<JSTemporalPlainDateTime> two; ASSIGN_RETURN_ON_EXCEPTION( isolate, two, ToTemporalDateTime(isolate, two_obj, method_name), Smi); // 3. Return 𝔽(! CompareISODateTime(one.[[ISOYear]], one.[[ISOMonth]], // one.[[ISODay]], one.[[ISOHour]], one.[[ISOMinute]], one.[[ISOSecond]], // one.[[ISOMillisecond]], one.[[ISOMicrosecond]], one.[[ISONanosecond]], // two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]], two.[[ISOHour]], // two.[[ISOMinute]], two.[[ISOSecond]], two.[[ISOMillisecond]], // two.[[ISOMicrosecond]], two.[[ISONanosecond]])). return Handle<Smi>( Smi::FromInt(CompareISODateTime( { {one->iso_year(), one->iso_month(), one->iso_day()}, {one->iso_hour(), one->iso_minute(), one->iso_second(), one->iso_millisecond(), one->iso_microsecond(), one->iso_nanosecond()}, }, { {two->iso_year(), two->iso_month(), two->iso_day()}, {two->iso_hour(), two->iso_minute(), two->iso_second(), two->iso_millisecond(), two->iso_microsecond(), two->iso_nanosecond()}, })), isolate); } // #sec-temporal.plaindatetime.prototype.equals MaybeHandle<Oddball> JSTemporalPlainDateTime::Equals( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> other_obj) { // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, // [[InitializedTemporalDateTime]]). // 3. Set other to ? ToTemporalDateTime(other). Handle<JSTemporalPlainDateTime> other; ASSIGN_RETURN_ON_EXCEPTION( isolate, other, ToTemporalDateTime(isolate, other_obj, "Temporal.PlainDateTime.prototype.equals"), Oddball); // 4. Let result be ! CompareISODateTime(dateTime.[[ISOYear]], // dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], // dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], // dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], // dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], // other.[[ISODay]], other.[[ISOHour]], other.[[ISOMinute]], // other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], // other.[[ISONanosecond]]). int32_t result = CompareISODateTime( { {date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}, }, { {other->iso_year(), other->iso_month(), other->iso_day()}, {other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond()}, }); // 5. If result is not 0, return false. if (result != 0) return isolate->factory()->false_value(); // 6. Return ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]). return CalendarEquals(isolate, handle(date_time->calendar(), isolate), handle(other->calendar(), isolate)); } // #sec-temporal.plaindatetime.prototype.with MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::With( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> temporal_date_time_like_obj, Handle<Object> options_obj) { const char* method_name = "Temporal.PlainDateTime.prototype.with"; // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, // [[InitializedTemporalDateTime]]). // 3. If Type(temporalDateTimeLike) is not Object, then if (!IsJSReceiver(*temporal_date_time_like_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalPlainDateTime); } Handle<JSReceiver> temporal_date_time_like = Handle<JSReceiver>::cast(temporal_date_time_like_obj); // 4. Perform ? RejectObjectWithCalendarOrTimeZone(temporalTimeLike). MAYBE_RETURN( RejectObjectWithCalendarOrTimeZone(isolate, temporal_date_time_like), Handle<JSTemporalPlainDateTime>()); // 5. Let calendar be dateTime.[[Calendar]]. Handle<JSReceiver> calendar = Handle<JSReceiver>(date_time->calendar(), isolate); // 6. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", // "microsecond", "millisecond", "minute", "month", "monthCode", "nanosecond", // "second", "year" »). Handle<FixedArray> field_names = All10UnitsInFixedArray(isolate); ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, CalendarFields(isolate, calendar, field_names), JSTemporalPlainDateTime); // 7. Let partialDateTime be ? // PreparePartialTemporalFields(temporalDateTimeLike, fieldNames). Handle<JSReceiver> partial_date_time; ASSIGN_RETURN_ON_EXCEPTION(isolate, partial_date_time, PreparePartialTemporalFields( isolate, temporal_date_time_like, field_names), JSTemporalPlainDateTime); // 8. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainDateTime); // 9. Let fields be ? PrepareTemporalFields(dateTime, fieldNames, «»). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, PrepareTemporalFields(isolate, date_time, field_names, RequiredFields::kNone), JSTemporalPlainDateTime); // 10. Set fields to ? CalendarMergeFields(calendar, fields, partialDateTime). ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, CalendarMergeFields(isolate, calendar, fields, partial_date_time), JSTemporalPlainDateTime); // 11. Set fields to ? PrepareTemporalFields(fields, fieldNames, «»). ASSIGN_RETURN_ON_EXCEPTION(isolate, fields, PrepareTemporalFields(isolate, fields, field_names, RequiredFields::kNone), JSTemporalPlainDateTime); // 12. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, // options). temporal::DateTimeRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, InterpretTemporalDateTimeFields(isolate, calendar, fields, options, method_name), Handle<JSTemporalPlainDateTime>()); // 13. Assert: ! IsValidISODate(result.[[Year]], result.[[Month]], // result.[[Day]]) is true. DCHECK(IsValidISODate(isolate, result.date)); // 14. Assert: ! IsValidTime(result.[[Hour]], result.[[Minute]], // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], // result.[[Nanosecond]]) is true. DCHECK(IsValidTime(isolate, result.time)); // 15. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], // result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], // result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], // calendar). return temporal::CreateTemporalDateTime(isolate, {result.date, result.time}, calendar); } // #sec-temporal.plaindatetime.prototype.withplaintime MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::WithPlainTime( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> plain_time_like) { // 1. Let temporalDateTime be the this value. // 2. Perform ? RequireInternalSlot(temporalDateTime, // [[InitializedTemporalDateTime]]). // 3. If plainTimeLike is undefined, then if (IsUndefined(*plain_time_like)) { // a. Return ? CreateTemporalDateTime(temporalDateTime.[[ISOYear]], // temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], 0, 0, 0, 0, // 0, 0, temporalDateTime.[[Calendar]]). return temporal::CreateTemporalDateTime( isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {0, 0, 0, 0, 0, 0}}, handle(date_time->calendar(), isolate)); } Handle<JSTemporalPlainTime> plain_time; // 4. Let plainTime be ? ToTemporalTime(plainTimeLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, plain_time, temporal::ToTemporalTime( isolate, plain_time_like, "Temporal.PlainDateTime.prototype.withPlainTime"), JSTemporalPlainDateTime); // 5. Return ? CreateTemporalDateTime(temporalDateTime.[[ISOYear]], // temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], // plainTime.[[ISOHour]], plainTime.[[ISOMinute]], plainTime.[[ISOSecond]], // plainTime.[[ISOMillisecond]], plainTime.[[ISOMicrosecond]], // plainTime.[[ISONanosecond]], temporalDateTime.[[Calendar]]). return temporal::CreateTemporalDateTime( isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {plain_time->iso_hour(), plain_time->iso_minute(), plain_time->iso_second(), plain_time->iso_millisecond(), plain_time->iso_microsecond(), plain_time->iso_nanosecond()}}, handle(date_time->calendar(), isolate)); } // #sec-temporal.plaindatetime.prototype.withcalendar MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::WithCalendar( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> calendar_like) { // 1. Let temporalDateTime be the this value. // 2. Perform ? RequireInternalSlot(temporalDateTime, // [[InitializedTemporalDateTime]]). // 3. Let calendar be ? ToTemporalCalendar(calendar). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, temporal::ToTemporalCalendar( isolate, calendar_like, "Temporal.PlainDateTime.prototype.withCalendar"), JSTemporalPlainDateTime); // 4. Return ? CreateTemporalDateTime(temporalDateTime.[[ISOYear]], // temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], // temporalDateTime.[[ISOHour]], temporalDateTime.[[ISOMinute]], // temporalDateTime.[[ISOSecond]], temporalDateTime.[[ISOMillisecond]], // temporalDateTime.[[ISOMicrosecond]], temporalDateTime.[[ISONanosecond]], // calendar). return temporal::CreateTemporalDateTime( isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}, calendar); } // #sec-temporal.plaindatetime.prototype.toplainyearmonth MaybeHandle<JSTemporalPlainYearMonth> JSTemporalPlainDateTime::ToPlainYearMonth( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time) { return ToPlain<JSTemporalPlainDateTime, JSTemporalPlainYearMonth, YearMonthFromFields>(isolate, date_time, isolate->factory()->monthCode_string(), isolate->factory()->year_string()); } // #sec-temporal.plaindatetime.prototype.toplainmonthday MaybeHandle<JSTemporalPlainMonthDay> JSTemporalPlainDateTime::ToPlainMonthDay( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time) { return ToPlain<JSTemporalPlainDateTime, JSTemporalPlainMonthDay, MonthDayFromFields>(isolate, date_time, isolate->factory()->day_string(), isolate->factory()->monthCode_string()); } // #sec-temporal.plaindatetime.prototype.tozoneddatetime MaybeHandle<JSTemporalZonedDateTime> JSTemporalPlainDateTime::ToZonedDateTime( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> temporal_time_zone_like, Handle<Object> options_obj) { const char* method_name = "Temporal.PlainDateTime.prototype.toZonedDateTime"; // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, // [[InitializedTemporalDateTime]]). // 3. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike). Handle<JSReceiver> time_zone; ASSIGN_RETURN_ON_EXCEPTION(isolate, time_zone, temporal::ToTemporalTimeZone( isolate, temporal_time_zone_like, method_name), JSTemporalZonedDateTime); // 4. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalZonedDateTime); // 5. Let disambiguation be ? ToTemporalDisambiguation(options). Disambiguation disambiguation; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, disambiguation, ToTemporalDisambiguation(isolate, options, method_name), Handle<JSTemporalZonedDateTime>()); // 6. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, dateTime, // disambiguation). Handle<JSTemporalInstant> instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, instant, BuiltinTimeZoneGetInstantFor(isolate, time_zone, date_time, disambiguation, method_name), JSTemporalZonedDateTime); // 7. Return ? CreateTemporalZonedDateTime(instant.[[Nanoseconds]], // timeZone, dateTime.[[Calendar]]). return CreateTemporalZonedDateTime( isolate, handle(instant->nanoseconds(), isolate), time_zone, Handle<JSReceiver>(date_time->calendar(), isolate)); } namespace { // #sec-temporal-consolidatecalendars MaybeHandle<JSReceiver> ConsolidateCalendars(Isolate* isolate, Handle<JSReceiver> one, Handle<JSReceiver> two) { Factory* factory = isolate->factory(); // 1. If one and two are the same Object value, return two. if (one.is_identical_to(two)) return two; // 2. Let calendarOne be ? ToString(one). Handle<String> calendar_one; ASSIGN_RETURN_ON_EXCEPTION(isolate, calendar_one, Object::ToString(isolate, one), JSReceiver); // 3. Let calendarTwo be ? ToString(two). Handle<String> calendar_two; ASSIGN_RETURN_ON_EXCEPTION(isolate, calendar_two, Object::ToString(isolate, two), JSReceiver); // 4. If calendarOne is calendarTwo, return two. if (String::Equals(isolate, calendar_one, calendar_two)) { return two; } // 5. If calendarOne is "iso8601", return two. if (String::Equals(isolate, calendar_one, factory->iso8601_string())) { return two; } // 6. If calendarTwo is "iso8601", return one. if (String::Equals(isolate, calendar_two, factory->iso8601_string())) { return one; } // 7. Throw a RangeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSReceiver); } } // namespace // #sec-temporal.plaindatetime.prototype.withplaindate MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::WithPlainDate( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> temporal_date_like) { // 1. Let temporalDateTime be the this value. // 2. Perform ? RequireInternalSlot(temporalDateTime, // [[InitializedTemporalDateTime]]). // 3. Let plainDate be ? ToTemporalDate(plainDateLike). Handle<JSTemporalPlainDate> plain_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, plain_date, ToTemporalDate(isolate, temporal_date_like, "Temporal.PlainDateTime.prototype.withPlainDate"), JSTemporalPlainDateTime); // 4. Let calendar be ? ConsolidateCalendars(temporalDateTime.[[Calendar]], // plainDate.[[Calendar]]). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ConsolidateCalendars(isolate, handle(date_time->calendar(), isolate), handle(plain_date->calendar(), isolate)), JSTemporalPlainDateTime); // 5. Return ? CreateTemporalDateTime(plainDate.[[ISOYear]], // plainDate.[[ISOMonth]], plainDate.[[ISODay]], temporalDateTime.[[ISOHour]], // temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], // temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], // temporalDateTime.[[ISONanosecond]], calendar). return temporal::CreateTemporalDateTime( isolate, {{plain_date->iso_year(), plain_date->iso_month(), plain_date->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}, calendar); } namespace { MaybeHandle<String> TemporalDateTimeToString(Isolate* isolate, const DateTimeRecord& date_time, Handle<JSReceiver> calendar, Precision precision, ShowCalendar show_calendar) { IncrementalStringBuilder builder(isolate); // 1. Assert: isoYear, isoMonth, isoDay, hour, minute, second, millisecond, // microsecond, and nanosecond are integers. // 2. Let year be ! PadISOYear(isoYear). PadISOYear(&builder, date_time.date.year); // 3. Let month be ToZeroPaddedDecimalString(isoMonth, 2). builder.AppendCharacter('-'); ToZeroPaddedDecimalString(&builder, date_time.date.month, 2); // 4. Let day be ToZeroPaddedDecimalString(isoDay, 2). builder.AppendCharacter('-'); ToZeroPaddedDecimalString(&builder, date_time.date.day, 2); // 5. Let hour be ToZeroPaddedDecimalString(hour, 2). builder.AppendCharacter('T'); ToZeroPaddedDecimalString(&builder, date_time.time.hour, 2); // 6. Let minute be ToZeroPaddedDecimalString(minute, 2). builder.AppendCharacter(':'); ToZeroPaddedDecimalString(&builder, date_time.time.minute, 2); // 7. Let seconds be ! FormatSecondsStringPart(second, millisecond, // microsecond, nanosecond, precision). FormatSecondsStringPart( &builder, date_time.time.second, date_time.time.millisecond, date_time.time.microsecond, date_time.time.nanosecond, precision); // 8. Let calendarString be ? MaybeFormatCalendarAnnotation(calendar, // showCalendar). Handle<String> calendar_string; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar_string, MaybeFormatCalendarAnnotation(isolate, calendar, show_calendar), String); // 9. Return the string-concatenation of year, the code unit 0x002D // (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, 0x0054 // (LATIN CAPITAL LETTER T), hour, the code unit 0x003A (COLON), minute, builder.AppendString(calendar_string); return builder.Finish().ToHandleChecked(); } } // namespace // #sec-temporal.plaindatetime.prototype.tojson MaybeHandle<String> JSTemporalPlainDateTime::ToJSON( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time) { return TemporalDateTimeToString( isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}, Handle<JSReceiver>(date_time->calendar(), isolate), Precision::kAuto, ShowCalendar::kAuto); } // #sec-temporal.plaindatetime.prototype.tolocalestring MaybeHandle<String> JSTemporalPlainDateTime::ToLocaleString( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> locales, Handle<Object> options) { #ifdef V8_INTL_SUPPORT return JSDateTimeFormat::TemporalToLocaleString( isolate, date_time, locales, options, "Temporal.PlainDateTime.prototype.toLocaleString"); #else // V8_INTL_SUPPORT return TemporalDateTimeToString( isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}, Handle<JSReceiver>(date_time->calendar(), isolate), Precision::kAuto, ShowCalendar::kAuto); #endif // V8_INTL_SUPPORT } namespace { constexpr double kNsPerDay = 8.64e13; DateTimeRecord RoundTime( Isolate* isolate, const TimeRecord& time, double increment, Unit unit, RoundingMode rounding_mode, // 3.a a. If dayLengthNs is not present, set dayLengthNs to nsPerDay. double day_length_ns = kNsPerDay); // #sec-temporal-roundisodatetime DateTimeRecord RoundISODateTime( Isolate* isolate, const DateTimeRecord& date_time, double increment, Unit unit, RoundingMode rounding_mode, // 3. If dayLength is not present, set dayLength to nsPerDay. double day_length_ns = kNsPerDay) { // 1. Assert: year, month, day, hour, minute, second, millisecond, // microsecond, and nanosecond are integers. TEMPORAL_ENTER_FUNC(); // 2. Assert: ISODateTimeWithinLimits(year, month, day, hour, minute, second, // millisecond, microsecond, nanosecond) is true. DCHECK(ISODateTimeWithinLimits(isolate, date_time)); // 4. Let roundedTime be ! RoundTime(hour, minute, second, millisecond, // microsecond, nanosecond, increment, unit, roundingMode, dayLength). DateTimeRecord rounded_time = RoundTime(isolate, date_time.time, increment, unit, rounding_mode, day_length_ns); // 5. Let balanceResult be ! BalanceISODate(year, month, day + // roundedTime.[[Days]]). rounded_time.date.year = date_time.date.year; rounded_time.date.month = date_time.date.month; rounded_time.date.day += date_time.date.day; DateRecord balance_result = BalanceISODate(isolate, rounded_time.date); // 6. Return the Record { [[Year]]: balanceResult.[[Year]], [[Month]]: // balanceResult.[[Month]], [[Day]]: balanceResult.[[Day]], [[Hour]]: // roundedTime.[[Hour]], [[Minute]]: roundedTime.[[Minute]], [[Second]]: // roundedTime.[[Second]], [[Millisecond]]: roundedTime.[[Millisecond]], // [[Microsecond]]: roundedTime.[[Microsecond]], [[Nanosecond]]: // roundedTime.[[Nanosecond]] }. return {balance_result, rounded_time.time}; } } // namespace // #sec-temporal.plaindatetime.prototype.tostring MaybeHandle<String> JSTemporalPlainDateTime::ToString( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> options_obj) { const char* method_name = "Temporal.PlainDateTime.prototype.toString"; // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, // [[InitializedTemporalDateTime]]). // 3. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), String); // 4. Let precision be ? ToSecondsStringPrecision(options). StringPrecision precision; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, precision, ToSecondsStringPrecision(isolate, options, method_name), Handle<String>()); // 5. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). RoundingMode rounding_mode; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_mode, ToTemporalRoundingMode(isolate, options, RoundingMode::kTrunc, method_name), Handle<String>()); // 6. Let showCalendar be ? ToShowCalendarOption(options). ShowCalendar show_calendar; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, show_calendar, ToShowCalendarOption(isolate, options, method_name), Handle<String>()); // 7. Let result be ! RoundISODateTime(dateTime.[[ISOYear]], // dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], // dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], // dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], // dateTime.[[ISONanosecond]], precision.[[Increment]], precision.[[Unit]], // roundingMode). DateTimeRecord result = RoundISODateTime( isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}, precision.increment, precision.unit, rounding_mode); // 8. Return ? TemporalDateTimeToString(result.[[Year]], result.[[Month]], // result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], // result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], // dateTime.[[Calendar]], precision.[[Precision]], showCalendar). return TemporalDateTimeToString(isolate, result, handle(date_time->calendar(), isolate), precision.precision, show_calendar); } // #sec-temporal.now.plaindatetime MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::Now( Isolate* isolate, Handle<Object> calendar_like, Handle<Object> temporal_time_zone_like) { const char* method_name = "Temporal.Now.plainDateTime"; // 1. Return ? SystemDateTime(temporalTimeZoneLike, calendarLike). return SystemDateTime(isolate, temporal_time_zone_like, calendar_like, method_name); } // #sec-temporal.now.plaindatetimeiso MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::NowISO( Isolate* isolate, Handle<Object> temporal_time_zone_like) { const char* method_name = "Temporal.Now.plainDateTimeISO"; // 1. Let calendar be ! GetISO8601Calendar(). Handle<JSReceiver> calendar = temporal::GetISO8601Calendar(isolate); // 2. Return ? SystemDateTime(temporalTimeZoneLike, calendar). return SystemDateTime(isolate, temporal_time_zone_like, calendar, method_name); } namespace { // #sec-temporal-totemporaldatetimeroundingincrement Maybe<double> ToTemporalDateTimeRoundingIncrement( Isolate* isolate, Handle<JSReceiver> normalized_option, Unit smallest_unit) { Maximum maximum; // 1. If smallestUnit is "day", then if (smallest_unit == Unit::kDay) { // a. Let maximum be 1. maximum.value = 1; maximum.defined = true; // 2. Else, } else { // a. Let maximum be ! // MaximumTemporalDurationRoundingIncrement(smallestUnit). maximum = MaximumTemporalDurationRoundingIncrement(smallest_unit); // b. Assert: maximum is not undefined. DCHECK(maximum.defined); } // 3. Return ? ToTemporalRoundingIncrement(normalizedOptions, maximum, false). return ToTemporalRoundingIncrement(isolate, normalized_option, maximum.value, maximum.defined, false); } } // namespace // #sec-temporal.plaindatetime.prototype.round MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::Round( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> round_to_obj) { const char* method_name = "Temporal.PlainDateTime.prototype.round"; Factory* factory = isolate->factory(); // 1. Let temporalTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, // [[InitializedTemporalDateTime]]). // 3. If roundTo is undefined, then if (IsUndefined(*round_to_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalPlainDateTime); } Handle<JSReceiver> round_to; // 4. If Type(roundTo) is String, then if (IsString(*round_to_obj)) { // a. Let paramString be roundTo. Handle<String> param_string = Handle<String>::cast(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()); // 5. Else } else { // a. Set roundTo to ? GetOptionsObject(roundTo). ASSIGN_RETURN_ON_EXCEPTION( isolate, round_to, GetOptionsObject(isolate, round_to_obj, method_name), JSTemporalPlainDateTime); } // 6. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, // required). Unit smallest_unit; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, smallest_unit, GetTemporalUnit(isolate, round_to, "smallestUnit", UnitGroup::kTime, Unit::kDay, true, method_name), Handle<JSTemporalPlainDateTime>()); // 7. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand"). RoundingMode rounding_mode; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_mode, ToTemporalRoundingMode(isolate, round_to, RoundingMode::kHalfExpand, method_name), Handle<JSTemporalPlainDateTime>()); // 8. Let roundingIncrement be ? ToTemporalDateTimeRoundingIncrement(roundTo, // smallestUnit). double rounding_increment; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_increment, ToTemporalDateTimeRoundingIncrement(isolate, round_to, smallest_unit), Handle<JSTemporalPlainDateTime>()); // 9. Let result be ! RoundISODateTime(dateTime.[[ISOYear]], // dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], // dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], // dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], // dateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode). DateTimeRecord result = RoundISODateTime( isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}, rounding_increment, smallest_unit, rounding_mode); // 10. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], // result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], // result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], // dateTime.[[Calendar]]). return temporal::CreateTemporalDateTime( isolate, result, handle(date_time->calendar(), isolate)); } namespace { MaybeHandle<JSTemporalPlainDateTime> AddDurationToOrSubtractDurationFromPlainDateTime( Isolate* isolate, Arithmetic operation, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> temporal_duration_like, Handle<Object> options_obj, const char* method_name) { // 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1. double sign = operation == Arithmetic::kSubtract ? -1.0 : 1.0; // 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike). DurationRecord duration; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, duration, temporal::ToTemporalDurationRecord(isolate, temporal_duration_like, method_name), Handle<JSTemporalPlainDateTime>()); TimeDurationRecord& time_duration = duration.time_duration; // 3. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainDateTime); // 4. Let result be ? AddDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], // dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], // dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], // dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], // dateTime.[[Calendar]], duration.[[Years]], duration.[[Months]], // duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], // duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], // duration.[[Microseconds]], duration.[[Nanoseconds]], options). DateTimeRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, AddDateTime(isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}, handle(date_time->calendar(), isolate), {sign * duration.years, sign * duration.months, sign * duration.weeks, {sign * time_duration.days, sign * time_duration.hours, sign * time_duration.minutes, sign * time_duration.seconds, sign * time_duration.milliseconds, sign * time_duration.microseconds, sign * time_duration.nanoseconds}}, options), Handle<JSTemporalPlainDateTime>()); // 5. Assert: ! IsValidISODate(result.[[Year]], result.[[Month]], // result.[[Day]]) is true. DCHECK(IsValidISODate(isolate, result.date)); // 6. Assert: ! IsValidTime(result.[[Hour]], result.[[Minute]], // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], // result.[[Nanosecond]]) is true. DCHECK(IsValidTime(isolate, result.time)); // 7. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], // result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], // result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], // dateTime.[[Calendar]]). return temporal::CreateTemporalDateTime( isolate, result, handle(date_time->calendar(), isolate)); } } // namespace // #sec-temporal.plaindatetime.prototype.add MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::Add( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> temporal_duration_like, Handle<Object> options) { return AddDurationToOrSubtractDurationFromPlainDateTime( isolate, Arithmetic::kAdd, date_time, temporal_duration_like, options, "Temporal.PlainDateTime.prototype.add"); } // #sec-temporal.plaindatetime.prototype.subtract MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainDateTime::Subtract( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> temporal_duration_like, Handle<Object> options) { return AddDurationToOrSubtractDurationFromPlainDateTime( isolate, Arithmetic::kSubtract, date_time, temporal_duration_like, options, "Temporal.PlainDateTime.prototype.subtract"); } namespace { // #sec-temporal-differencetemporalplaindatetime MaybeHandle<JSTemporalDuration> DifferenceTemporalPlainDateTime( Isolate* isolate, TimePreposition operation, Handle<JSTemporalPlainDateTime> date_time, Handle<Object> other_obj, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If operation is since, let sign be -1. Otherwise, let sign be 1. double sign = operation == TimePreposition::kSince ? -1 : 1; // 2. Set other to ? ToTemporalDateTime(other). Handle<JSTemporalPlainDateTime> other; ASSIGN_RETURN_ON_EXCEPTION( isolate, other, ToTemporalDateTime(isolate, other_obj, method_name), JSTemporalDuration); // 3. If ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]) is false, // throw a RangeError exception. bool calendar_equals; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, calendar_equals, CalendarEqualsBool(isolate, handle(date_time->calendar(), isolate), handle(other->calendar(), isolate)), Handle<JSTemporalDuration>()); if (!calendar_equals) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalDuration); } // 4. Let settings be ? GetDifferenceSettings(operation, options, datetime, « // », "nanosecond", "day"). DifferenceSettings settings; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, settings, GetDifferenceSettings(isolate, operation, options, UnitGroup::kDateTime, DisallowedUnitsInDifferenceSettings::kNone, Unit::kNanosecond, Unit::kDay, method_name), Handle<JSTemporalDuration>()); // 5. Let diff be ? DifferenceISODateTime(dateTime.[[ISOYear]], // dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], // dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], // dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], // dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], // other.[[ISODay]], other.[[ISOHour]], other.[[ISOMinute]], // other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], // other.[[ISONanosecond]], dateTime.[[Calendar]], settings.[[LargestUnit]], // settings.[[Options]]). DurationRecord diff; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, diff, DifferenceISODateTime( isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}, {{other->iso_year(), other->iso_month(), other->iso_day()}, {other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond()}}, handle(date_time->calendar(), isolate), settings.largest_unit, settings.options, method_name), Handle<JSTemporalDuration>()); // 6. Let relativeTo be ! CreateTemporalDate(dateTime.[[ISOYear]], // dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]). Handle<JSTemporalPlainDate> relative_to; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, relative_to, CreateTemporalDate( isolate, {date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, handle(date_time->calendar(), isolate)), Handle<JSTemporalDuration>()); // 7. Let roundResult be (? RoundDuration(diff.[[Years]], diff.[[Months]], // diff.[[Weeks]], diff.[[Days]], diff.[[Hours]], diff.[[Minutes]], // diff.[[Seconds]], diff.[[Milliseconds]], diff.[[Microseconds]], // diff.[[Nanoseconds]], settings.[[RoundingIncrement]], // settings.[[SmallestUnit]], settings.[[RoundingMode]], // relativeTo)).[[DurationRecord]]. DurationRecordWithRemainder round_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, round_result, RoundDuration(isolate, diff, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, relative_to, method_name), Handle<JSTemporalDuration>()); // 8. Let result be ? BalanceDuration(roundResult.[[Days]], // roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], // roundResult.[[Milliseconds]], roundResult.[[Microseconds]], // roundResult.[[Nanoseconds]], settings.[[LargestUnit]]). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, round_result.record.time_duration, BalanceDuration(isolate, settings.largest_unit, round_result.record.time_duration, method_name), Handle<JSTemporalDuration>()); // 9. Return ! CreateTemporalDuration(sign × roundResult.[[Years]], sign × // roundResult.[[Months]], sign × roundResult.[[Weeks]], sign × // result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × // result.[[Seconds]], sign × result.[[Milliseconds]], sign × // result.[[Microseconds]], sign × result.[[Nanoseconds]]). return CreateTemporalDuration( isolate, {sign * round_result.record.years, sign * round_result.record.months, sign * round_result.record.weeks, {sign * round_result.record.time_duration.days, sign * round_result.record.time_duration.hours, sign * round_result.record.time_duration.minutes, sign * round_result.record.time_duration.seconds, sign * round_result.record.time_duration.milliseconds, sign * round_result.record.time_duration.microseconds, sign * round_result.record.time_duration.nanoseconds}}) .ToHandleChecked(); } } // namespace // #sec-temporal.plaindatetime.prototype.until MaybeHandle<JSTemporalDuration> JSTemporalPlainDateTime::Until( Isolate* isolate, Handle<JSTemporalPlainDateTime> handle, Handle<Object> other, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return DifferenceTemporalPlainDateTime( isolate, TimePreposition::kUntil, handle, other, options, "Temporal.PlainDateTime.prototype.until"); } // #sec-temporal.plaindatetime.prototype.since MaybeHandle<JSTemporalDuration> JSTemporalPlainDateTime::Since( Isolate* isolate, Handle<JSTemporalPlainDateTime> handle, Handle<Object> other, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return DifferenceTemporalPlainDateTime( isolate, TimePreposition::kSince, handle, other, options, "Temporal.PlainDateTime.prototype.since"); } // #sec-temporal.plaindatetime.prototype.getisofields MaybeHandle<JSReceiver> JSTemporalPlainDateTime::GetISOFields( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time) { Factory* factory = isolate->factory(); // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(temporalDateTime, // [[InitializedTemporalDateTime]]). // 3. Let fields be ! OrdinaryObjectCreate(%Object.prototype%). Handle<JSObject> fields = isolate->factory()->NewJSObject(isolate->object_function()); // 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", // temporalTime.[[Calendar]]). CHECK(JSReceiver::CreateDataProperty( isolate, fields, factory->calendar_string(), Handle<JSReceiver>(date_time->calendar(), isolate), Just(kThrowOnError)) .FromJust()); // 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", // 𝔽(dateTime.[[ISODay]])). // 6. Perform ! CreateDataPropertyOrThrow(fields, "isoHour", // 𝔽(temporalTime.[[ISOHour]])). // 7. Perform ! CreateDataPropertyOrThrow(fields, "isoMicrosecond", // 𝔽(temporalTime.[[ISOMicrosecond]])). // 8. Perform ! CreateDataPropertyOrThrow(fields, "isoMillisecond", // 𝔽(temporalTime.[[ISOMillisecond]])). // 9. Perform ! CreateDataPropertyOrThrow(fields, "isoMinute", // 𝔽(temporalTime.[[ISOMinute]])). // 10. Perform ! CreateDataPropertyOrThrow(fields, "isoMonth", // 𝔽(temporalTime.[[ISOMonth]])). // 11. Perform ! CreateDataPropertyOrThrow(fields, "isoNanosecond", // 𝔽(temporalTime.[[ISONanosecond]])). // 12. Perform ! CreateDataPropertyOrThrow(fields, "isoSecond", // 𝔽(temporalTime.[[ISOSecond]])). // 13. Perform ! CreateDataPropertyOrThrow(fields, "isoYear", // 𝔽(temporalTime.[[ISOYear]])). DEFINE_INT_FIELD(fields, isoDay, iso_day, date_time) DEFINE_INT_FIELD(fields, isoHour, iso_hour, date_time) DEFINE_INT_FIELD(fields, isoMicrosecond, iso_microsecond, date_time) DEFINE_INT_FIELD(fields, isoMillisecond, iso_millisecond, date_time) DEFINE_INT_FIELD(fields, isoMinute, iso_minute, date_time) DEFINE_INT_FIELD(fields, isoMonth, iso_month, date_time) DEFINE_INT_FIELD(fields, isoNanosecond, iso_nanosecond, date_time) DEFINE_INT_FIELD(fields, isoSecond, iso_second, date_time) DEFINE_INT_FIELD(fields, isoYear, iso_year, date_time) // 14. Return fields. return fields; } // #sec-temporal.plaindatetime.prototype.toplaindate MaybeHandle<JSTemporalPlainDate> JSTemporalPlainDateTime::ToPlainDate( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time) { // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, // [[InitializedTemporalDateTime]]). // 3. Return ? CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], // dateTime.[[ISODay]], dateTime.[[Calendar]]). return CreateTemporalDate( isolate, {date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, Handle<JSReceiver>(date_time->calendar(), isolate)); } // #sec-temporal.plaindatetime.prototype.toplaintime MaybeHandle<JSTemporalPlainTime> JSTemporalPlainDateTime::ToPlainTime( Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time) { // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, // [[InitializedTemporalDateTime]]). // 3. Return ? CreateTemporalTime(dateTime.[[ISOHour]], // dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], // dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], // dateTime.[[ISONanosecond]]). return CreateTemporalTime( isolate, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}); } // #sec-temporal.plainmonthday MaybeHandle<JSTemporalPlainMonthDay> JSTemporalPlainMonthDay::Constructor( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<Object> iso_month_obj, Handle<Object> iso_day_obj, Handle<Object> calendar_like, Handle<Object> reference_iso_year_obj) { const char* method_name = "Temporal.PlainMonthDay"; // 1. If NewTarget is undefined, throw a TypeError exception. if (IsUndefined(*new_target)) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, isolate->factory()->NewStringFromAsciiChecked( method_name)), JSTemporalPlainMonthDay); } // 3. Let m be ? ToIntegerThrowOnInfinity(isoMonth). TO_INT_THROW_ON_INFTY(iso_month, JSTemporalPlainMonthDay); // 5. Let d be ? ToIntegerThrowOnInfinity(isoDay). TO_INT_THROW_ON_INFTY(iso_day, JSTemporalPlainMonthDay); // 7. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, calendar_like, method_name), JSTemporalPlainMonthDay); // 2. If referenceISOYear is undefined, then // a. Set referenceISOYear to 1972𝔽. // ... // 8. Let ref be ? ToIntegerThrowOnInfinity(referenceISOYear). int32_t ref = 1972; if (!IsUndefined(*reference_iso_year_obj)) { TO_INT_THROW_ON_INFTY(reference_iso_year, JSTemporalPlainMonthDay); ref = reference_iso_year; } // 10. Return ? CreateTemporalMonthDay(y, m, calendar, ref, NewTarget). return CreateTemporalMonthDay(isolate, target, new_target, iso_month, iso_day, calendar, ref); } namespace { // #sec-temporal-parsetemporalmonthdaystring Maybe<DateRecordWithCalendar> ParseTemporalMonthDayString( Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); // 1. Assert: Type(isoString) is String. // 2. If isoString does not satisfy the syntax of a TemporalMonthDayString // (see 13.33), then base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseTemporalMonthDayString(isolate, iso_string); if (!parsed.has_value()) { // a. Throw a *RangeError* exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateRecordWithCalendar>()); } // 3. If isoString contains a UTCDesignator, then if (parsed->utc_designator) { // a. Throw a *RangeError* exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateRecordWithCalendar>()); } // 3. Let result be ? ParseISODateTime(isoString). DateTimeRecordWithCalendar result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ParseISODateTime(isolate, iso_string, *parsed), Nothing<DateRecordWithCalendar>()); // 5. Let year be result.[[Year]]. // 6. If no part of isoString is produced by the DateYear production, then // a. Set year to undefined. // 7. Return the Record { [[Year]]: year, [[Month]]: result.[[Month]], // [[Day]]: result.[[Day]], [[Calendar]]: result.[[Calendar]] }. DateRecordWithCalendar ret({result.date, result.calendar}); return Just(ret); } // #sec-temporal-totemporalmonthday MaybeHandle<JSTemporalPlainMonthDay> ToTemporalMonthDay( Isolate* isolate, Handle<Object> item_obj, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 2. Assert: Type(options) is Object or Undefined. DCHECK(IsJSReceiver(*options) || IsUndefined(*options)); // 3. Let referenceISOYear be 1972 (the first leap year after the Unix epoch). constexpr int32_t kReferenceIsoYear = 1972; // 4. If Type(item) is Object, then if (IsJSReceiver(*item_obj)) { Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); // a. If item has an [[InitializedTemporalMonthDay]] internal slot, then // i. Return item. if (IsJSTemporalPlainMonthDay(*item_obj)) { return Handle<JSTemporalPlainMonthDay>::cast(item_obj); } bool calendar_absent = false; // b. If item has an [[InitializedTemporalDate]], // [[InitializedTemporalDateTime]], [[InitializedTemporalTime]], // [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] // internal slot, then // i. Let calendar be item.[[Calendar]]. // ii. Let calendarAbsent be false. Handle<JSReceiver> calendar; if (IsJSTemporalPlainDate(*item_obj)) { calendar = handle(Handle<JSTemporalPlainDate>::cast(item_obj)->calendar(), isolate); } else if (IsJSTemporalPlainDateTime(*item_obj)) { calendar = handle( Handle<JSTemporalPlainDateTime>::cast(item_obj)->calendar(), isolate); } else if (IsJSTemporalPlainTime(*item_obj)) { calendar = handle(Handle<JSTemporalPlainTime>::cast(item_obj)->calendar(), isolate); } else if (IsJSTemporalPlainYearMonth(*item_obj)) { calendar = handle(Handle<JSTemporalPlainYearMonth>::cast(item_obj)->calendar(), isolate); } else if (IsJSTemporalZonedDateTime(*item_obj)) { calendar = handle( Handle<JSTemporalZonedDateTime>::cast(item_obj)->calendar(), isolate); // c. Else, } else { // i. Let calendar be ? Get(item, "calendar"). Handle<Object> calendar_obj; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar_obj, JSReceiver::GetProperty(isolate, item, factory->calendar_string()), JSTemporalPlainMonthDay); // ii. If calendar is undefined, then if (IsUndefined(*calendar_obj)) { // 1. Let calendarAbsent be true. calendar_absent = true; } // iv. Set calendar to ? ToTemporalCalendarWithISODefault(calendar). ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, calendar_obj, method_name), JSTemporalPlainMonthDay); } // d. Let fieldNames be ? CalendarFields(calendar, « "day", "month", // "monthCode", "year" »). Handle<FixedArray> field_names = DayMonthMonthCodeYearInFixedArray(isolate); ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, CalendarFields(isolate, calendar, field_names), JSTemporalPlainMonthDay); // e. Let fields be ? PrepareTemporalFields(item, fieldNames, «»). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION(isolate, fields, PrepareTemporalFields(isolate, item, field_names, RequiredFields::kNone), JSTemporalPlainMonthDay); // f. Let month be ? Get(fields, "month"). Handle<Object> month; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, month, JSReceiver::GetProperty(isolate, fields, factory->month_string()), Handle<JSTemporalPlainMonthDay>()); // g. Let monthCode be ? Get(fields, "monthCode"). Handle<Object> month_code; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, month_code, JSReceiver::GetProperty(isolate, fields, factory->monthCode_string()), Handle<JSTemporalPlainMonthDay>()); // h. Let year be ? Get(fields, "year"). Handle<Object> year; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, year, JSReceiver::GetProperty(isolate, fields, factory->year_string()), Handle<JSTemporalPlainMonthDay>()); // i. If calendarAbsent is true, and month is not undefined, and monthCode // is undefined and year is undefined, then if (calendar_absent && !IsUndefined(*month) && IsUndefined(*month_code) && IsUndefined(*year)) { // i. Perform ! CreateDataPropertyOrThrow(fields, "year", // 𝔽(referenceISOYear)). CHECK(JSReceiver::CreateDataProperty( isolate, fields, factory->year_string(), handle(Smi::FromInt(kReferenceIsoYear), isolate), Just(kThrowOnError)) .FromJust()); } // j. Return ? MonthDayFromFields(calendar, fields, options). return MonthDayFromFields(isolate, calendar, fields, options); } // 5. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainMonthDay>()); // 6. Let string be ? ToString(item). Handle<String> string; ASSIGN_RETURN_ON_EXCEPTION(isolate, string, Object::ToString(isolate, item_obj), JSTemporalPlainMonthDay); // 7. Let result be ? ParseTemporalMonthDayString(string). DateRecordWithCalendar result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ParseTemporalMonthDayString(isolate, string), Handle<JSTemporalPlainMonthDay>()); // 8. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, result.calendar, method_name), JSTemporalPlainMonthDay); // 9. If result.[[Year]] is undefined, then // We use kMintInt31 to represent undefined if (result.date.year == kMinInt31) { // a. Return ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], // calendar, referenceISOYear). return CreateTemporalMonthDay(isolate, result.date.month, result.date.day, calendar, kReferenceIsoYear); } Handle<JSTemporalPlainMonthDay> created_result; // 10. Set result to ? CreateTemporalMonthDay(result.[[Month]], // result.[[Day]], calendar, referenceISOYear). ASSIGN_RETURN_ON_EXCEPTION( isolate, created_result, CreateTemporalMonthDay(isolate, result.date.month, result.date.day, calendar, kReferenceIsoYear), JSTemporalPlainMonthDay); // 11. NOTE: The following operation is called without options, in order for // the calendar to store a canonical value in the [[ISOYear]] internal slot of // the result. // 12. Return ? CalendarMonthDayFromFields(calendar, result). return MonthDayFromFields(isolate, calendar, created_result); } MaybeHandle<JSTemporalPlainMonthDay> ToTemporalMonthDay( Isolate* isolate, Handle<Object> item_obj, const char* method_name) { // 1. If options is not present, set options to undefined. return ToTemporalMonthDay(isolate, item_obj, isolate->factory()->undefined_value(), method_name); } } // namespace // #sec-temporal.plainmonthday.from MaybeHandle<JSTemporalPlainMonthDay> JSTemporalPlainMonthDay::From( Isolate* isolate, Handle<Object> item, Handle<Object> options_obj) { const char* method_name = "Temporal.PlainMonthDay.from"; // 1. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainMonthDay); // 2. If Type(item) is Object and item has an [[InitializedTemporalMonthDay]] // internal slot, then if (IsJSTemporalPlainMonthDay(*item)) { // a. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainMonthDay>()); // b. Return ? CreateTemporalMonthDay(item.[[ISOMonth]], item.[[ISODay]], // item.[[Calendar]], item.[[ISOYear]]). Handle<JSTemporalPlainMonthDay> month_day = Handle<JSTemporalPlainMonthDay>::cast(item); return CreateTemporalMonthDay( isolate, month_day->iso_month(), month_day->iso_day(), handle(month_day->calendar(), isolate), month_day->iso_year()); } // 3. Return ? ToTemporalMonthDay(item, options). return ToTemporalMonthDay(isolate, item, options, method_name); } // #sec-temporal.plainyearmonth.prototype.equals MaybeHandle<Oddball> JSTemporalPlainMonthDay::Equals( Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day, Handle<Object> other_obj) { // 1. Let monthDay be the this value. // 2. Perform ? RequireInternalSlot(monthDay, // [[InitializedTemporalMonthDay]]). // 3. Set other to ? ToTemporalMonthDay(other). Handle<JSTemporalPlainMonthDay> other; ASSIGN_RETURN_ON_EXCEPTION( isolate, other, ToTemporalMonthDay(isolate, other_obj, "Temporal.PlainMonthDay.prototype.equals"), Oddball); // 4. If monthDay.[[ISOMonth]] ≠ other.[[ISOMonth]], return false. if (month_day->iso_month() != other->iso_month()) return isolate->factory()->false_value(); // 5. If monthDay.[[ISODay]] ≠ other.[[ISODay]], return false. if (month_day->iso_day() != other->iso_day()) return isolate->factory()->false_value(); // 6. If monthDay.[[ISOYear]] ≠ other.[[ISOYear]], return false. if (month_day->iso_year() != other->iso_year()) return isolate->factory()->false_value(); // 7. Return ? CalendarEquals(monthDay.[[Calendar]], other.[[Calendar]]). return CalendarEquals(isolate, Handle<JSReceiver>(month_day->calendar(), isolate), Handle<JSReceiver>(other->calendar(), isolate)); } // #sec-temporal.plainmonthday.prototype.with MaybeHandle<JSTemporalPlainMonthDay> JSTemporalPlainMonthDay::With( Isolate* isolate, Handle<JSTemporalPlainMonthDay> temporal_month_day, Handle<Object> temporal_month_day_like_obj, Handle<Object> options_obj) { // 6. Let fieldNames be ? CalendarFields(calendar, « "day", "month", // "monthCode", "year" »). Handle<FixedArray> field_names = DayMonthMonthCodeYearInFixedArray(isolate); return PlainDateOrYearMonthOrMonthDayWith<JSTemporalPlainMonthDay, MonthDayFromFields>( isolate, temporal_month_day, temporal_month_day_like_obj, options_obj, field_names, "Temporal.PlainMonthDay.prototype.with"); } namespace { // Common code shared by PlainMonthDay and PlainYearMonth.prototype.toPlainDate template <typename T> MaybeHandle<JSTemporalPlainDate> PlainMonthDayOrYearMonthToPlainDate( Isolate* isolate, Handle<T> temporal, Handle<Object> item_obj, Handle<String> receiver_field_name_1, Handle<String> receiver_field_name_2, Handle<String> input_field_name) { Factory* factory = isolate->factory(); // 1. Let monthDay be the this value. // 2. Perform ? RequireInternalSlot(monthDay, // [[InitializedTemporalXXX]]). // 3. If Type(item) is not Object, then if (!IsJSReceiver(*item_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalPlainDate); } Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); // 4. Let calendar be Xxx.[[Calendar]]. Handle<JSReceiver> calendar(temporal->calendar(), isolate); // 5. Let receiverFieldNames be ? CalendarFields(calendar, « // receiverFieldName1, receiverFieldName2 »). Handle<FixedArray> receiver_field_names = factory->NewFixedArray(2); receiver_field_names->set(0, *receiver_field_name_1); receiver_field_names->set(1, *receiver_field_name_2); ASSIGN_RETURN_ON_EXCEPTION( isolate, receiver_field_names, CalendarFields(isolate, calendar, receiver_field_names), JSTemporalPlainDate); // 6. Let fields be ? PrepareTemporalFields(temporal, receiverFieldNames, «»). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, PrepareTemporalFields(isolate, temporal, receiver_field_names, RequiredFields::kNone), JSTemporalPlainDate); // 7. Let inputFieldNames be ? CalendarFields(calendar, « inputFieldName »). Handle<FixedArray> input_field_names = factory->NewFixedArray(1); input_field_names->set(0, *input_field_name); ASSIGN_RETURN_ON_EXCEPTION( isolate, input_field_names, CalendarFields(isolate, calendar, input_field_names), JSTemporalPlainDate); // 8. Let inputFields be ? PrepareTemporalFields(item, inputFieldNames, «»). Handle<JSReceiver> input_fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, input_fields, PrepareTemporalFields(isolate, item, input_field_names, RequiredFields::kNone), JSTemporalPlainDate); // 9. Let mergedFields be ? CalendarMergeFields(calendar, fields, // inputFields). Handle<JSReceiver> merged_fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, merged_fields, CalendarMergeFields(isolate, calendar, fields, input_fields), JSTemporalPlainDate); // 10. Let mergedFieldNames be the List containing all the elements of // receiverFieldNames followed by all the elements of inputFieldNames, with // duplicate elements removed. Handle<FixedArray> merged_field_names = factory->NewFixedArray( receiver_field_names->length() + input_field_names->length()); Handle<StringSet> added = StringSet::New(isolate); for (int i = 0; i < receiver_field_names->length(); i++) { Handle<Object> item(receiver_field_names->get(i), isolate); DCHECK(IsString(*item)); Handle<String> string = Handle<String>::cast(item); if (!added->Has(isolate, string)) { merged_field_names->set(added->NumberOfElements(), *item); added = StringSet::Add(isolate, added, string); } } for (int i = 0; i < input_field_names->length(); i++) { Handle<Object> item(input_field_names->get(i), isolate); DCHECK(IsString(*item)); Handle<String> string = Handle<String>::cast(item); if (!added->Has(isolate, string)) { merged_field_names->set(added->NumberOfElements(), *item); added = StringSet::Add(isolate, added, string); } } merged_field_names = FixedArray::ShrinkOrEmpty(isolate, merged_field_names, added->NumberOfElements()); // 11. Set mergedFields to ? PrepareTemporalFields(mergedFields, // mergedFieldNames, «»). ASSIGN_RETURN_ON_EXCEPTION( isolate, merged_fields, PrepareTemporalFields(isolate, merged_fields, merged_field_names, RequiredFields::kNone), JSTemporalPlainDate); // 12. Let options be ! OrdinaryObjectCreate(null). Handle<JSObject> options = factory->NewJSObjectWithNullProto(); // 13. Perform ! CreateDataPropertyOrThrow(options, "overflow", "reject"). CHECK(JSReceiver::CreateDataProperty( isolate, options, factory->overflow_string(), factory->reject_string(), Just(kThrowOnError)) .FromJust()); // 14. Return ? DateFromFields(calendar, mergedFields, options). return DateFromFields(isolate, calendar, merged_fields, options); } } // namespace // #sec-temporal.plainmonthday.prototype.toplaindate MaybeHandle<JSTemporalPlainDate> JSTemporalPlainMonthDay::ToPlainDate( Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day, Handle<Object> item_obj) { Factory* factory = isolate->factory(); // 5. Let receiverFieldNames be ? CalendarFields(calendar, « "day", // "monthCode" »). // 7. Let inputFieldNames be ? CalendarFields(calendar, « "year" »). return PlainMonthDayOrYearMonthToPlainDate<JSTemporalPlainMonthDay>( isolate, month_day, item_obj, factory->day_string(), factory->monthCode_string(), factory->year_string()); } // #sec-temporal.plainmonthday.prototype.getisofields MaybeHandle<JSReceiver> JSTemporalPlainMonthDay::GetISOFields( Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day) { Factory* factory = isolate->factory(); // 1. Let monthDay be the this value. // 2. Perform ? RequireInternalSlot(monthDay, // [[InitializedTemporalMonthDay]]). // 3. Let fields be ! OrdinaryObjectCreate(%Object.prototype%). Handle<JSObject> fields = factory->NewJSObject(isolate->object_function()); // 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", // montyDay.[[Calendar]]). CHECK(JSReceiver::CreateDataProperty( isolate, fields, factory->calendar_string(), Handle<JSReceiver>(month_day->calendar(), isolate), Just(kThrowOnError)) .FromJust()); // 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", // 𝔽(montyDay.[[ISODay]])). // 6. Perform ! CreateDataPropertyOrThrow(fields, "isoMonth", // 𝔽(montyDay.[[ISOMonth]])). // 7. Perform ! CreateDataPropertyOrThrow(fields, "isoYear", // 𝔽(montyDay.[[ISOYear]])). DEFINE_INT_FIELD(fields, isoDay, iso_day, month_day) DEFINE_INT_FIELD(fields, isoMonth, iso_month, month_day) DEFINE_INT_FIELD(fields, isoYear, iso_year, month_day) // 8. Return fields. return fields; } // #sec-temporal.plainmonthday.prototype.tojson MaybeHandle<String> JSTemporalPlainMonthDay::ToJSON( Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day) { return TemporalMonthDayToString(isolate, month_day, ShowCalendar::kAuto); } // #sec-temporal.plainmonthday.prototype.tostring MaybeHandle<String> JSTemporalPlainMonthDay::ToString( Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day, Handle<Object> options) { return TemporalToString<JSTemporalPlainMonthDay, TemporalMonthDayToString>( isolate, month_day, options, "Temporal.PlainMonthDay.prototype.toString"); } // #sec-temporal.plainmonthday.prototype.tolocalestring MaybeHandle<String> JSTemporalPlainMonthDay::ToLocaleString( Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day, Handle<Object> locales, Handle<Object> options) { #ifdef V8_INTL_SUPPORT return JSDateTimeFormat::TemporalToLocaleString( isolate, month_day, locales, options, "Temporal.PlainMonthDay.prototype.toLocaleString"); #else // V8_INTL_SUPPORT return TemporalMonthDayToString(isolate, month_day, ShowCalendar::kAuto); #endif // V8_INTL_SUPPORT } MaybeHandle<JSTemporalPlainYearMonth> JSTemporalPlainYearMonth::Constructor( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<Object> iso_year_obj, Handle<Object> iso_month_obj, Handle<Object> calendar_like, Handle<Object> reference_iso_day_obj) { const char* method_name = "Temporal.PlainYearMonth"; // 1. If NewTarget is undefined, throw a TypeError exception. if (IsUndefined(*new_target)) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, isolate->factory()->NewStringFromAsciiChecked( method_name)), JSTemporalPlainYearMonth); } // 7. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike). // 10. Return ? CreateTemporalYearMonth(y, m, calendar, ref, NewTarget). // 3. Let y be ? ToIntegerThrowOnInfinity(isoYear). TO_INT_THROW_ON_INFTY(iso_year, JSTemporalPlainYearMonth); // 5. Let m be ? ToIntegerThrowOnInfinity(isoMonth). TO_INT_THROW_ON_INFTY(iso_month, JSTemporalPlainYearMonth); // 7. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, calendar_like, method_name), JSTemporalPlainYearMonth); // 2. If referenceISODay is undefined, then // a. Set referenceISODay to 1𝔽. // ... // 8. Let ref be ? ToIntegerThrowOnInfinity(referenceISODay). int32_t ref = 1; if (!IsUndefined(*reference_iso_day_obj)) { TO_INT_THROW_ON_INFTY(reference_iso_day, JSTemporalPlainYearMonth); ref = reference_iso_day; } // 10. Return ? CreateTemporalYearMonth(y, m, calendar, ref, NewTarget). return CreateTemporalYearMonth(isolate, target, new_target, iso_year, iso_month, calendar, ref); } namespace { // #sec-temporal-parsetemporalyearmonthstring Maybe<DateRecordWithCalendar> ParseTemporalYearMonthString( Isolate* isolate, Handle<String> iso_string) { TEMPORAL_ENTER_FUNC(); // 1. Assert: Type(isoString) is String. // 2. If isoString does not satisfy the syntax of a TemporalYearMonthString // (see 13.33), then base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseTemporalYearMonthString(isolate, iso_string); if (!parsed.has_value()) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateRecordWithCalendar>()); } // 3. If _isoString_ contains a |UTCDesignator|, then if (parsed->utc_designator) { // a. Throw a *RangeError* exception. THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<DateRecordWithCalendar>()); } // 3. Let result be ? ParseISODateTime(isoString). DateTimeRecordWithCalendar result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ParseISODateTime(isolate, iso_string, *parsed), Nothing<DateRecordWithCalendar>()); // 4. Return the Record { [[Year]]: result.[[Year]], [[Month]]: // result.[[Month]], [[Day]]: result.[[Day]], [[Calendar]]: // result.[[Calendar]] }. DateRecordWithCalendar ret = { {result.date.year, result.date.month, result.date.day}, result.calendar}; return Just(ret); } // #sec-temporal-totemporalyearmonth MaybeHandle<JSTemporalPlainYearMonth> ToTemporalYearMonth( Isolate* isolate, Handle<Object> item_obj, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 2. Assert: Type(options) is Object or Undefined. DCHECK(IsJSReceiver(*options) || IsUndefined(*options)); // 3. If Type(item) is Object, then if (IsJSReceiver(*item_obj)) { Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); // a. If item has an [[InitializedTemporalYearMonth]] internal slot, then // i. Return item. if (IsJSTemporalPlainYearMonth(*item_obj)) { return Handle<JSTemporalPlainYearMonth>::cast(item_obj); } // b. Let calendar be ? GetTemporalCalendarWithISODefault(item). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, GetTemporalCalendarWithISODefault(isolate, item, method_name), JSTemporalPlainYearMonth); // c. Let fieldNames be ? CalendarFields(calendar, « "month", "monthCode", // "year" »). Handle<FixedArray> field_names = MonthMonthCodeYearInFixedArray(isolate); ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, CalendarFields(isolate, calendar, field_names), JSTemporalPlainYearMonth); // d. Let fields be ? PrepareTemporalFields(item, fieldNames, «»). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION(isolate, fields, PrepareTemporalFields(isolate, item, field_names, RequiredFields::kNone), JSTemporalPlainYearMonth); // e. Return ? YearMonthFromFields(calendar, fields, options). return YearMonthFromFields(isolate, calendar, fields, options); } // 4. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainYearMonth>()); // 5. Let string be ? ToString(item). Handle<String> string; ASSIGN_RETURN_ON_EXCEPTION(isolate, string, Object::ToString(isolate, item_obj), JSTemporalPlainYearMonth); // 6. Let result be ? ParseTemporalYearMonthString(string). DateRecordWithCalendar result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ParseTemporalYearMonthString(isolate, string), Handle<JSTemporalPlainYearMonth>()); // 7. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, result.calendar, method_name), JSTemporalPlainYearMonth); // 8. Set result to ? CreateTemporalYearMonth(result.[[Year]], // result.[[Month]], calendar, result.[[Day]]). Handle<JSTemporalPlainYearMonth> created_result; ASSIGN_RETURN_ON_EXCEPTION( isolate, created_result, CreateTemporalYearMonth(isolate, result.date.year, result.date.month, calendar, result.date.day), JSTemporalPlainYearMonth); // 9. NOTE: The following operation is called without options, in order for // the calendar to store a canonical value in the [[ISODay]] internal slot of // the result. // 10. Return ? CalendarYearMonthFromFields(calendar, result). return YearMonthFromFields(isolate, calendar, created_result); } MaybeHandle<JSTemporalPlainYearMonth> ToTemporalYearMonth( Isolate* isolate, Handle<Object> item_obj, const char* method_name) { // 1. If options is not present, set options to undefined. return ToTemporalYearMonth( isolate, item_obj, isolate->factory()->undefined_value(), method_name); } } // namespace // #sec-temporal.plainyearmonth.from MaybeHandle<JSTemporalPlainYearMonth> JSTemporalPlainYearMonth::From( Isolate* isolate, Handle<Object> item, Handle<Object> options_obj) { const char* method_name = "Temporal.PlainYearMonth.from"; // 1. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainYearMonth); // 2. If Type(item) is Object and item has an [[InitializedTemporalYearMonth]] // internal slot, then if (IsJSTemporalPlainYearMonth(*item)) { // a. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainYearMonth>()); // b. Return ? CreateTemporalYearMonth(item.[[ISOYear]], item.[[ISOMonth]], // item.[[Calendar]], item.[[ISODay]]). Handle<JSTemporalPlainYearMonth> year_month = Handle<JSTemporalPlainYearMonth>::cast(item); return CreateTemporalYearMonth( isolate, year_month->iso_year(), year_month->iso_month(), handle(year_month->calendar(), isolate), year_month->iso_day()); } // 3. Return ? ToTemporalYearMonth(item, options). return ToTemporalYearMonth(isolate, item, options, method_name); } // #sec-temporal.plainyearmonth.compare MaybeHandle<Smi> JSTemporalPlainYearMonth::Compare(Isolate* isolate, Handle<Object> one_obj, Handle<Object> two_obj) { const char* method_name = "Temporal.PlainYearMonth.compare"; // 1. Set one to ? ToTemporalYearMonth(one). Handle<JSTemporalPlainYearMonth> one; ASSIGN_RETURN_ON_EXCEPTION( isolate, one, ToTemporalYearMonth(isolate, one_obj, method_name), Smi); // 2. Set two to ? ToTemporalYearMonth(two). Handle<JSTemporalPlainYearMonth> two; ASSIGN_RETURN_ON_EXCEPTION( isolate, two, ToTemporalYearMonth(isolate, two_obj, method_name), Smi); // 3. Return 𝔽(! CompareISODate(one.[[ISOYear]], one.[[ISOMonth]], // one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]])). return handle(Smi::FromInt(CompareISODate( {one->iso_year(), one->iso_month(), one->iso_day()}, {two->iso_year(), two->iso_month(), two->iso_day()})), isolate); } // #sec-temporal.plainyearmonth.prototype.equals MaybeHandle<Oddball> JSTemporalPlainYearMonth::Equals( Isolate* isolate, Handle<JSTemporalPlainYearMonth> year_month, Handle<Object> other_obj) { // 1. Let yearMonth be the this value. // 2. Perform ? RequireInternalSlot(yearMonth, // [[InitializedTemporalYearMonth]]). // 3. Set other to ? ToTemporalYearMonth(other). Handle<JSTemporalPlainYearMonth> other; ASSIGN_RETURN_ON_EXCEPTION( isolate, other, ToTemporalYearMonth(isolate, other_obj, "Temporal.PlainYearMonth.prototype.equals"), Oddball); // 4. If yearMonth.[[ISOYear]] ≠ other.[[ISOYear]], return false. if (year_month->iso_year() != other->iso_year()) return isolate->factory()->false_value(); // 5. If yearMonth.[[ISOMonth]] ≠ other.[[ISOMonth]], return false. if (year_month->iso_month() != other->iso_month()) return isolate->factory()->false_value(); // 6. If yearMonth.[[ISODay]] ≠ other.[[ISODay]], return false. if (year_month->iso_day() != other->iso_day()) return isolate->factory()->false_value(); // 7. Return ? CalendarEquals(yearMonth.[[Calendar]], other.[[Calendar]]). return CalendarEquals(isolate, Handle<JSReceiver>(year_month->calendar(), isolate), Handle<JSReceiver>(other->calendar(), isolate)); } namespace { MaybeHandle<JSTemporalPlainYearMonth> AddDurationToOrSubtractDurationFromPlainYearMonth( Isolate* isolate, Arithmetic operation, Handle<JSTemporalPlainYearMonth> year_month, Handle<Object> temporal_duration_like, Handle<Object> options_obj, const char* method_name) { // 1. Let duration be ? ToTemporalDurationRecord(temporalDurationLike). DurationRecord duration; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, duration, temporal::ToTemporalDurationRecord(isolate, temporal_duration_like, method_name), Handle<JSTemporalPlainYearMonth>()); // 2. If operation is subtract, then if (operation == Arithmetic::kSubtract) { // a. Set duration to ! CreateNegatedDurationRecord(duration). duration = CreateNegatedDurationRecord(isolate, duration).ToChecked(); } // 3. Let balanceResult be ? BalanceDuration(duration.[[Days]], // duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], // duration.[[Milliseconds]], duration.[[Microseconds]], // duration.[[Nanoseconds]], "day"). TimeDurationRecord balance_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, balance_result, BalanceDuration(isolate, Unit::kDay, duration.time_duration, method_name), Handle<JSTemporalPlainYearMonth>()); // 4. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainYearMonth); // 5. Let calendar be yearMonth.[[Calendar]]. Handle<JSReceiver> calendar(year_month->calendar(), isolate); // 6. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »). Factory* factory = isolate->factory(); Handle<FixedArray> field_names = MonthCodeYearInFixedArray(isolate); ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, CalendarFields(isolate, calendar, field_names), JSTemporalPlainYearMonth); // 7. Let fields be ? PrepareTemporalFields(yearMonth, fieldNames, «»). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, PrepareTemporalFields(isolate, year_month, field_names, RequiredFields::kNone), JSTemporalPlainYearMonth); // 8. Set sign to ! DurationSign(duration.[[Years]], duration.[[Months]], // duration.[[Weeks]], balanceResult.[[Days]], 0, 0, 0, 0, 0, 0). int32_t sign = DurationSign(isolate, {duration.years, duration.months, duration.weeks, {balance_result.days, 0, 0, 0, 0, 0, 0}}); // 9. If sign < 0, then Handle<Object> day; if (sign < 0) { // a. Let dayFromCalendar be ? CalendarDaysInMonth(calendar, yearMonth). Handle<Object> day_from_calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, day_from_calendar, temporal::CalendarDaysInMonth(isolate, calendar, year_month), JSTemporalPlainYearMonth); // b. Let day be ? ToPositiveInteger(dayFromCalendar). ASSIGN_RETURN_ON_EXCEPTION(isolate, day, ToPositiveInteger(isolate, day_from_calendar), JSTemporalPlainYearMonth); // 10. Else, } else { // a. Let day be 1. day = handle(Smi::FromInt(1), isolate); } // 11. Perform ! CreateDataPropertyOrThrow(fields, "day", day). CHECK(JSReceiver::CreateDataProperty(isolate, fields, factory->day_string(), day, Just(kThrowOnError)) .FromJust()); // 12. Let date be ? CalendarDateFromFields(calendar, fields). Handle<JSTemporalPlainDate> date; ASSIGN_RETURN_ON_EXCEPTION( isolate, date, FromFields<JSTemporalPlainDate>( isolate, calendar, fields, isolate->factory()->undefined_value(), isolate->factory()->dateFromFields_string(), JS_TEMPORAL_PLAIN_DATE_TYPE), JSTemporalPlainYearMonth); // 13. Let durationToAdd be ! CreateTemporalDuration(duration.[[Years]], // duration.[[Months]], duration.[[Weeks]], balanceResult.[[Days]], 0, 0, 0, // 0, 0, 0). Handle<JSTemporalDuration> duration_to_add = CreateTemporalDuration(isolate, {duration.years, duration.months, duration.weeks, {balance_result.days, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); // 14. Let optionsCopy be OrdinaryObjectCreate(null). Handle<JSReceiver> options_copy = isolate->factory()->NewJSObjectWithNullProto(); // 15. Let entries be ? EnumerableOwnPropertyNames(options, key+value). // 16. For each element nextEntry of entries, do // a. Perform ! CreateDataPropertyOrThrow(optionsCopy, nextEntry[0], // nextEntry[1]). bool set; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, set, JSReceiver::SetOrCopyDataProperties( isolate, options_copy, options, PropertiesEnumerationMode::kEnumerationOrder, nullptr, false), Handle<JSTemporalPlainYearMonth>()); // 17. Let addedDate be ? CalendarDateAdd(calendar, date, durationToAdd, // options). Handle<JSTemporalPlainDate> added_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, added_date, CalendarDateAdd(isolate, calendar, date, duration_to_add, options), JSTemporalPlainYearMonth); // 18. Let addedDateFields be ? PrepareTemporalFields(addedDate, fieldNames, // «»). Handle<JSReceiver> added_date_fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, added_date_fields, PrepareTemporalFields(isolate, added_date, field_names, RequiredFields::kNone), JSTemporalPlainYearMonth); // 19. Return ? CalendarYearMonthFromFields(calendar, addedDateFields, // optionsCopy). return FromFields<JSTemporalPlainYearMonth>( isolate, calendar, added_date_fields, options_copy, isolate->factory()->yearMonthFromFields_string(), JS_TEMPORAL_PLAIN_YEAR_MONTH_TYPE); } } // namespace // #sec-temporal.plainyearmonth.prototype.add MaybeHandle<JSTemporalPlainYearMonth> JSTemporalPlainYearMonth::Add( Isolate* isolate, Handle<JSTemporalPlainYearMonth> year_month, Handle<Object> temporal_duration_like, Handle<Object> options) { return AddDurationToOrSubtractDurationFromPlainYearMonth( isolate, Arithmetic::kAdd, year_month, temporal_duration_like, options, "Temporal.PlainYearMonth.prototype.add"); } // #sec-temporal.plainyearmonth.prototype.subtract MaybeHandle<JSTemporalPlainYearMonth> JSTemporalPlainYearMonth::Subtract( Isolate* isolate, Handle<JSTemporalPlainYearMonth> year_month, Handle<Object> temporal_duration_like, Handle<Object> options) { return AddDurationToOrSubtractDurationFromPlainYearMonth( isolate, Arithmetic::kSubtract, year_month, temporal_duration_like, options, "Temporal.PlainYearMonth.prototype.subtract"); } namespace { // #sec-temporal-differencetemporalplandyearmonth MaybeHandle<JSTemporalDuration> DifferenceTemporalPlainYearMonth( Isolate* isolate, TimePreposition operation, Handle<JSTemporalPlainYearMonth> year_month, Handle<Object> other_obj, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If operation is since, let sign be -1. Otherwise, let sign be 1. double sign = operation == TimePreposition::kSince ? -1 : 1; // 2. Set other to ? ToTemporalDateTime(other). Handle<JSTemporalPlainYearMonth> other; ASSIGN_RETURN_ON_EXCEPTION( isolate, other, ToTemporalYearMonth(isolate, other_obj, method_name), JSTemporalDuration); // 3. Let calendar be yearMonth.[[Calendar]]. Handle<JSReceiver> calendar(year_month->calendar(), isolate); // 4. If ? CalendarEquals(calendar, other.[[Calendar]]) is false, throw a // RangeError exception. bool calendar_equals; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, calendar_equals, CalendarEqualsBool(isolate, calendar, handle(other->calendar(), isolate)), Handle<JSTemporalDuration>()); if (!calendar_equals) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalDuration); } // 5. Let settings be ? GetDifferenceSettings(operation, options, date, « // "week", "day" », "month", "year"). DifferenceSettings settings; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, settings, GetDifferenceSettings(isolate, operation, options, UnitGroup::kDate, DisallowedUnitsInDifferenceSettings::kWeekAndDay, Unit::kMonth, Unit::kYear, method_name), Handle<JSTemporalDuration>()); // 6. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »). Factory* factory = isolate->factory(); Handle<FixedArray> field_names = MonthCodeYearInFixedArray(isolate); ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, CalendarFields(isolate, calendar, field_names), JSTemporalDuration); // 7. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»). Handle<JSReceiver> other_fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, other_fields, PrepareTemporalFields(isolate, other, field_names, RequiredFields::kNone), JSTemporalDuration); // 8. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽). Handle<Object> one = handle(Smi::FromInt(1), isolate); CHECK(JSReceiver::CreateDataProperty(isolate, other_fields, factory->day_string(), one, Just(kThrowOnError)) .FromJust()); // 9. Let otherDate be ? CalendarDateFromFields(calendar, otherFields). // DateFromFields(Isolate* isolate, Handle<JSTemporalPlainDate> other_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, other_date, DateFromFields(isolate, calendar, other_fields, isolate->factory()->undefined_value()), JSTemporalDuration); // 10. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»). Handle<JSReceiver> this_fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, this_fields, PrepareTemporalFields(isolate, year_month, field_names, RequiredFields::kNone), JSTemporalDuration); // 11. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽). CHECK(JSReceiver::CreateDataProperty(isolate, this_fields, factory->day_string(), one, Just(kThrowOnError)) .FromJust()); // 12. Let thisDate be ? CalendarDateFromFields(calendar, thisFields). Handle<JSTemporalPlainDate> this_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, this_date, DateFromFields(isolate, calendar, this_fields, isolate->factory()->undefined_value()), JSTemporalDuration); // 13. Let untilOptions be ? MergeLargestUnitOption(settings.[[Options]], // settings.[[LargestUnit]]). Handle<JSReceiver> until_options; ASSIGN_RETURN_ON_EXCEPTION( isolate, until_options, MergeLargestUnitOption(isolate, settings.options, settings.largest_unit), JSTemporalDuration); // 14. Let result be ? CalendarDateUntil(calendar, thisDate, otherDate, // untilOptions). Handle<JSTemporalDuration> result; ASSIGN_RETURN_ON_EXCEPTION(isolate, result, CalendarDateUntil(isolate, calendar, this_date, other_date, until_options), JSTemporalDuration); // 15. If settings.[[SmallestUnit]] is not "month" or // settings.[[RoundingIncrement]] ≠ 1, then if (settings.smallest_unit != Unit::kMonth || settings.rounding_increment != 1) { // a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], 0, // 0, 0, 0, 0, 0, 0, 0, settings.[[RoundingIncrement]], // settings.[[SmallestUnit]], settings.[[RoundingMode]], // thisDate)).[[DurationRecord]]. DurationRecordWithRemainder round_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, round_result, RoundDuration(isolate, {Object::Number(result->years()), Object::Number(result->months()), 0, {0, 0, 0, 0, 0, 0, 0}}, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, this_date, method_name), Handle<JSTemporalDuration>()); // 16. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × // result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0). return CreateTemporalDuration(isolate, {round_result.record.years * sign, round_result.record.months * sign, 0, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); } // 16. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × // result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0). return CreateTemporalDuration(isolate, {Object::Number(result->years()) * sign, Object::Number(result->months()) * sign, 0, {0, 0, 0, 0, 0, 0, 0}}) .ToHandleChecked(); } } // namespace // #sec-temporal.plainyearmonth.prototype.until MaybeHandle<JSTemporalDuration> JSTemporalPlainYearMonth::Until( Isolate* isolate, Handle<JSTemporalPlainYearMonth> handle, Handle<Object> other, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return DifferenceTemporalPlainYearMonth( isolate, TimePreposition::kUntil, handle, other, options, "Temporal.PlainYearMonth.prototype.until"); } // #sec-temporal.plainyearmonth.prototype.since MaybeHandle<JSTemporalDuration> JSTemporalPlainYearMonth::Since( Isolate* isolate, Handle<JSTemporalPlainYearMonth> handle, Handle<Object> other, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return DifferenceTemporalPlainYearMonth( isolate, TimePreposition::kSince, handle, other, options, "Temporal.PlainYearMonth.prototype.since"); } // #sec-temporal.plainyearmonth.prototype.with MaybeHandle<JSTemporalPlainYearMonth> JSTemporalPlainYearMonth::With( Isolate* isolate, Handle<JSTemporalPlainYearMonth> temporal_year_month, Handle<Object> temporal_year_month_like_obj, Handle<Object> options_obj) { // 6. Let fieldNames be ? CalendarFields(calendar, « "month", "monthCode", // "year" »). Handle<FixedArray> field_names = MonthMonthCodeYearInFixedArray(isolate); return PlainDateOrYearMonthOrMonthDayWith<JSTemporalPlainYearMonth, YearMonthFromFields>( isolate, temporal_year_month, temporal_year_month_like_obj, options_obj, field_names, "Temporal.PlainYearMonth.prototype.with"); } // #sec-temporal.plainyearmonth.prototype.toplaindate MaybeHandle<JSTemporalPlainDate> JSTemporalPlainYearMonth::ToPlainDate( Isolate* isolate, Handle<JSTemporalPlainYearMonth> year_month, Handle<Object> item_obj) { Factory* factory = isolate->factory(); // 5. Let receiverFieldNames be ? CalendarFields(calendar, « "monthCode", // "year" »). // 7. Let inputFieldNames be ? CalendarFields(calendar, « "day" »). return PlainMonthDayOrYearMonthToPlainDate<JSTemporalPlainYearMonth>( isolate, year_month, item_obj, factory->monthCode_string(), factory->year_string(), factory->day_string()); } // #sec-temporal.plainyearmonth.prototype.getisofields MaybeHandle<JSReceiver> JSTemporalPlainYearMonth::GetISOFields( Isolate* isolate, Handle<JSTemporalPlainYearMonth> year_month) { Factory* factory = isolate->factory(); // 1. Let yearMonth be the this value. // 2. Perform ? RequireInternalSlot(yearMonth, // [[InitializedTemporalYearMonth]]). // 3. Let fields be ! OrdinaryObjectCreate(%Object.prototype%). Handle<JSObject> fields = isolate->factory()->NewJSObject(isolate->object_function()); // 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", // yearMonth.[[Calendar]]). CHECK(JSReceiver::CreateDataProperty( isolate, fields, factory->calendar_string(), Handle<JSReceiver>(year_month->calendar(), isolate), Just(kThrowOnError)) .FromJust()); // 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", // 𝔽(yearMonth.[[ISODay]])). // 6. Perform ! CreateDataPropertyOrThrow(fields, "isoMonth", // 𝔽(yearMonth.[[ISOMonth]])). // 7. Perform ! CreateDataPropertyOrThrow(fields, "isoYear", // 𝔽(yearMonth.[[ISOYear]])). DEFINE_INT_FIELD(fields, isoDay, iso_day, year_month) DEFINE_INT_FIELD(fields, isoMonth, iso_month, year_month) DEFINE_INT_FIELD(fields, isoYear, iso_year, year_month) // 8. Return fields. return fields; } // #sec-temporal.plainyearmonth.prototype.tojson MaybeHandle<String> JSTemporalPlainYearMonth::ToJSON( Isolate* isolate, Handle<JSTemporalPlainYearMonth> year_month) { return TemporalYearMonthToString(isolate, year_month, ShowCalendar::kAuto); } // #sec-temporal.plainyearmonth.prototype.tostring MaybeHandle<String> JSTemporalPlainYearMonth::ToString( Isolate* isolate, Handle<JSTemporalPlainYearMonth> year_month, Handle<Object> options) { return TemporalToString<JSTemporalPlainYearMonth, TemporalYearMonthToString>( isolate, year_month, options, "Temporal.PlainYearMonth.prototype.toString"); } // #sec-temporal.plainyearmonth.prototype.tolocalestring MaybeHandle<String> JSTemporalPlainYearMonth::ToLocaleString( Isolate* isolate, Handle<JSTemporalPlainYearMonth> year_month, Handle<Object> locales, Handle<Object> options) { #ifdef V8_INTL_SUPPORT return JSDateTimeFormat::TemporalToLocaleString( isolate, year_month, locales, options, "Temporal.PlainYearMonth.prototype.toLocaleString"); #else // V8_INTL_SUPPORT return TemporalYearMonthToString(isolate, year_month, ShowCalendar::kAuto); #endif // V8_INTL_SUPPORT } // #sec-temporal-plaintime-constructor MaybeHandle<JSTemporalPlainTime> JSTemporalPlainTime::Constructor( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<Object> hour_obj, Handle<Object> minute_obj, Handle<Object> second_obj, Handle<Object> millisecond_obj, Handle<Object> microsecond_obj, Handle<Object> nanosecond_obj) { const char* method_name = "Temporal.PlainTime"; // 1. If NewTarget is undefined, then // a. Throw a TypeError exception. if (IsUndefined(*new_target)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kMethodInvokedOnWrongType, isolate->factory()->NewStringFromAsciiChecked( method_name)), JSTemporalPlainTime); } TO_INT_THROW_ON_INFTY(hour, JSTemporalPlainTime); TO_INT_THROW_ON_INFTY(minute, JSTemporalPlainTime); TO_INT_THROW_ON_INFTY(second, JSTemporalPlainTime); TO_INT_THROW_ON_INFTY(millisecond, JSTemporalPlainTime); TO_INT_THROW_ON_INFTY(microsecond, JSTemporalPlainTime); TO_INT_THROW_ON_INFTY(nanosecond, JSTemporalPlainTime); // 14. Return ? CreateTemporalTime(hour, minute, second, millisecond, // microsecond, nanosecond, NewTarget). return CreateTemporalTime( isolate, target, new_target, {hour, minute, second, millisecond, microsecond, nanosecond}); } // #sec-temporal.plaintime.prototype.tozoneddatetime MaybeHandle<JSTemporalZonedDateTime> JSTemporalPlainTime::ToZonedDateTime( Isolate* isolate, Handle<JSTemporalPlainTime> temporal_time, Handle<Object> item_obj) { const char* method_name = "Temporal.PlainTime.prototype.toZonedDateTime"; Factory* factory = isolate->factory(); // 1. Let temporalTime be the this value. // 2. Perform ? RequireInternalSlot(temporalTime, // [[InitializedTemporalTime]]). // 3. If Type(item) is not Object, then if (!IsJSReceiver(*item_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalZonedDateTime); } Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); // 4. Let temporalDateLike be ? Get(item, "plainDate"). Handle<Object> temporal_date_like; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_like, JSReceiver::GetProperty(isolate, item, factory->plainDate_string()), JSTemporalZonedDateTime); // 5. If temporalDateLike is undefined, then if (IsUndefined(*temporal_date_like)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalZonedDateTime); } // 6. Let temporalDate be ? ToTemporalDate(temporalDateLike). Handle<JSTemporalPlainDate> temporal_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date, ToTemporalDate(isolate, temporal_date_like, method_name), JSTemporalZonedDateTime); // 7. Let temporalTimeZoneLike be ? Get(item, "timeZone"). Handle<Object> temporal_time_zone_like; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_time_zone_like, JSReceiver::GetProperty(isolate, item, factory->timeZone_string()), JSTemporalZonedDateTime); // 8. If temporalTimeZoneLike is undefined, then if (IsUndefined(*temporal_time_zone_like)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalZonedDateTime); } // 9. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike). Handle<JSReceiver> time_zone; ASSIGN_RETURN_ON_EXCEPTION(isolate, time_zone, temporal::ToTemporalTimeZone( isolate, temporal_time_zone_like, method_name), JSTemporalZonedDateTime); // 10. Let temporalDateTime be ? // CreateTemporalDateTime(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], // temporalDate.[[ISODay]], temporalTime.[[ISOHour]], // temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], // temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], // temporalTime.[[ISONanosecond]], temporalDate.[[Calendar]]). Handle<JSReceiver> calendar(temporal_date->calendar(), isolate); Handle<JSTemporalPlainDateTime> temporal_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_time, temporal::CreateTemporalDateTime( isolate, {{temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()}, {temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond()}}, calendar), JSTemporalZonedDateTime); // 11. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, // temporalDateTime, "compatible"). Handle<JSTemporalInstant> instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, instant, BuiltinTimeZoneGetInstantFor(isolate, time_zone, temporal_date_time, Disambiguation::kCompatible, method_name), JSTemporalZonedDateTime); // 12. Return ? CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, // temporalDate.[[Calendar]]). return CreateTemporalZonedDateTime( isolate, handle(instant->nanoseconds(), isolate), time_zone, calendar); } namespace { // #sec-temporal-comparetemporaltime int32_t CompareTemporalTime(const TimeRecord& time1, const TimeRecord& time2) { TEMPORAL_ENTER_FUNC(); // 1. Assert: h1, min1, s1, ms1, mus1, ns1, h2, min2, s2, ms2, mus2, and ns2 // are integers. // 2. If h1 > h2, return 1. if (time1.hour > time2.hour) return 1; // 3. If h1 < h2, return -1. if (time1.hour < time2.hour) return -1; // 4. If min1 > min2, return 1. if (time1.minute > time2.minute) return 1; // 5. If min1 < min2, return -1. if (time1.minute < time2.minute) return -1; // 6. If s1 > s2, return 1. if (time1.second > time2.second) return 1; // 7. If s1 < s2, return -1. if (time1.second < time2.second) return -1; // 8. If ms1 > ms2, return 1. if (time1.millisecond > time2.millisecond) return 1; // 9. If ms1 < ms2, return -1. if (time1.millisecond < time2.millisecond) return -1; // 10. If mus1 > mus2, return 1. if (time1.microsecond > time2.microsecond) return 1; // 11. If mus1 < mus2, return -1. if (time1.microsecond < time2.microsecond) return -1; // 12. If ns1 > ns2, return 1. if (time1.nanosecond > time2.nanosecond) return 1; // 13. If ns1 < ns2, return -1. if (time1.nanosecond < time2.nanosecond) return -1; // 14. Return 0. return 0; } } // namespace // #sec-temporal.plaintime.compare MaybeHandle<Smi> JSTemporalPlainTime::Compare(Isolate* isolate, Handle<Object> one_obj, Handle<Object> two_obj) { const char* method_name = "Temporal.PainTime.compare"; // 1. Set one to ? ToTemporalTime(one). Handle<JSTemporalPlainTime> one; ASSIGN_RETURN_ON_EXCEPTION( isolate, one, temporal::ToTemporalTime(isolate, one_obj, method_name), Smi); // 2. Set two to ? ToTemporalTime(two). Handle<JSTemporalPlainTime> two; ASSIGN_RETURN_ON_EXCEPTION( isolate, two, temporal::ToTemporalTime(isolate, two_obj, method_name), Smi); // 3. Return 𝔽(! CompareTemporalTime(one.[[ISOHour]], one.[[ISOMinute]], // one.[[ISOSecond]], one.[[ISOMillisecond]], one.[[ISOMicrosecond]], // one.[[ISONanosecond]], two.[[ISOHour]], two.[[ISOMinute]], // two.[[ISOSecond]], two.[[ISOMillisecond]], two.[[ISOMicrosecond]], // two.[[ISONanosecond]])). return handle(Smi::FromInt(CompareTemporalTime( {one->iso_hour(), one->iso_minute(), one->iso_second(), one->iso_millisecond(), one->iso_microsecond(), one->iso_nanosecond()}, {two->iso_hour(), two->iso_minute(), two->iso_second(), two->iso_millisecond(), two->iso_microsecond(), two->iso_nanosecond()})), isolate); } // #sec-temporal.plaintime.prototype.equals MaybeHandle<Oddball> JSTemporalPlainTime::Equals( Isolate* isolate, Handle<JSTemporalPlainTime> temporal_time, Handle<Object> other_obj) { // 1. Let temporalTime be the this value. // 2. Perform ? RequireInternalSlot(temporalTime, // [[InitializedTemporalTime]]). // 3. Set other to ? ToTemporalTime(other). Handle<JSTemporalPlainTime> other; ASSIGN_RETURN_ON_EXCEPTION( isolate, other, temporal::ToTemporalTime(isolate, other_obj, "Temporal.PlainTime.prototype.equals"), Oddball); // 4. If temporalTime.[[ISOHour]] ≠ other.[[ISOHour]], return false. if (temporal_time->iso_hour() != other->iso_hour()) return isolate->factory()->false_value(); // 5. If temporalTime.[[ISOMinute]] ≠ other.[[ISOMinute]], return false. if (temporal_time->iso_minute() != other->iso_minute()) return isolate->factory()->false_value(); // 6. If temporalTime.[[ISOSecond]] ≠ other.[[ISOSecond]], return false. if (temporal_time->iso_second() != other->iso_second()) return isolate->factory()->false_value(); // 7. If temporalTime.[[ISOMillisecond]] ≠ other.[[ISOMillisecond]], return // false. if (temporal_time->iso_millisecond() != other->iso_millisecond()) return isolate->factory()->false_value(); // 8. If temporalTime.[[ISOMicrosecond]] ≠ other.[[ISOMicrosecond]], return // false. if (temporal_time->iso_microsecond() != other->iso_microsecond()) return isolate->factory()->false_value(); // 9. If temporalTime.[[ISONanosecond]] ≠ other.[[ISONanosecond]], return // false. if (temporal_time->iso_nanosecond() != other->iso_nanosecond()) return isolate->factory()->false_value(); // 10. Return true. return isolate->factory()->true_value(); } namespace { // #sec-temporal-maximumtemporaldurationroundingincrement Maximum MaximumTemporalDurationRoundingIncrement(Unit unit) { switch (unit) { // 1. If unit is "year", "month", "week", or "day", then case Unit::kYear: case Unit::kMonth: case Unit::kWeek: case Unit::kDay: // a. Return undefined. return {false, 0}; // 2. If unit is "hour", then case Unit::kHour: // a. Return 24. return {true, 24}; // 3. If unit is "minute" or "second", then case Unit::kMinute: case Unit::kSecond: // a. Return 60. return {true, 60}; // 4. Assert: unit is one of "millisecond", "microsecond", or "nanosecond". case Unit::kMillisecond: case Unit::kMicrosecond: case Unit::kNanosecond: // 5. Return 1000. return {true, 1000}; default: UNREACHABLE(); } } } // namespace // #sec-temporal.plaintime.prototype.round MaybeHandle<JSTemporalPlainTime> JSTemporalPlainTime::Round( Isolate* isolate, Handle<JSTemporalPlainTime> temporal_time, Handle<Object> round_to_obj) { const char* method_name = "Temporal.PlainTime.prototype.round"; Factory* factory = isolate->factory(); // 1. Let temporalTime be the this value. // 2. Perform ? RequireInternalSlot(temporalTime, // [[InitializedTemporalTime]]). // 3. If roundTo is undefined, then if (IsUndefined(*round_to_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalPlainTime); } Handle<JSReceiver> round_to; // 4. If Type(roundTo) is String, then if (IsString(*round_to_obj)) { // a. Let paramString be roundTo. Handle<String> param_string = Handle<String>::cast(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 { // 5. Set roundTo to ? GetOptionsObject(roundTo). ASSIGN_RETURN_ON_EXCEPTION( isolate, round_to, GetOptionsObject(isolate, round_to_obj, method_name), JSTemporalPlainTime); } // 6. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, // required). Unit smallest_unit; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, smallest_unit, GetTemporalUnit(isolate, round_to, "smallestUnit", UnitGroup::kTime, Unit::kNotPresent, true, method_name), Handle<JSTemporalPlainTime>()); // 7. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand"). RoundingMode rounding_mode; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_mode, ToTemporalRoundingMode(isolate, round_to, RoundingMode::kHalfExpand, method_name), Handle<JSTemporalPlainTime>()); // 8. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit). Maximum maximum = MaximumTemporalDurationRoundingIncrement(smallest_unit); // 9. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo, // maximum, false). double rounding_increment; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_increment, ToTemporalRoundingIncrement(isolate, round_to, maximum.value, maximum.defined, false), Handle<JSTemporalPlainTime>()); // 12. Let result be ! RoundTime(temporalTime.[[ISOHour]], // temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], // temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], // temporalTime.[[ISONanosecond]], roundingIncrement, smallestUnit, // roundingMode). DateTimeRecord result = RoundTime( isolate, {temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond()}, rounding_increment, smallest_unit, rounding_mode); // 13. Return ? CreateTemporalTime(result.[[Hour]], result.[[Minute]], // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], // result.[[Nanosecond]]). return CreateTemporalTime(isolate, result.time); } // #sec-temporal.plaintime.prototype.with MaybeHandle<JSTemporalPlainTime> JSTemporalPlainTime::With( Isolate* isolate, Handle<JSTemporalPlainTime> temporal_time, Handle<Object> temporal_time_like_obj, Handle<Object> options_obj) { const char* method_name = "Temporal.PlainTime.prototype.with"; // 1. Let temporalTime be the this value. // 2. Perform ? RequireInternalSlot(temporalTime, // [[InitializedTemporalTime]]). // 3. If Type(temporalTimeLike) is not Object, then if (!IsJSReceiver(*temporal_time_like_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalPlainTime); } Handle<JSReceiver> temporal_time_like = Handle<JSReceiver>::cast(temporal_time_like_obj); // 4. Perform ? RejectObjectWithCalendarOrTimeZone(temporalTimeLike). MAYBE_RETURN(RejectObjectWithCalendarOrTimeZone(isolate, temporal_time_like), Handle<JSTemporalPlainTime>()); // 5. Let partialTime be ? ToPartialTime(temporalTimeLike). TimeRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, ToPartialTime( isolate, temporal_time_like, {temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond()}, method_name), Handle<JSTemporalPlainTime>()); // 6. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainTime); // 7. Let overflow be ? ToTemporalOverflow(options). ShowOverflow overflow; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, overflow, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainTime>()); // 20. Let result be ? RegulateTime(hour, minute, second, millisecond, // microsecond, nanosecond, overflow). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, temporal::RegulateTime(isolate, result, overflow), Handle<JSTemporalPlainTime>()); // 25. Return ? CreateTemporalTime(result.[[Hour]], result.[[Minute]], // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], // result.[[Nanosecond]]). return CreateTemporalTime(isolate, result); } // #sec-temporal.now.plaintimeiso MaybeHandle<JSTemporalPlainTime> JSTemporalPlainTime::NowISO( Isolate* isolate, Handle<Object> temporal_time_zone_like) { const char* method_name = "Temporal.Now.plainTimeISO"; // 1. Let calendar be ! GetISO8601Calendar(). Handle<JSReceiver> calendar = temporal::GetISO8601Calendar(isolate); // 2. Let dateTime be ? SystemDateTime(temporalTimeZoneLike, calendar). Handle<JSTemporalPlainDateTime> date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, date_time, SystemDateTime(isolate, temporal_time_zone_like, calendar, method_name), JSTemporalPlainTime); // 3. Return ! CreateTemporalTime(dateTime.[[ISOHour]], // dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], // dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], // dateTime.[[ISONanosecond]]). return CreateTemporalTime( isolate, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}) .ToHandleChecked(); } // #sec-temporal.plaintime.from MaybeHandle<JSTemporalPlainTime> JSTemporalPlainTime::From( Isolate* isolate, Handle<Object> item_obj, Handle<Object> options_obj) { const char* method_name = "Temporal.PlainTime.from"; // 1. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalPlainTime); // 2. Let overflow be ? ToTemporalOverflow(options). ShowOverflow overflow; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, overflow, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalPlainTime>()); // 3. If Type(item) is Object and item has an [[InitializedTemporalTime]] // internal slot, then if (IsJSTemporalPlainTime(*item_obj)) { // a. Return ? CreateTemporalTime(item.[[ISOHour]], item.[[ISOMinute]], // item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]], // item.[[ISONanosecond]]). Handle<JSTemporalPlainTime> item = Handle<JSTemporalPlainTime>::cast(item_obj); return CreateTemporalTime( isolate, {item->iso_hour(), item->iso_minute(), item->iso_second(), item->iso_millisecond(), item->iso_microsecond(), item->iso_nanosecond()}); } // 4. Return ? ToTemporalTime(item, overflow). return temporal::ToTemporalTime(isolate, item_obj, method_name, overflow); } // #sec-temporal.plaintime.prototype.toplaindatetime MaybeHandle<JSTemporalPlainDateTime> JSTemporalPlainTime::ToPlainDateTime( Isolate* isolate, Handle<JSTemporalPlainTime> temporal_time, Handle<Object> temporal_date_like) { // 1. Let temporalTime be the this value. // 2. Perform ? RequireInternalSlot(temporalTime, // [[InitializedTemporalTime]]). // 3. Set temporalDate to ? ToTemporalDate(temporalDate). Handle<JSTemporalPlainDate> temporal_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date, ToTemporalDate(isolate, temporal_date_like, "Temporal.PlainTime.prototype.toPlainDateTime"), JSTemporalPlainDateTime); // 4. Return ? CreateTemporalDateTime(temporalDate.[[ISOYear]], // temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], // temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], // temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], // temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], // temporalDate.[[Calendar]]). return temporal::CreateTemporalDateTime( isolate, {{temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()}, {temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond()}}, handle(temporal_date->calendar(), isolate)); } namespace { // #sec-temporal-adddurationtoorsubtractdurationfromplaintime MaybeHandle<JSTemporalPlainTime> AddDurationToOrSubtractDurationFromPlainTime( Isolate* isolate, Arithmetic operation, Handle<JSTemporalPlainTime> temporal_time, Handle<Object> temporal_duration_like, const char* method_name) { // 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1. double sign = operation == Arithmetic::kSubtract ? -1.0 : 1.0; // 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike). DurationRecord duration; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, duration, temporal::ToTemporalDurationRecord(isolate, temporal_duration_like, method_name), Handle<JSTemporalPlainTime>()); TimeDurationRecord& time_duration = duration.time_duration; // 3. Let result be ! AddTime(temporalTime.[[ISOHour]], // temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], // temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], // temporalTime.[[ISONanosecond]], sign x duration.[[Hours]], sign x // duration.[[Minutes]], sign x duration.[[Seconds]], sign x // duration.[[Milliseconds]], sign x duration.[[Microseconds]], sign x // duration.[[Nanoseconds]]). DateTimeRecord result = AddTime( isolate, {temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond()}, {0, sign * time_duration.hours, sign * time_duration.minutes, sign * time_duration.seconds, sign * time_duration.milliseconds, sign * time_duration.microseconds, sign * time_duration.nanoseconds}); // 4. Assert: ! IsValidTime(result.[[Hour]], result.[[Minute]], // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], // result.[[Nanosecond]]) is true. DCHECK(IsValidTime(isolate, result.time)); // 5. Return ? CreateTemporalTime(result.[[Hour]], result.[[Minute]], // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], // result.[[Nanosecond]]). return CreateTemporalTime(isolate, result.time); } } // namespace // #sec-temporal.plaintime.prototype.add MaybeHandle<JSTemporalPlainTime> JSTemporalPlainTime::Add( Isolate* isolate, Handle<JSTemporalPlainTime> temporal_time, Handle<Object> temporal_duration_like) { return AddDurationToOrSubtractDurationFromPlainTime( isolate, Arithmetic::kAdd, temporal_time, temporal_duration_like, "Temporal.PlainTime.prototype.add"); } // #sec-temporal.plaintime.prototype.subtract MaybeHandle<JSTemporalPlainTime> JSTemporalPlainTime::Subtract( Isolate* isolate, Handle<JSTemporalPlainTime> temporal_time, Handle<Object> temporal_duration_like) { return AddDurationToOrSubtractDurationFromPlainTime( isolate, Arithmetic::kSubtract, temporal_time, temporal_duration_like, "Temporal.PlainTime.prototype.subtract"); } namespace { // #sec-temporal-differencetemporalplantime MaybeHandle<JSTemporalDuration> DifferenceTemporalPlainTime( Isolate* isolate, TimePreposition operation, Handle<JSTemporalPlainTime> temporal_time, Handle<Object> other_obj, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If operation is since, let sign be -1. Otherwise, let sign be 1. double sign = operation == TimePreposition::kSince ? -1 : 1; // 2. Set other to ? ToTemporalDate(other). Handle<JSTemporalPlainTime> other; ASSIGN_RETURN_ON_EXCEPTION( isolate, other, temporal::ToTemporalTime(isolate, other_obj, method_name), JSTemporalDuration); // 3. Let settings be ? GetDifferenceSettings(operation, options, time, « », // "nanosecond", "hour"). DifferenceSettings settings; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, settings, GetDifferenceSettings(isolate, operation, options, UnitGroup::kTime, DisallowedUnitsInDifferenceSettings::kNone, Unit::kNanosecond, Unit::kHour, method_name), Handle<JSTemporalDuration>()); // 4. Let result be ! DifferenceTime(temporalTime.[[ISOHour]], // temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], // temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], // temporalTime.[[ISONanosecond]], other.[[ISOHour]], other.[[ISOMinute]], // other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], // other.[[ISONanosecond]]). DurationRecordWithRemainder result; result.record.time_duration = DifferenceTime( isolate, {temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond()}, {other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond()}) .ToChecked(); // 5. Set result to (! RoundDuration(0, 0, 0, 0, result.[[Hours]], // result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], // result.[[Microseconds]], result.[[Nanoseconds]], // settings.[[RoundingIncrement]], settings.[[SmallestUnit]], // settings.[[RoundingMode]])).[[DurationRecord]]. result.record.years = result.record.months = result.record.weeks = result.record.time_duration.days = 0; result = RoundDuration(isolate, result.record, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, method_name) .ToChecked(); // 6. Set result to ! BalanceDuration(0, result.[[Hours]], result.[[Minutes]], // result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], // result.[[Nanoseconds]], settings.[[LargestUnit]]). result.record.time_duration.days = 0; result.record.time_duration = BalanceDuration(isolate, settings.largest_unit, result.record.time_duration, method_name) .ToChecked(); // 7. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], // sign × result.[[Minutes]], sign × result.[[Seconds]], sign × // result.[[Milliseconds]], sign × result.[[Microseconds]], sign × // result.[[Nanoseconds]]). result.record.years = result.record.months = result.record.weeks = result.record.time_duration.days = 0; result.record.time_duration.hours *= sign; result.record.time_duration.minutes *= sign; result.record.time_duration.seconds *= sign; result.record.time_duration.milliseconds *= sign; result.record.time_duration.microseconds *= sign; result.record.time_duration.nanoseconds *= sign; return CreateTemporalDuration(isolate, result.record).ToHandleChecked(); } } // namespace // #sec-temporal.plaintime.prototype.until MaybeHandle<JSTemporalDuration> JSTemporalPlainTime::Until( Isolate* isolate, Handle<JSTemporalPlainTime> handle, Handle<Object> other, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return DifferenceTemporalPlainTime(isolate, TimePreposition::kUntil, handle, other, options, "Temporal.PlainTime.prototype.until"); } // #sec-temporal.plaintime.prototype.since MaybeHandle<JSTemporalDuration> JSTemporalPlainTime::Since( Isolate* isolate, Handle<JSTemporalPlainTime> handle, Handle<Object> other, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return DifferenceTemporalPlainTime(isolate, TimePreposition::kSince, handle, other, options, "Temporal.PlainTime.prototype.since"); } // #sec-temporal.plaintime.prototype.getisofields MaybeHandle<JSReceiver> JSTemporalPlainTime::GetISOFields( Isolate* isolate, Handle<JSTemporalPlainTime> temporal_time) { Factory* factory = isolate->factory(); // 1. Let temporalTime be the this value. // 2. Perform ? RequireInternalSlot(temporalTime, // [[InitializedTemporalTime]]). // 3. Let fields be ! OrdinaryObjectCreate(%Object.prototype%). Handle<JSObject> fields = isolate->factory()->NewJSObject(isolate->object_function()); // 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", // temporalTime.[[Calendar]]). Handle<JSTemporalCalendar> iso8601_calendar = temporal::GetISO8601Calendar(isolate); CHECK(JSReceiver::CreateDataProperty(isolate, fields, factory->calendar_string(), iso8601_calendar, Just(kThrowOnError)) .FromJust()); // 5. Perform ! CreateDataPropertyOrThrow(fields, "isoHour", // 𝔽(temporalTime.[[ISOHour]])). // 6. Perform ! CreateDataPropertyOrThrow(fields, "isoMicrosecond", // 𝔽(temporalTime.[[ISOMicrosecond]])). // 7. Perform ! CreateDataPropertyOrThrow(fields, "isoMillisecond", // 𝔽(temporalTime.[[ISOMillisecond]])). // 8. Perform ! CreateDataPropertyOrThrow(fields, "isoMinute", // 𝔽(temporalTime.[[ISOMinute]])). // 9. Perform ! CreateDataPropertyOrThrow(fields, "isoNanosecond", // 𝔽(temporalTime.[[ISONanosecond]])). // 10. Perform ! CreateDataPropertyOrThrow(fields, "isoSecond", // 𝔽(temporalTime.[[ISOSecond]])). DEFINE_INT_FIELD(fields, isoHour, iso_hour, temporal_time) DEFINE_INT_FIELD(fields, isoMicrosecond, iso_microsecond, temporal_time) DEFINE_INT_FIELD(fields, isoMillisecond, iso_millisecond, temporal_time) DEFINE_INT_FIELD(fields, isoMinute, iso_minute, temporal_time) DEFINE_INT_FIELD(fields, isoNanosecond, iso_nanosecond, temporal_time) DEFINE_INT_FIELD(fields, isoSecond, iso_second, temporal_time) // 11. Return fields. return fields; } // #sec-temporal.plaintime.prototype.tojson MaybeHandle<String> JSTemporalPlainTime::ToJSON( Isolate* isolate, Handle<JSTemporalPlainTime> temporal_time) { return TemporalTimeToString(isolate, temporal_time, Precision::kAuto); } // #sup-temporal.plaintime.prototype.tolocalestring MaybeHandle<String> JSTemporalPlainTime::ToLocaleString( Isolate* isolate, Handle<JSTemporalPlainTime> temporal_time, Handle<Object> locales, Handle<Object> options) { #ifdef V8_INTL_SUPPORT return JSDateTimeFormat::TemporalToLocaleString( isolate, temporal_time, locales, options, "Temporal.PlainTime.prototype.toLocaleString"); #else // V8_INTL_SUPPORT return TemporalTimeToString(isolate, temporal_time, Precision::kAuto); #endif // V8_INTL_SUPPORT } namespace { // #sec-temporal-getunsignedroundingmode UnsignedRoundingMode GetUnsignedRoundingMode(RoundingMode rounding_mode, bool is_negative) { // 1. If isNegative is true, return the specification type in the third column // of Table 14 where the first column is roundingMode and the second column is // "negative". if (is_negative) { switch (rounding_mode) { case RoundingMode::kCeil: return UnsignedRoundingMode::kZero; case RoundingMode::kFloor: return UnsignedRoundingMode::kInfinity; case RoundingMode::kExpand: return UnsignedRoundingMode::kInfinity; case RoundingMode::kTrunc: return UnsignedRoundingMode::kZero; case RoundingMode::kHalfCeil: return UnsignedRoundingMode::kHalfZero; case RoundingMode::kHalfFloor: return UnsignedRoundingMode::kHalfInfinity; case RoundingMode::kHalfExpand: return UnsignedRoundingMode::kHalfInfinity; case RoundingMode::kHalfTrunc: return UnsignedRoundingMode::kHalfZero; case RoundingMode::kHalfEven: return UnsignedRoundingMode::kHalfEven; } } // 2. Else, return the specification type in the third column of Table 14 // where the first column is roundingMode and the second column is "positive". switch (rounding_mode) { case RoundingMode::kCeil: return UnsignedRoundingMode::kInfinity; case RoundingMode::kFloor: return UnsignedRoundingMode::kZero; case RoundingMode::kExpand: return UnsignedRoundingMode::kInfinity; case RoundingMode::kTrunc: return UnsignedRoundingMode::kZero; case RoundingMode::kHalfCeil: return UnsignedRoundingMode::kHalfInfinity; case RoundingMode::kHalfFloor: return UnsignedRoundingMode::kHalfZero; case RoundingMode::kHalfExpand: return UnsignedRoundingMode::kHalfInfinity; case RoundingMode::kHalfTrunc: return UnsignedRoundingMode::kHalfZero; case RoundingMode::kHalfEven: return UnsignedRoundingMode::kHalfEven; } } // #sec-temporal-applyunsignedroundingmode double ApplyUnsignedRoundingMode(double x, double r1, double r2, UnsignedRoundingMode unsigned_rounding_mode) { // 1. If x is equal to r1, return r1. if (x == r1) return r1; // 2. Assert: r1 < x < r2. DCHECK_LT(r1, x); DCHECK_LT(x, r2); // 3. Assert: unsignedRoundingMode is not undefined. // 4. If unsignedRoundingMode is zero, return r1. if (unsigned_rounding_mode == UnsignedRoundingMode::kZero) return r1; // 5. If unsignedRoundingMode is infinity, return r2. if (unsigned_rounding_mode == UnsignedRoundingMode::kInfinity) return r2; // 6. Let d1 be x – r1. double d1 = x - r1; // 7. Let d2 be r2 – x. double d2 = r2 - x; // 8. If d1 < d2, return r1. if (d1 < d2) return r1; // 9. If d2 < d1, return r2. if (d2 < d1) return r2; // 10. Assert: d1 is equal to d2. DCHECK_EQ(d1, d2); // 11. If unsignedRoundingMode is half-zero, return r1. if (unsigned_rounding_mode == UnsignedRoundingMode::kHalfZero) return r1; // 12. If unsignedRoundingMode is half-infinity, return r2. if (unsigned_rounding_mode == UnsignedRoundingMode::kHalfInfinity) return r2; // 13. Assert: unsignedRoundingMode is half-even. DCHECK_EQ(unsigned_rounding_mode, UnsignedRoundingMode::kHalfEven); // 14. Let cardinality be (r1 / (r2 – r1)) modulo 2. int64_t cardinality = static_cast<int64_t>(r1) % 2; // 15. If cardinality is 0, return r1. if (cardinality == 0) return r1; // 16. Return r2. return r2; } // #sec-temporal-applyunsignedroundingmode Handle<BigInt> ApplyUnsignedRoundingMode( Isolate* isolate, Handle<BigInt> num, Handle<BigInt> increment, Handle<BigInt> r1, Handle<BigInt> r2, UnsignedRoundingMode unsigned_rounding_mode) { // 1. If x is equal to r1, return r1. Handle<BigInt> rr1 = BigInt::Multiply(isolate, increment, r1).ToHandleChecked(); Handle<BigInt> rr2 = BigInt::Multiply(isolate, increment, r2).ToHandleChecked(); if (BigInt::EqualToBigInt(*num, *rr1)) return r1; // 2. Assert: r1 < x < r2. DCHECK_EQ(BigInt::CompareToBigInt(rr1, num), ComparisonResult::kLessThan); DCHECK_EQ(BigInt::CompareToBigInt(num, rr2), ComparisonResult::kLessThan); // 3. Assert: unsignedRoundingMode is not undefined. // 4. If unsignedRoundingMode is zero, return r1. if (unsigned_rounding_mode == UnsignedRoundingMode::kZero) return r1; // 5. If unsignedRoundingMode is infinity, return r2. if (unsigned_rounding_mode == UnsignedRoundingMode::kInfinity) return r2; // 6. Let d1 be x – r1. Handle<BigInt> dd1 = BigInt::Subtract(isolate, num, rr1).ToHandleChecked(); // 7. Let d2 be r2 – x. Handle<BigInt> dd2 = BigInt::Subtract(isolate, rr2, num).ToHandleChecked(); // 8. If d1 < d2, return r1. if (BigInt::CompareToBigInt(dd1, dd2) == ComparisonResult::kLessThan) { return r1; } // 9. If d2 < d1, return r2. if (BigInt::CompareToBigInt(dd2, dd1) == ComparisonResult::kLessThan) { return r2; } // 10. Assert: d1 is equal to d2. DCHECK_EQ(BigInt::CompareToBigInt(dd1, dd2), ComparisonResult::kEqual); // 11. If unsignedRoundingMode is half-zero, return r1. if (unsigned_rounding_mode == UnsignedRoundingMode::kHalfZero) return r1; // 12. If unsignedRoundingMode is half-infinity, return r2. if (unsigned_rounding_mode == UnsignedRoundingMode::kHalfInfinity) return r2; // 13. Assert: unsignedRoundingMode is half-even. DCHECK_EQ(unsigned_rounding_mode, UnsignedRoundingMode::kHalfEven); // 14. Let cardinality be (r1 / (r2 – r1)) modulo 2. Handle<BigInt> cardinality = BigInt::Remainder(isolate, r1, BigInt::FromInt64(isolate, 2)) .ToHandleChecked(); // 15. If cardinality is 0, return r1. if (!cardinality->ToBoolean()) return r1; // 16. Return r2. return r2; } // #sec-temporal-roundnumbertoincrement // For the case that x is double. double RoundNumberToIncrement(Isolate* isolate, double x, double increment, RoundingMode rounding_mode) { TEMPORAL_ENTER_FUNC(); // 1. Let quotient be x / increment. double quotient = x / increment; bool is_negative; // 2. If quotient < 0, then if (quotient < 0) { // a. Let isNegative be true. is_negative = true; // b. Set quotient to -quotient. quotient = -quotient; // 3. Else, } else { // a. Let isNegative be false. is_negative = false; } // 4. Let unsignedRoundingMode be GetUnsignedRoundingMode(roundingMode, // isNegative). UnsignedRoundingMode unsigned_rounding_mode = GetUnsignedRoundingMode(rounding_mode, is_negative); // 5. Let r1 be the largest integer such that r1 ≤ quotient. double r1 = std::floor(quotient); // 6. Let r2 be the smallest integer such that r2 > quotient. double r2 = std::floor(quotient + 1); // 7. Let rounded be ApplyUnsignedRoundingMode(quotient, r1, r2, // unsignedRoundingMode). double rounded = ApplyUnsignedRoundingMode(quotient, r1, r2, unsigned_rounding_mode); // 8. If isNegative is true, set rounded to -rounded. if (is_negative) { rounded = -rounded; } // 9. Return rounded × increment. return rounded * increment; } // #sec-temporal-roundnumbertoincrementasifpositive Handle<BigInt> RoundNumberToIncrementAsIfPositive(Isolate* isolate, Handle<BigInt> x, double increment, RoundingMode rounding_mode) { TEMPORAL_ENTER_FUNC(); // 1. Let quotient be x / increment. // 2. Let unsignedRoundingMode be GetUnsignedRoundingMode(roundingMode, // false). UnsignedRoundingMode unsigned_rounding_mode = GetUnsignedRoundingMode(rounding_mode, false); Handle<BigInt> increment_bigint = BigInt::FromNumber(isolate, isolate->factory()->NewNumber(increment)) .ToHandleChecked(); // 3. Let r1 be the largest integer such that r1 ≤ quotient. Handle<BigInt> r1 = BigInt::Divide(isolate, x, increment_bigint).ToHandleChecked(); // Adjust for negative quotient. if (r1->IsNegative() && BigInt::Remainder(isolate, x, increment_bigint) .ToHandleChecked() ->ToBoolean()) { r1 = BigInt::Decrement(isolate, r1).ToHandleChecked(); } // 4. Let r2 be the smallest integer such that r2 > quotient. Handle<BigInt> r2 = BigInt::Increment(isolate, r1).ToHandleChecked(); // 5. Let rounded be ApplyUnsignedRoundingMode(quotient, r1, r2, // unsignedRoundingMode). Handle<BigInt> rounded = ApplyUnsignedRoundingMode( isolate, x, increment_bigint, r1, r2, unsigned_rounding_mode); // 6. Return rounded × increment. Handle<BigInt> result = BigInt::Multiply(isolate, rounded, increment_bigint).ToHandleChecked(); return result; } DateTimeRecord RoundTime(Isolate* isolate, const TimeRecord& time, double increment, Unit unit, RoundingMode rounding_mode, double day_length_ns) { TEMPORAL_ENTER_FUNC(); // 1. Assert: hour, minute, second, millisecond, microsecond, nanosecond, and // increment are integers. // 2. Let fractionalSecond be nanosecond × 10^−9 + microsecond × 10^−6 + // millisecond × 10−3 + second. double fractional_second = static_cast<double>(time.nanosecond) / 100000000.0 + static_cast<double>(time.microsecond) / 1000000.0 + static_cast<double>(time.millisecond) / 1000.0 + static_cast<double>(time.second); double quantity; switch (unit) { // 3. If unit is "day", then case Unit::kDay: // a. If dayLengthNs is not present, set it to 8.64 × 10^13. // b. Let quantity be (((((hour × 60 + minute) × 60 + second) × 1000 + // millisecond) × 1000 + microsecond) × 1000 + nanosecond) / dayLengthNs. quantity = (((((time.hour * 60.0 + time.minute) * 60.0 + time.second) * 1000.0 + time.millisecond) * 1000.0 + time.microsecond) * 1000.0 + time.nanosecond) / day_length_ns; break; // 4. Else if unit is "hour", then case Unit::kHour: // a. Let quantity be (fractionalSecond / 60 + minute) / 60 + hour. quantity = (fractional_second / 60.0 + time.minute) / 60.0 + time.hour; break; // 5. Else if unit is "minute", then case Unit::kMinute: // a. Let quantity be fractionalSecond / 60 + minute. quantity = fractional_second / 60.0 + time.minute; break; // 6. Else if unit is "second", then case Unit::kSecond: // a. Let quantity be fractionalSecond. quantity = fractional_second; break; // 7. Else if unit is "millisecond", then case Unit::kMillisecond: // a. Let quantity be nanosecond × 10^−6 + microsecond × 10^−3 + // millisecond. quantity = time.nanosecond / 1000000.0 + time.microsecond / 1000.0 + time.millisecond; break; // 8. Else if unit is "microsecond", then case Unit::kMicrosecond: // a. Let quantity be nanosecond × 10^−3 + microsecond. quantity = time.nanosecond / 1000.0 + time.microsecond; break; // 9. Else, default: // a. Assert: unit is "nanosecond". DCHECK_EQ(unit, Unit::kNanosecond); // b. Let quantity be nanosecond. quantity = time.nanosecond; break; } // 10. Let result be ! RoundNumberToIncrement(quantity, increment, // roundingMode). int32_t result = RoundNumberToIncrement(isolate, quantity, increment, rounding_mode); switch (unit) { // 11. If unit is "day", then case Unit::kDay: // a. Return the Record { [[Days]]: result, [[Hour]]: 0, [[Minute]]: 0, // [[Second]]: 0, [[Millisecond]]: 0, [[Microsecond]]: 0, [[Nanosecond]]: // 0 }. return {{0, 0, result}, {0, 0, 0, 0, 0, 0}}; // 12. If unit is "hour", then case Unit::kHour: // a. Return ! BalanceTime(result, 0, 0, 0, 0, 0). return BalanceTime({static_cast<double>(result), 0, 0, 0, 0, 0}); // 13. If unit is "minute", then case Unit::kMinute: // a. Return ! BalanceTime(hour, result, 0, 0, 0, 0). return BalanceTime({static_cast<double>(time.hour), static_cast<double>(result), 0, 0, 0, 0}); // 14. If unit is "second", then case Unit::kSecond: // a. Return ! BalanceTime(hour, minute, result, 0, 0, 0). return BalanceTime({static_cast<double>(time.hour), static_cast<double>(time.minute), static_cast<double>(result), 0, 0, 0}); // 15. If unit is "millisecond", then case Unit::kMillisecond: // a. Return ! BalanceTime(hour, minute, second, result, 0, 0). return BalanceTime({static_cast<double>(time.hour), static_cast<double>(time.minute), static_cast<double>(time.second), static_cast<double>(result), 0, 0}); // 16. If unit is "microsecond", then case Unit::kMicrosecond: // a. Return ! BalanceTime(hour, minute, second, millisecond, result, 0). return BalanceTime({static_cast<double>(time.hour), static_cast<double>(time.minute), static_cast<double>(time.second), static_cast<double>(time.millisecond), static_cast<double>(result), 0}); default: // 17. Assert: unit is "nanosecond". DCHECK_EQ(unit, Unit::kNanosecond); // 18. Return ! BalanceTime(hour, minute, second, millisecond, // microsecond, result). return BalanceTime( {static_cast<double>(time.hour), static_cast<double>(time.minute), static_cast<double>(time.second), static_cast<double>(time.millisecond), static_cast<double>(time.microsecond), static_cast<double>(result)}); } } // #sec-temporal-tosecondsstringprecision Maybe<StringPrecision> ToSecondsStringPrecision( Isolate* isolate, Handle<JSReceiver> normalized_options, const char* method_name) { // 1. Let smallestUnit be ? GetTemporalUnit(normalizedOptions, "smallestUnit", // time, undefined). Unit smallest_unit; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, smallest_unit, GetTemporalUnit(isolate, normalized_options, "smallestUnit", UnitGroup::kTime, Unit::kNotPresent, false, method_name), Nothing<StringPrecision>()); switch (smallest_unit) { // 2. If smallestUnit is "hour", throw a RangeError exception. case Unit::kHour: THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kPropertyValueOutOfRange, isolate->factory()->smallestUnit_string()), Nothing<StringPrecision>()); // 2. If smallestUnit is "minute", then case Unit::kMinute: // a. Return the new Record { [[Precision]]: "minute", [[Unit]]: "minute", // [[Increment]]: 1 }. return Just(StringPrecision({Precision::kMinute, Unit::kMinute, 1})); // 3. If smallestUnit is "second", then case Unit::kSecond: // a. Return the new Record { [[Precision]]: 0, [[Unit]]: "second", // [[Increment]]: 1 }. return Just(StringPrecision({Precision::k0, Unit::kSecond, 1})); // 4. If smallestUnit is "millisecond", then case Unit::kMillisecond: // a. Return the new Record { [[Precision]]: 3, [[Unit]]: "millisecond", // [[Increment]]: 1 }. return Just(StringPrecision({Precision::k3, Unit::kMillisecond, 1})); // 5. If smallestUnit is "microsecond", then case Unit::kMicrosecond: // a. Return the new Record { [[Precision]]: 6, [[Unit]]: "microsecond", // [[Increment]]: 1 }. return Just(StringPrecision({Precision::k6, Unit::kMicrosecond, 1})); // 6. If smallestUnit is "nanosecond", then case Unit::kNanosecond: // a. Return the new Record { [[Precision]]: 9, [[Unit]]: "nanosecond", // [[Increment]]: 1 }. return Just(StringPrecision({Precision::k9, Unit::kNanosecond, 1})); default: break; } Factory* factory = isolate->factory(); // 8. Assert: smallestUnit is undefined. DCHECK(smallest_unit == Unit::kNotPresent); // 9. Let fractionalDigitsVal be ? Get(normalizedOptions, // "fractionalSecondDigits"). Handle<Object> fractional_digits_val; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, fractional_digits_val, JSReceiver::GetProperty(isolate, normalized_options, factory->fractionalSecondDigits_string()), Nothing<StringPrecision>()); // 10. If Type(fractionalDigitsVal) is not Number, then if (!IsNumber(*fractional_digits_val)) { // a. If fractionalDigitsVal is not undefined, then if (!IsUndefined(*fractional_digits_val)) { // i. If ? ToString(fractionalDigitsVal) is not "auto", throw a RangeError // exception. Handle<String> string; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, string, Object::ToString(isolate, fractional_digits_val), Nothing<StringPrecision>()); if (!String::Equals(isolate, string, factory->auto_string())) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kPropertyValueOutOfRange, factory->fractionalSecondDigits_string()), Nothing<StringPrecision>()); } } // b. Return the Record { [[Precision]]: "auto", [[Unit]]: "nanosecond", // [[Increment]]: 1 }. return Just(StringPrecision({Precision::kAuto, Unit::kNanosecond, 1})); } // 11. If fractionalDigitsVal is NaN, +∞𝔽, or -∞𝔽, throw a RangeError // exception. if (IsNaN(*fractional_digits_val) || std::isinf(Object::Number(*fractional_digits_val))) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kPropertyValueOutOfRange, factory->fractionalSecondDigits_string()), Nothing<StringPrecision>()); } // 12. Let fractionalDigitCount be RoundTowardsZero(ℝ(fractionalDigitsVal)). int64_t fractional_digit_count = RoundTowardsZero(Object::Number(*fractional_digits_val)); // 13. If fractionalDigitCount < 0 or fractionalDigitCount > 9, throw a // RangeError exception. if (fractional_digit_count < 0 || fractional_digit_count > 9) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kPropertyValueOutOfRange, factory->fractionalSecondDigits_string()), Nothing<StringPrecision>()); } // 14. If fractionalDigitCount is 0, then switch (fractional_digit_count) { case 0: // a. Return the Record { [[Precision]]: 0, [[Unit]]: "second", // [[Increment]]: 1 }. return Just(StringPrecision({Precision::k0, Unit::kSecond, 1})); // 15. If fractionalDigitCount is 1, 2, or 3, then // a. Return the Record { [[Precision]]: fractionalDigitCount, [[Unit]]: // "millisecond", [[Increment]]: 10^(3 - fractionalDigitCount) }. case 1: return Just(StringPrecision({Precision::k1, Unit::kMillisecond, 100})); case 2: return Just(StringPrecision({Precision::k2, Unit::kMillisecond, 10})); case 3: return Just(StringPrecision({Precision::k3, Unit::kMillisecond, 1})); // 16. If fractionalDigitCount is 4, 5, or 6, then // a. Return the Record { [[Precision]]: fractionalDigitCount, [[Unit]]: // "microsecond", [[Increment]]: 10^(6 - fractionalDigitCount) }. case 4: return Just(StringPrecision({Precision::k4, Unit::kMicrosecond, 100})); case 5: return Just(StringPrecision({Precision::k5, Unit::kMicrosecond, 10})); case 6: return Just(StringPrecision({Precision::k6, Unit::kMicrosecond, 1})); // 17. Assert: fractionalDigitCount is 7, 8, or 9. // 18. Return the Record { [[Precision]]: fractionalDigitCount, [[Unit]]: // "nanosecond", [[Increment]]: 109 - fractionalDigitCount }. case 7: return Just(StringPrecision({Precision::k7, Unit::kNanosecond, 100})); case 8: return Just(StringPrecision({Precision::k8, Unit::kNanosecond, 10})); case 9: return Just(StringPrecision({Precision::k9, Unit::kNanosecond, 1})); default: UNREACHABLE(); } } // #sec-temporal-compareepochnanoseconds MaybeHandle<Smi> CompareEpochNanoseconds(Isolate* isolate, Handle<BigInt> one, Handle<BigInt> two) { TEMPORAL_ENTER_FUNC(); // 1. If epochNanosecondsOne > epochNanosecondsTwo, return 1. // 2. If epochNanosecondsOne < epochNanosecondsTwo, return -1. // 3. Return 0. return handle( Smi::FromInt(CompareResultToSign(BigInt::CompareToBigInt(one, two))), isolate); } } // namespace // #sec-temporal.plaintime.prototype.tostring MaybeHandle<String> JSTemporalPlainTime::ToString( Isolate* isolate, Handle<JSTemporalPlainTime> temporal_time, Handle<Object> options_obj) { const char* method_name = "Temporal.PlainTime.prototype.toString"; // 1. Let temporalTime be the this value. // 2. Perform ? RequireInternalSlot(temporalTime, // [[InitializedTemporalTime]]). // 3. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), String); // 4. Let precision be ? ToSecondsStringPrecision(options). StringPrecision precision; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, precision, ToSecondsStringPrecision(isolate, options, method_name), Handle<String>()); // 5. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). RoundingMode rounding_mode; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_mode, ToTemporalRoundingMode(isolate, options, RoundingMode::kTrunc, method_name), Handle<String>()); // 6. Let roundResult be ! RoundTime(temporalTime.[[ISOHour]], // temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], // temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], // temporalTime.[[ISONanosecond]], precision.[[Increment]], // precision.[[Unit]], roundingMode). DateTimeRecord round_result = RoundTime( isolate, {temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond()}, precision.increment, precision.unit, rounding_mode); // 7. Return ! TemporalTimeToString(roundResult.[[Hour]], // roundResult.[[Minute]], roundResult.[[Second]], // roundResult.[[Millisecond]], roundResult.[[Microsecond]], // roundResult.[[Nanosecond]], precision.[[Precision]]). return TemporalTimeToString(isolate, round_result.time, precision.precision); } // #sec-temporal.zoneddatetime MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::Constructor( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<Object> epoch_nanoseconds_obj, Handle<Object> time_zone_like, Handle<Object> calendar_like) { const char* method_name = "Temporal.ZonedDateTime"; // 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( method_name)), JSTemporalZonedDateTime); } // 2. Set epochNanoseconds to ? ToBigInt(epochNanoseconds). Handle<BigInt> epoch_nanoseconds; ASSIGN_RETURN_ON_EXCEPTION(isolate, epoch_nanoseconds, BigInt::FromObject(isolate, epoch_nanoseconds_obj), JSTemporalZonedDateTime); // 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a // RangeError exception. if (!IsValidEpochNanoseconds(isolate, epoch_nanoseconds)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalZonedDateTime); } // 4. Let timeZone be ? ToTemporalTimeZone(timeZoneLike). Handle<JSReceiver> time_zone; ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone, temporal::ToTemporalTimeZone(isolate, time_zone_like, method_name), JSTemporalZonedDateTime); // 5. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, calendar_like, method_name), JSTemporalZonedDateTime); // 6. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, // calendar, NewTarget). return CreateTemporalZonedDateTime(isolate, target, new_target, epoch_nanoseconds, time_zone, calendar); } // #sec-get-temporal.zoneddatetime.prototype.hoursinday MaybeHandle<Object> JSTemporalZonedDateTime::HoursInDay( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.ZonedDateTime.prototype.hoursInDay"; // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Let timeZone be zonedDateTime.[[TimeZone]]. Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); // 4. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); // 5. Let isoCalendar be ! GetISO8601Calendar(). Handle<JSReceiver> iso_calendar = temporal::GetISO8601Calendar(isolate); // 6. Let temporalDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, // instant, isoCalendar). Handle<JSTemporalPlainDateTime> temporal_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor(isolate, time_zone, instant, iso_calendar, method_name), Smi); // 7. Let year be temporalDateTime.[[ISOYear]]. // 8. Let month be temporalDateTime.[[ISOMonth]]. // 9. Let day be temporalDateTime.[[ISODay]]. // 10. Let today be ? CreateTemporalDateTime(year, month, day, 0, 0, 0, 0, 0, // 0, isoCalendar). Handle<JSTemporalPlainDateTime> today; ASSIGN_RETURN_ON_EXCEPTION( isolate, today, temporal::CreateTemporalDateTime( isolate, {{temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day()}, {0, 0, 0, 0, 0, 0}}, iso_calendar), Smi); // 11. Let tomorrowFields be BalanceISODate(year, month, day + 1). DateRecord tomorrow_fields = BalanceISODate( isolate, {temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day() + 1}); // 12. Let tomorrow be ? CreateTemporalDateTime(tomorrowFields.[[Year]], // tomorrowFields.[[Month]], tomorrowFields.[[Day]], 0, 0, 0, 0, 0, 0, // isoCalendar). Handle<JSTemporalPlainDateTime> tomorrow; ASSIGN_RETURN_ON_EXCEPTION( isolate, tomorrow, temporal::CreateTemporalDateTime( isolate, {tomorrow_fields, {0, 0, 0, 0, 0, 0}}, iso_calendar), Smi); // 13. Let todayInstant be ? BuiltinTimeZoneGetInstantFor(timeZone, today, // "compatible"). Handle<JSTemporalInstant> today_instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, today_instant, BuiltinTimeZoneGetInstantFor(isolate, time_zone, today, Disambiguation::kCompatible, method_name), Smi); // 14. Let tomorrowInstant be ? BuiltinTimeZoneGetInstantFor(timeZone, // tomorrow, "compatible"). Handle<JSTemporalInstant> tomorrow_instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, tomorrow_instant, BuiltinTimeZoneGetInstantFor(isolate, time_zone, tomorrow, Disambiguation::kCompatible, method_name), Smi); // 15. Let diffNs be tomorrowInstant.[[Nanoseconds]] − // todayInstant.[[Nanoseconds]]. Handle<BigInt> diff_ns = BigInt::Subtract(isolate, handle(tomorrow_instant->nanoseconds(), isolate), handle(today_instant->nanoseconds(), isolate)) .ToHandleChecked(); // 16. Return 𝔽(diffNs / (3.6 × 10^12)). // // Note: The result of the division may be non integer for TimeZone which // change fractional hours. Perform this division in two steps: // First convert it to seconds in BigInt, then perform floating point // division (seconds / 3600) to convert to hours. int64_t diff_seconds = BigInt::Divide(isolate, diff_ns, BigInt::FromUint64(isolate, 1000000000)) .ToHandleChecked() ->AsInt64(); double hours_in_that_day = static_cast<double>(diff_seconds) / 3600.0; return isolate->factory()->NewNumber(hours_in_that_day); } namespace { // #sec-temporal-totemporalzoneddatetime MaybeHandle<JSTemporalZonedDateTime> ToTemporalZonedDateTime( Isolate* isolate, Handle<Object> item_obj, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 2. Assert: Type(options) is Object or Undefined. DCHECK(IsUndefined(*options) || IsJSReceiver(*options)); // 3. Let offsetBehaviour be option. OffsetBehaviour offset_behaviour = OffsetBehaviour::kOption; // 4. Let matchBehaviour be match exactly. MatchBehaviour match_behaviour = MatchBehaviour::kMatchExactly; Handle<Object> offset_string; Handle<JSReceiver> time_zone; Handle<JSReceiver> calendar; temporal::DateTimeRecord result; // 5. If Type(item) is Object, then if (IsJSReceiver(*item_obj)) { Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); // a. If item has an [[InitializedTemporalZonedDateTime]] internal slot, // then if (IsJSTemporalZonedDateTime(*item_obj)) { // i. Return item. return Handle<JSTemporalZonedDateTime>::cast(item_obj); } // b. Let calendar be ? GetTemporalCalendarWithISODefault(item). ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, GetTemporalCalendarWithISODefault(isolate, item, method_name), JSTemporalZonedDateTime); // c. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", // "microsecond", "millisecond", "minute", "month", "monthCode", // "nanosecond", "second", "year" »). Handle<FixedArray> field_names = All10UnitsInFixedArray(isolate); ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, CalendarFields(isolate, calendar, field_names), JSTemporalZonedDateTime); // d. Append "timeZone" to fieldNames. int32_t field_length = field_names->length(); field_names = FixedArray::SetAndGrow(isolate, field_names, field_length++, factory->timeZone_string()); // e. Append "offset" to fieldNames. field_names = FixedArray::SetAndGrow(isolate, field_names, field_length++, factory->offset_string()); field_names->Shrink(isolate, field_length); // f. Let fields be ? PrepareTemporalFields(item, fieldNames, « "timeZone" // »). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION(isolate, fields, PrepareTemporalFields(isolate, item, field_names, RequiredFields::kTimeZone), JSTemporalZonedDateTime); // g. Let timeZone be ? Get(fields, "timeZone"). Handle<Object> time_zone_obj; ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone_obj, JSReceiver::GetProperty(isolate, fields, factory->timeZone_string()), JSTemporalZonedDateTime); // h. Set timeZone to ? ToTemporalTimeZone(timeZone). ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone, temporal::ToTemporalTimeZone(isolate, time_zone_obj, method_name), JSTemporalZonedDateTime); // i. Let offsetString be ? Get(fields, "offset"). ASSIGN_RETURN_ON_EXCEPTION( isolate, offset_string, JSReceiver::GetProperty(isolate, fields, factory->offset_string()), JSTemporalZonedDateTime); // j. If offsetString is undefined, then if (IsUndefined(*offset_string)) { // i. Set offsetBehaviour to wall. offset_behaviour = OffsetBehaviour::kWall; // k. Else, } else { // i. Set offsetString to ? ToString(offsetString). ASSIGN_RETURN_ON_EXCEPTION(isolate, offset_string, Object::ToString(isolate, offset_string), JSTemporalZonedDateTime); } // l. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, // options). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, InterpretTemporalDateTimeFields(isolate, calendar, fields, options, method_name), Handle<JSTemporalZonedDateTime>()); // 5. Else, } else { // a. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalZonedDateTime>()); // b. Let string be ? ToString(item). Handle<String> string; ASSIGN_RETURN_ON_EXCEPTION(isolate, string, Object::ToString(isolate, item_obj), JSTemporalZonedDateTime); // c. Let result be ? ParseTemporalZonedDateTimeString(string). DateTimeRecordWithCalendar parsed_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, parsed_result, ParseTemporalZonedDateTimeString(isolate, string), Handle<JSTemporalZonedDateTime>()); result = {parsed_result.date, parsed_result.time}; // d. Let timeZoneName be result.[[TimeZone]].[[Name]]. // e. Assert: timeZoneName is not undefined. DCHECK(!IsUndefined(*parsed_result.time_zone.name)); Handle<String> time_zone_name = Handle<String>::cast(parsed_result.time_zone.name); // f. If ParseText(StringToCodePoints(timeZoneName), // TimeZoneNumericUTCOffset) is a List of errors, then base::Optional<ParsedISO8601Result> parsed = TemporalParser::ParseTimeZoneNumericUTCOffset(isolate, time_zone_name); if (!parsed.has_value()) { // i. If ! IsValidTimeZoneName(timeZoneName) is false, throw a RangeError // exception. if (!IsValidTimeZoneName(isolate, time_zone_name)) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Handle<JSTemporalZonedDateTime>()); } // ii. Set timeZoneName to ! CanonicalizeTimeZoneName(timeZoneName). time_zone_name = CanonicalizeTimeZoneName(isolate, time_zone_name); } // g. Let offsetString be result.[[TimeZone]].[[OffsetString]]. offset_string = parsed_result.time_zone.offset_string; // h. If result.[[TimeZone]].[[Z]] is true, then if (parsed_result.time_zone.z) { // i. Set offsetBehaviour to exact. offset_behaviour = OffsetBehaviour::kExact; // i. Else if offsetString is undefined, then } else if (IsUndefined(*offset_string)) { // i. Set offsetBehaviour to wall. offset_behaviour = OffsetBehaviour::kWall; } // j. Let timeZone be ! CreateTemporalTimeZone(timeZoneName). time_zone = temporal::CreateTemporalTimeZone(isolate, time_zone_name) .ToHandleChecked(); // k. Let calendar be ? // ToTemporalCalendarWithISODefault(result.[[Calendar]]). ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ToTemporalCalendarWithISODefault(isolate, parsed_result.calendar, method_name), JSTemporalZonedDateTime); // j. Set matchBehaviour to match minutes. match_behaviour = MatchBehaviour::kMatchMinutes; } // 7. Let offsetNanoseconds be 0. int64_t offset_nanoseconds = 0; // 6. If offsetBehaviour is option, then if (offset_behaviour == OffsetBehaviour::kOption) { // a. Set offsetNanoseconds to ? ParseTimeZoneOffsetString(offsetString). DCHECK(IsString(*offset_string)); MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_nanoseconds, ParseTimeZoneOffsetString(isolate, Handle<String>::cast(offset_string)), Handle<JSTemporalZonedDateTime>()); } // 7. Let disambiguation be ? ToTemporalDisambiguation(options). Disambiguation disambiguation; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, disambiguation, ToTemporalDisambiguation(isolate, options, method_name), Handle<JSTemporalZonedDateTime>()); // 8. Let offset be ? ToTemporalOffset(options, "reject"). enum Offset offset; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset, ToTemporalOffset(isolate, options, Offset::kReject, method_name), Handle<JSTemporalZonedDateTime>()); // 9. Let epochNanoseconds be ? InterpretISODateTimeOffset(result.[[Year]], // result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], // result.[[Nanosecond]], offsetBehaviour, offsetNanoseconds, timeZone, // disambiguation, offset, matchBehaviour). // Handle<BigInt> epoch_nanoseconds; ASSIGN_RETURN_ON_EXCEPTION( isolate, epoch_nanoseconds, InterpretISODateTimeOffset(isolate, result, offset_behaviour, offset_nanoseconds, time_zone, disambiguation, offset, match_behaviour, method_name), JSTemporalZonedDateTime); // 8. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, // calendar). return CreateTemporalZonedDateTime(isolate, epoch_nanoseconds, time_zone, calendar); } MaybeHandle<JSTemporalZonedDateTime> ToTemporalZonedDateTime( Isolate* isolate, Handle<Object> item_obj, const char* method_name) { // 1. If options is not present, set options to undefined. return ToTemporalZonedDateTime( isolate, item_obj, isolate->factory()->undefined_value(), method_name); } } // namespace // #sec-temporal.zoneddatetime.from MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::From( Isolate* isolate, Handle<Object> item, Handle<Object> options_obj) { const char* method_name = "Temporal.ZonedDateTime.from"; // 1. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalZonedDateTime); // 2. If Type(item) is Object and item has an // [[InitializedTemporalZonedDateTime]] internal slot, then if (IsJSTemporalZonedDateTime(*item)) { // a. Perform ? ToTemporalOverflow(options). MAYBE_RETURN_ON_EXCEPTION_VALUE( isolate, ToTemporalOverflow(isolate, options, method_name), Handle<JSTemporalZonedDateTime>()); // b. Perform ? ToTemporalDisambiguation(options). { Disambiguation disambiguation; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, disambiguation, ToTemporalDisambiguation(isolate, options, method_name), Handle<JSTemporalZonedDateTime>()); USE(disambiguation); } // c. Perform ? ToTemporalOffset(options, "reject"). { enum Offset offset; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset, ToTemporalOffset(isolate, options, Offset::kReject, method_name), Handle<JSTemporalZonedDateTime>()); USE(offset); } // d. Return ? CreateTemporalZonedDateTime(item.[[Nanoseconds]], // item.[[TimeZone]], item.[[Calendar]]). Handle<JSTemporalZonedDateTime> zoned_date_time = Handle<JSTemporalZonedDateTime>::cast(item); return CreateTemporalZonedDateTime( isolate, handle(zoned_date_time->nanoseconds(), isolate), handle(zoned_date_time->time_zone(), isolate), handle(zoned_date_time->calendar(), isolate)); } // 3. Return ? ToTemporalZonedDateTime(item, options). return ToTemporalZonedDateTime(isolate, item, options, method_name); } // #sec-temporal.zoneddatetime.compare MaybeHandle<Smi> JSTemporalZonedDateTime::Compare(Isolate* isolate, Handle<Object> one_obj, Handle<Object> two_obj) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.ZonedDateTime.compare"; // 1. Set one to ? ToTemporalZonedDateTime(one). Handle<JSTemporalZonedDateTime> one; ASSIGN_RETURN_ON_EXCEPTION( isolate, one, ToTemporalZonedDateTime(isolate, one_obj, method_name), Smi); // 2. Set two to ? ToTemporalZonedDateTime(two). Handle<JSTemporalZonedDateTime> two; ASSIGN_RETURN_ON_EXCEPTION( isolate, two, ToTemporalZonedDateTime(isolate, two_obj, method_name), Smi); // 3. Return 𝔽(! CompareEpochNanoseconds(one.[[Nanoseconds]], // two.[[Nanoseconds]])). return CompareEpochNanoseconds(isolate, handle(one->nanoseconds(), isolate), handle(two->nanoseconds(), isolate)); } namespace { // #sec-temporal-timezoneequals Maybe<bool> TimeZoneEquals(Isolate* isolate, Handle<JSReceiver> one, Handle<JSReceiver> two) { // 1. If one and two are the same Object value, return true. if (one.is_identical_to(two)) { return Just(true); } // 2. Let timeZoneOne be ? ToString(one). Handle<String> time_zone_one; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, time_zone_one, Object::ToString(isolate, one), Nothing<bool>()); // 3. Let timeZoneTwo be ? ToString(two). Handle<String> time_zone_two; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, time_zone_two, Object::ToString(isolate, two), Nothing<bool>()); // 4. If timeZoneOne is timeZoneTwo, return true. if (String::Equals(isolate, time_zone_one, time_zone_two)) { return Just(true); } // 5. Return false. return Just(false); } } // namespace // #sec-temporal.zoneddatetime.prototype.equals MaybeHandle<Oddball> JSTemporalZonedDateTime::Equals( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> other_obj) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.ZonedDateTime.prototype.equals"; Factory* factory = isolate->factory(); // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Set other to ? ToTemporalZonedDateTime(other). Handle<JSTemporalZonedDateTime> other; ASSIGN_RETURN_ON_EXCEPTION( isolate, other, ToTemporalZonedDateTime(isolate, other_obj, method_name), Oddball); // 4. If zonedDateTime.[[Nanoseconds]] ≠ other.[[Nanoseconds]], return false. if (!BigInt::EqualToBigInt(zoned_date_time->nanoseconds(), other->nanoseconds())) { return factory->false_value(); } // 5. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is // false, return false. bool equals; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, equals, TimeZoneEquals(isolate, handle(zoned_date_time->time_zone(), isolate), handle(other->time_zone(), isolate)), Handle<Oddball>()); if (!equals) { return factory->false_value(); } // 6. Return ? CalendarEquals(zonedDateTime.[[Calendar]], other.[[Calendar]]). return CalendarEquals(isolate, handle(zoned_date_time->calendar(), isolate), handle(other->calendar(), isolate)); } namespace { // #sec-temporal-interpretisodatetimeoffset MaybeHandle<BigInt> InterpretISODateTimeOffset( Isolate* isolate, const DateTimeRecord& data, OffsetBehaviour offset_behaviour, int64_t offset_nanoseconds, Handle<JSReceiver> time_zone, Disambiguation disambiguation, Offset offset_option, MatchBehaviour match_behaviour, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. Assert: offsetNanoseconds is an integer or undefined. // 2. Let calendar be ! GetISO8601Calendar(). Handle<JSReceiver> calendar = temporal::GetISO8601Calendar(isolate); // 3. Let dateTime be ? CreateTemporalDateTime(year, month, day, hour, minute, // second, millisecond, microsecond, nanosecond, calendar). Handle<JSTemporalPlainDateTime> date_time; ASSIGN_RETURN_ON_EXCEPTION(isolate, date_time, temporal::CreateTemporalDateTime( isolate, {data.date, data.time}, calendar), BigInt); // 4. If offsetBehaviour is wall, or offsetOption is "ignore", then if (offset_behaviour == OffsetBehaviour::kWall || offset_option == Offset::kIgnore) { // a. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, dateTime, // disambiguation). Handle<JSTemporalInstant> instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, instant, BuiltinTimeZoneGetInstantFor(isolate, time_zone, date_time, disambiguation, method_name), BigInt); // b. Return instant.[[Nanoseconds]]. return handle(instant->nanoseconds(), isolate); } // 5. If offsetBehaviour is exact, or offsetOption is "use", then if (offset_behaviour == OffsetBehaviour::kExact || offset_option == Offset::kUse) { // a. Let epochNanoseconds be ? GetEpochFromISOParts(year, month, day, hour, // minute, second, millisecond, microsecond, nanosecond). Handle<BigInt> epoch_nanoseconds = GetEpochFromISOParts(isolate, {data.date, data.time}); // b. Set epochNanoseconds to epochNanoseconds - ℤ(offsetNanoseconds). epoch_nanoseconds = BigInt::Subtract(isolate, epoch_nanoseconds, BigInt::FromInt64(isolate, offset_nanoseconds)) .ToHandleChecked(); // c. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a // RangeError exception. if (!IsValidEpochNanoseconds(isolate, epoch_nanoseconds)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), BigInt); } // d. Return epochNanoseconds. return epoch_nanoseconds; } // 6. Assert: offsetBehaviour is option. DCHECK_EQ(offset_behaviour, OffsetBehaviour::kOption); // 7. Assert: offsetOption is "prefer" or "reject". DCHECK(offset_option == Offset::kPrefer || offset_option == Offset::kReject); // 8. Let possibleInstants be ? GetPossibleInstantsFor(timeZone, dateTime). Handle<FixedArray> possible_instants; ASSIGN_RETURN_ON_EXCEPTION( isolate, possible_instants, GetPossibleInstantsFor(isolate, time_zone, date_time), BigInt); // 9. For each element candidate of possibleInstants, do for (int i = 0; i < possible_instants->length(); i++) { DCHECK(IsJSTemporalInstant(possible_instants->get(i))); Handle<JSTemporalInstant> candidate( JSTemporalInstant::cast(possible_instants->get(i)), isolate); // a. Let candidateNanoseconds be ? GetOffsetNanosecondsFor(timeZone, // candidate). int64_t candidate_nanoseconds; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, candidate_nanoseconds, GetOffsetNanosecondsFor(isolate, time_zone, candidate, method_name), Handle<BigInt>()); // b. If candidateNanoseconds = offsetNanoseconds, then if (candidate_nanoseconds == offset_nanoseconds) { // i. Return candidate.[[Nanoseconds]]. return Handle<BigInt>(candidate->nanoseconds(), isolate); } // c. If matchBehaviour is match minutes, then if (match_behaviour == MatchBehaviour::kMatchMinutes) { // i. Let roundedCandidateNanoseconds be ! // RoundNumberToIncrement(candidateNanoseconds, 60 × 10^9, "halfExpand"). double rounded_candidate_nanoseconds = RoundNumberToIncrement( isolate, candidate_nanoseconds, 6e10, RoundingMode::kHalfExpand); // ii. If roundedCandidateNanoseconds = offsetNanoseconds, then if (rounded_candidate_nanoseconds == offset_nanoseconds) { // 1. Return candidate.[[Nanoseconds]]. return Handle<BigInt>(candidate->nanoseconds(), isolate); } } } // 10. If offsetOption is "reject", throw a RangeError exception. if (offset_option == Offset::kReject) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), BigInt); } // 11. Let instant be ? DisambiguatePossibleInstants(possibleInstants, // timeZone, dateTime, disambiguation). Handle<JSTemporalInstant> instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, instant, DisambiguatePossibleInstants(isolate, possible_instants, time_zone, date_time, disambiguation, method_name), BigInt); // 12. Return instant.[[Nanoseconds]]. return Handle<BigInt>(instant->nanoseconds(), isolate); } } // namespace // #sec-temporal.zoneddatetime.prototype.with MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::With( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> temporal_zoned_date_time_like_obj, Handle<Object> options_obj) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.ZonedDateTime.prototype.with"; Factory* factory = isolate->factory(); // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. If Type(temporalZonedDateTimeLike) is not Object, then if (!IsJSReceiver(*temporal_zoned_date_time_like_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalZonedDateTime); } Handle<JSReceiver> temporal_zoned_date_time_like = Handle<JSReceiver>::cast(temporal_zoned_date_time_like_obj); // 4. Perform ? RejectObjectWithCalendarOrTimeZone(temporalZonedDateTimeLike). MAYBE_RETURN(RejectObjectWithCalendarOrTimeZone( isolate, temporal_zoned_date_time_like), Handle<JSTemporalZonedDateTime>()); // 5. Let calendar be zonedDateTime.[[Calendar]]. Handle<JSReceiver> calendar(zoned_date_time->calendar(), isolate); // 6. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", // "microsecond", "millisecond", "minute", "month", "monthCode", "nanosecond", // "second", "year" »). Handle<FixedArray> field_names; ASSIGN_RETURN_ON_EXCEPTION( isolate, field_names, CalendarFields(isolate, calendar, All10UnitsInFixedArray(isolate)), JSTemporalZonedDateTime); // 7. Append "offset" to fieldNames. int32_t field_length = field_names->length(); field_names = FixedArray::SetAndGrow(isolate, field_names, field_length++, factory->offset_string()); field_names->Shrink(isolate, field_length); // 8. Let partialZonedDateTime be ? // PreparePartialTemporalFields(temporalZonedDateTimeLike, fieldNames). Handle<JSReceiver> partial_zoned_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, partial_zoned_date_time, PreparePartialTemporalFields(isolate, temporal_zoned_date_time_like, field_names), JSTemporalZonedDateTime); // 9. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalZonedDateTime); // 10. Let disambiguation be ? ToTemporalDisambiguation(options). Disambiguation disambiguation; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, disambiguation, ToTemporalDisambiguation(isolate, options, method_name), Handle<JSTemporalZonedDateTime>()); // 11. Let offset be ? ToTemporalOffset(options, "prefer"). enum Offset offset; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset, ToTemporalOffset(isolate, options, Offset::kPrefer, method_name), Handle<JSTemporalZonedDateTime>()); // 12. Let timeZone be zonedDateTime.[[TimeZone]]. Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); // 13. Append "timeZone" to fieldNames. field_length = field_names->length(); field_names = FixedArray::SetAndGrow(isolate, field_names, field_length++, factory->timeZone_string()); field_names->Shrink(isolate, field_length); // 14. Let fields be ? PrepareTemporalFields(zonedDateTime, fieldNames, « // "timeZone", "offset"»). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, PrepareTemporalFields(isolate, zoned_date_time, field_names, RequiredFields::kTimeZoneAndOffset), JSTemporalZonedDateTime); // 15. Set fields to ? CalendarMergeFields(calendar, fields, // partialZonedDateTime). ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, CalendarMergeFields(isolate, calendar, fields, partial_zoned_date_time), JSTemporalZonedDateTime); // 16. Set fields to ? PrepareTemporalFields(fields, fieldNames, « "timeZone" // , "offset"»). ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, PrepareTemporalFields(isolate, fields, field_names, RequiredFields::kTimeZoneAndOffset), JSTemporalZonedDateTime); // 17. Let offsetString be ? Get(fields, "offset"). Handle<Object> offset_string; ASSIGN_RETURN_ON_EXCEPTION( isolate, offset_string, JSReceiver::GetProperty(isolate, fields, factory->offset_string()), JSTemporalZonedDateTime); // 18. Assert: Type(offsetString) is String. DCHECK(IsString(*offset_string)); // 19. Let dateTimeResult be ? InterpretTemporalDateTimeFields(calendar, // fields, options). temporal::DateTimeRecord date_time_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, date_time_result, InterpretTemporalDateTimeFields(isolate, calendar, fields, options, method_name), Handle<JSTemporalZonedDateTime>()); // 20. Let offsetNanoseconds be ? ParseTimeZoneOffsetString(offsetString). int64_t offset_nanoseconds; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_nanoseconds, ParseTimeZoneOffsetString(isolate, Handle<String>::cast(offset_string)), Handle<JSTemporalZonedDateTime>()); // 21. Let epochNanoseconds be ? // InterpretISODateTimeOffset(dateTimeResult.[[Year]], // dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]], // dateTimeResult.[[Minute]], dateTimeResult.[[Second]], // dateTimeResult.[[Millisecond]], dateTimeResult.[[Microsecond]], // dateTimeResult.[[Nanosecond]], option, offsetNanoseconds, timeZone, // disambiguation, offset, match exactly). Handle<BigInt> epoch_nanoseconds; ASSIGN_RETURN_ON_EXCEPTION( isolate, epoch_nanoseconds, InterpretISODateTimeOffset( isolate, {date_time_result.date, date_time_result.time}, OffsetBehaviour::kOption, offset_nanoseconds, time_zone, disambiguation, offset, MatchBehaviour::kMatchExactly, method_name), JSTemporalZonedDateTime); // 27. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, // calendar). return CreateTemporalZonedDateTime(isolate, epoch_nanoseconds, time_zone, calendar); } // #sec-temporal.zoneddatetime.prototype.withcalendar MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::WithCalendar( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> calendar_like) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.ZonedDateTime.prototype.withCalendar"; // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Let calendar be ? ToTemporalCalendar(calendarLike). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, temporal::ToTemporalCalendar(isolate, calendar_like, method_name), JSTemporalZonedDateTime); // 4. Return ? CreateTemporalZonedDateTime(zonedDateTime.[[Nanoseconds]], // zonedDateTime.[[TimeZone]], calendar). Handle<BigInt> nanoseconds(zoned_date_time->nanoseconds(), isolate); Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); return CreateTemporalZonedDateTime(isolate, nanoseconds, time_zone, calendar); } // #sec-temporal.zoneddatetime.prototype.withplaindate MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::WithPlainDate( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> plain_date_like) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.ZonedDateTime.prototype.withPlainDate"; // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Let plainDate be ? ToTemporalDate(plainDateLike). Handle<JSTemporalPlainDate> plain_date; ASSIGN_RETURN_ON_EXCEPTION( isolate, plain_date, ToTemporalDate(isolate, plain_date_like, method_name), JSTemporalZonedDateTime); // 4. Let timeZone be zonedDateTime.[[TimeZone]]. Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); // 5. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); // 6. Let plainDateTime be ? // temporal::BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, // zonedDateTime.[[Calendar]]). Handle<JSTemporalPlainDateTime> plain_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, plain_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor( isolate, time_zone, instant, handle(zoned_date_time->calendar(), isolate), method_name), JSTemporalZonedDateTime); // 7. Let calendar be ? ConsolidateCalendars(zonedDateTime.[[Calendar]], // plainDate.[[Calendar]]). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, ConsolidateCalendars(isolate, handle(zoned_date_time->calendar(), isolate), handle(plain_date->calendar(), isolate)), JSTemporalZonedDateTime); // 8. Let resultPlainDateTime be ? // CreateTemporalDateTime(plainDate.[[ISOYear]], plainDate.[[ISOMonth]], // plainDate.[[ISODay]], plainDateTime.[[ISOHour]], // plainDateTime.[[ISOMinute]], plainDateTime.[[ISOSecond]], // plainDateTime.[[ISOMillisecond]], plainDateTime.[[ISOMicrosecond]], // plainDateTime.[[ISONanosecond]], calendar). Handle<JSTemporalPlainDateTime> result_plain_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, result_plain_date_time, temporal::CreateTemporalDateTime( isolate, {{plain_date->iso_year(), plain_date->iso_month(), plain_date->iso_day()}, {plain_date_time->iso_hour(), plain_date_time->iso_minute(), plain_date_time->iso_second(), plain_date_time->iso_millisecond(), plain_date_time->iso_microsecond(), plain_date_time->iso_nanosecond()}}, calendar), JSTemporalZonedDateTime); // 9. Set instant to ? BuiltinTimeZoneGetInstantFor(timeZone, // resultPlainDateTime, "compatible"). ASSIGN_RETURN_ON_EXCEPTION( isolate, instant, BuiltinTimeZoneGetInstantFor(isolate, time_zone, result_plain_date_time, Disambiguation::kCompatible, method_name), JSTemporalZonedDateTime); // 10. Return ? CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, // calendar). return CreateTemporalZonedDateTime( isolate, handle(instant->nanoseconds(), isolate), time_zone, calendar); } // #sec-temporal.zoneddatetime.prototype.withplaintime MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::WithPlainTime( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> plain_time_like) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.ZonedDateTime.prototype.withPlainTime"; // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. If plainTimeLike is undefined, then Handle<JSTemporalPlainTime> plain_time; if (IsUndefined(*plain_time_like)) { // a. Let plainTime be ? CreateTemporalTime(0, 0, 0, 0, 0, 0). ASSIGN_RETURN_ON_EXCEPTION(isolate, plain_time, CreateTemporalTime(isolate, {0, 0, 0, 0, 0, 0}), JSTemporalZonedDateTime); // 4. Else, } else { // a. Let plainTime be ? ToTemporalTime(plainTimeLike). ASSIGN_RETURN_ON_EXCEPTION( isolate, plain_time, temporal::ToTemporalTime(isolate, plain_time_like, method_name), JSTemporalZonedDateTime); } // 5. Let timeZone be zonedDateTime.[[TimeZone]]. Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); // 6. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); // 7. Let calendar be zonedDateTime.[[Calendar]]. Handle<JSReceiver> calendar(zoned_date_time->calendar(), isolate); // 8. Let plainDateTime be ? // temporal::BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar). Handle<JSTemporalPlainDateTime> plain_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, plain_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor(isolate, time_zone, instant, calendar, method_name), JSTemporalZonedDateTime); // 9. Let resultPlainDateTime be ? // CreateTemporalDateTime(plainDateTime.[[ISOYear]], // plainDateTime.[[ISOMonth]], plainDateTime.[[ISODay]], // plainTime.[[ISOHour]], plainTime.[[ISOMinute]], plainTime.[[ISOSecond]], // plainTime.[[ISOMillisecond]], plainTime.[[ISOMicrosecond]], // plainTime.[[ISONanosecond]], calendar). Handle<JSTemporalPlainDateTime> result_plain_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, result_plain_date_time, temporal::CreateTemporalDateTime( isolate, {{plain_date_time->iso_year(), plain_date_time->iso_month(), plain_date_time->iso_day()}, {plain_time->iso_hour(), plain_time->iso_minute(), plain_time->iso_second(), plain_time->iso_millisecond(), plain_time->iso_microsecond(), plain_time->iso_nanosecond()}}, calendar), JSTemporalZonedDateTime); // 10. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, // resultPlainDateTime, "compatible"). ASSIGN_RETURN_ON_EXCEPTION( isolate, instant, BuiltinTimeZoneGetInstantFor(isolate, time_zone, result_plain_date_time, Disambiguation::kCompatible, method_name), JSTemporalZonedDateTime); // 11. Return ? CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, // calendar). return CreateTemporalZonedDateTime( isolate, handle(instant->nanoseconds(), isolate), time_zone, calendar); } // #sec-temporal.zoneddatetime.prototype.withtimezone MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::WithTimeZone( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> time_zone_like) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.ZonedDateTime.prototype.withTimeZone"; // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Let timeZone be ? ToTemporalTimeZone(timeZoneLike). Handle<JSReceiver> time_zone; ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone, temporal::ToTemporalTimeZone(isolate, time_zone_like, method_name), JSTemporalZonedDateTime); // 4. Return ? CreateTemporalZonedDateTime(zonedDateTime.[[Nanoseconds]], // timeZone, zonedDateTime.[[Calendar]]). Handle<BigInt> nanoseconds(zoned_date_time->nanoseconds(), isolate); Handle<JSReceiver> calendar(zoned_date_time->calendar(), isolate); return CreateTemporalZonedDateTime(isolate, nanoseconds, time_zone, calendar); } // Common code shared by ZonedDateTime.prototype.toPlainYearMonth and // toPlainMonthDay template <typename T, MaybeHandle<T> (*from_fields_func)( Isolate*, Handle<JSReceiver>, Handle<JSReceiver>, Handle<Object>)> MaybeHandle<T> ZonedDateTimeToPlainYearMonthOrMonthDay( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<String> field_name_1, Handle<String> field_name_2, const char* method_name) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Let timeZone be zonedDateTime.[[TimeZone]]. Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); // 4. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); // 5. Let calendar be zonedDateTime.[[Calendar]]. Handle<JSReceiver> calendar(zoned_date_time->calendar(), isolate); // 6. Let temporalDateTime be ? // temporal::BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar). Handle<JSTemporalPlainDateTime> temporal_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor(isolate, time_zone, instant, calendar, method_name), T); // 7. Let fieldNames be ? CalendarFields(calendar, « field_name_1, // field_name_2 »). Handle<FixedArray> field_names = factory->NewFixedArray(2); field_names->set(0, *field_name_1); field_names->set(1, *field_name_2); ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, CalendarFields(isolate, calendar, field_names), T); // 8. Let fields be ? PrepareTemporalFields(temporalDateTime, fieldNames, «»). Handle<JSReceiver> fields; ASSIGN_RETURN_ON_EXCEPTION( isolate, fields, PrepareTemporalFields(isolate, temporal_date_time, field_names, RequiredFields::kNone), T); // 9. Return ? XxxFromFields(calendar, fields). return from_fields_func(isolate, calendar, fields, factory->undefined_value()); } // #sec-temporal.zoneddatetime.prototype.toplainyearmonth MaybeHandle<JSTemporalPlainYearMonth> JSTemporalZonedDateTime::ToPlainYearMonth( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time) { return ZonedDateTimeToPlainYearMonthOrMonthDay<JSTemporalPlainYearMonth, YearMonthFromFields>( isolate, zoned_date_time, isolate->factory()->monthCode_string(), isolate->factory()->year_string(), "Temporal.ZonedDateTime.prototype.toPlainYearMonth"); } // #sec-temporal.zoneddatetime.prototype.toplainmonthday MaybeHandle<JSTemporalPlainMonthDay> JSTemporalZonedDateTime::ToPlainMonthDay( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time) { return ZonedDateTimeToPlainYearMonthOrMonthDay<JSTemporalPlainMonthDay, MonthDayFromFields>( isolate, zoned_date_time, isolate->factory()->day_string(), isolate->factory()->monthCode_string(), "Temporal.ZonedDateTime.prototype.toPlainMonthDay"); } namespace { // #sec-temporal-temporalzoneddatetimetostring MaybeHandle<String> TemporalZonedDateTimeToString( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Precision precision, ShowCalendar show_calendar, ShowTimeZone show_time_zone, ShowOffset show_offset, double increment, Unit unit, RoundingMode rounding_mode, const char* method_name) { // 4. Let ns be ! RoundTemporalInstant(zonedDateTime.[[Nanoseconds]], // increment, unit, roundingMode). Handle<BigInt> ns = RoundTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate), increment, unit, rounding_mode); // 5. Let timeZone be zonedDateTime.[[TimeZone]]. Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); // 6. Let instant be ! CreateTemporalInstant(ns). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant(isolate, ns).ToHandleChecked(); // 7. Let isoCalendar be ! GetISO8601Calendar(). Handle<JSTemporalCalendar> iso_calendar = temporal::GetISO8601Calendar(isolate); // 8. Let temporalDateTime be ? // BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, // isoCalendar). Handle<JSTemporalPlainDateTime> temporal_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor(isolate, time_zone, instant, iso_calendar, method_name), String); // 9. Let dateTimeString be ? // TemporalDateTimeToString(temporalDateTime.[[ISOYear]], // temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], // temporalDateTime.[[ISOHour]], temporalDateTime.[[ISOMinute]], // temporalDateTime.[[ISOSecond]], temporalDateTime.[[ISOMillisecond]], // temporalDateTime.[[ISOMicrosecond]], temporalDateTime.[[ISONanosecond]], // isoCalendar, precision, "never"). Handle<String> date_time_string; ASSIGN_RETURN_ON_EXCEPTION( isolate, date_time_string, TemporalDateTimeToString( isolate, {{temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day()}, {temporal_date_time->iso_hour(), temporal_date_time->iso_minute(), temporal_date_time->iso_second(), temporal_date_time->iso_millisecond(), temporal_date_time->iso_microsecond(), temporal_date_time->iso_nanosecond()}}, iso_calendar, precision, ShowCalendar::kNever), String); IncrementalStringBuilder builder(isolate); builder.AppendString(date_time_string); // 10. If showOffset is "never", then if (show_offset == ShowOffset::kNever) { // a. Let offsetString be the empty String. // 11. Else, } else { // a. Let offsetNs be ? GetOffsetNanosecondsFor(timeZone, instant). int64_t offset_ns; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_ns, GetOffsetNanosecondsFor(isolate, time_zone, instant, method_name), Handle<String>()); // b. Let offsetString be ! FormatISOTimeZoneOffsetString(offsetNs). builder.AppendString(FormatISOTimeZoneOffsetString(isolate, offset_ns)); } // 12. If showTimeZone is "never", then if (show_time_zone == ShowTimeZone::kNever) { // a. Let timeZoneString be the empty String. // 13. Else, } else { // a. Let timeZoneID be ? ToString(timeZone). Handle<String> time_zone_id; ASSIGN_RETURN_ON_EXCEPTION(isolate, time_zone_id, Object::ToString(isolate, time_zone), String); // b. Let timeZoneString be the string-concatenation of the code unit 0x005B // (LEFT SQUARE BRACKET), timeZoneID, and the code unit 0x005D (RIGHT SQUARE // BRACKET). builder.AppendCStringLiteral("["); builder.AppendString(time_zone_id); builder.AppendCStringLiteral("]"); } // 14. Let calendarString be ? // MaybeFormatCalendarAnnotation(zonedDateTime.[[Calendar]], showCalendar). Handle<String> calendar_string; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar_string, MaybeFormatCalendarAnnotation( isolate, handle(zoned_date_time->calendar(), isolate), show_calendar), String); // 15. Return the string-concatenation of dateTimeString, offsetString, // timeZoneString, and calendarString. builder.AppendString(calendar_string); return builder.Finish(); } // #sec-temporal-temporalzoneddatetimetostring MaybeHandle<String> TemporalZonedDateTimeToString( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Precision precision, ShowCalendar show_calendar, ShowTimeZone show_time_zone, ShowOffset show_offset, const char* method_name) { // 1. Assert: Type(zonedDateTime) is Object and zonedDateTime has an // [[InitializedTemporalZonedDateTime]] internal slot. // 2. If increment is not present, set it to 1. // 3. If unit is not present, set it to "nanosecond". // 4. If roundingMode is not present, set it to "trunc". return TemporalZonedDateTimeToString( isolate, zoned_date_time, precision, show_calendar, show_time_zone, show_offset, 1, Unit::kNanosecond, RoundingMode::kTrunc, method_name); } } // namespace // #sec-temporal.zoneddatetime.prototype.tojson MaybeHandle<String> JSTemporalZonedDateTime::ToJSON( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time) { TEMPORAL_ENTER_FUNC(); // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Return ? TemporalZonedDateTimeToString(zonedDateTime, "auto", "auto", // "auto", "auto"). return TemporalZonedDateTimeToString( isolate, zoned_date_time, Precision::kAuto, ShowCalendar::kAuto, ShowTimeZone::kAuto, ShowOffset::kAuto, "Temporal.ZonedDateTime.prototype.toJSON"); } // #sec-temporal.zoneddatetime.prototype.tolocalestring MaybeHandle<String> JSTemporalZonedDateTime::ToLocaleString( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> locales, Handle<Object> options) { const char* method_name = "Temporal.ZonedDateTime.prototype.toLocaleString"; #ifdef V8_INTL_SUPPORT return JSDateTimeFormat::TemporalToLocaleString( isolate, zoned_date_time, locales, options, method_name); #else // V8_INTL_SUPPORT return TemporalZonedDateTimeToString( isolate, zoned_date_time, Precision::kAuto, ShowCalendar::kAuto, ShowTimeZone::kAuto, ShowOffset::kAuto, method_name); #endif // V8_INTL_SUPPORT } // #sec-temporal.zoneddatetime.prototype.tostring MaybeHandle<String> JSTemporalZonedDateTime::ToString( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> options_obj) { const char* method_name = "Temporal.ZonedDateTime.prototype.toString"; // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), String); // 4. Let precision be ? ToSecondsStringPrecision(options). StringPrecision precision; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, precision, ToSecondsStringPrecision(isolate, options, method_name), Handle<String>()); // 5. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). RoundingMode rounding_mode; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_mode, ToTemporalRoundingMode(isolate, options, RoundingMode::kTrunc, method_name), Handle<String>()); // 6. Let showCalendar be ? ToShowCalendarOption(options). ShowCalendar show_calendar; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, show_calendar, ToShowCalendarOption(isolate, options, method_name), Handle<String>()); // 7. Let showTimeZone be ? ToShowTimeZoneNameOption(options). ShowTimeZone show_time_zone; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, show_time_zone, ToShowTimeZoneNameOption(isolate, options, method_name), Handle<String>()); // 8. Let showOffset be ? ToShowOffsetOption(options). ShowOffset show_offset; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, show_offset, ToShowOffsetOption(isolate, options, method_name), Handle<String>()); // 9. Return ? TemporalZonedDateTimeToString(zonedDateTime, // precision.[[Precision]], showCalendar, showTimeZone, showOffset, // precision.[[Increment]], precision.[[Unit]], roundingMode). return TemporalZonedDateTimeToString( isolate, zoned_date_time, precision.precision, show_calendar, show_time_zone, show_offset, precision.increment, precision.unit, rounding_mode, method_name); } // #sec-temporal.now.zoneddatetime MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::Now( Isolate* isolate, Handle<Object> calendar_like, Handle<Object> temporal_time_zone_like) { const char* method_name = "Temporal.Now.zonedDateTime"; // 1. Return ? SystemZonedDateTime(temporalTimeZoneLike, calendarLike). return SystemZonedDateTime(isolate, temporal_time_zone_like, calendar_like, method_name); } // #sec-temporal.now.zoneddatetimeiso MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::NowISO( Isolate* isolate, Handle<Object> temporal_time_zone_like) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.Now.zonedDateTimeISO"; // 1. Let calendar be ! GetISO8601Calendar(). Handle<JSReceiver> calendar = temporal::GetISO8601Calendar(isolate); // 2. Return ? SystemZonedDateTime(temporalTimeZoneLike, calendar). return SystemZonedDateTime(isolate, temporal_time_zone_like, calendar, method_name); } // #sec-temporal.zoneddatetime.prototype.round MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::Round( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> round_to_obj) { const char* method_name = "Temporal.ZonedDateTime.prototype.round"; Factory* factory = isolate->factory(); // 1. Let temporalTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. If roundTo is undefined, then if (IsUndefined(*round_to_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalZonedDateTime); } Handle<JSReceiver> round_to; // 4. If Type(roundTo) is String, then if (IsString(*round_to_obj)) { // a. Let paramString be roundTo. Handle<String> param_string = Handle<String>::cast(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()); // 5. Else } else { // a. Set roundTo to ? GetOptionsObject(roundTo). ASSIGN_RETURN_ON_EXCEPTION( isolate, round_to, GetOptionsObject(isolate, round_to_obj, method_name), JSTemporalZonedDateTime); } // 6. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, // required, « "day" »). Unit smallest_unit; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, smallest_unit, GetTemporalUnit(isolate, round_to, "smallestUnit", UnitGroup::kTime, Unit::kDay, true, method_name, Unit::kDay), Handle<JSTemporalZonedDateTime>()); // 7. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand"). RoundingMode rounding_mode; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_mode, ToTemporalRoundingMode(isolate, round_to, RoundingMode::kHalfExpand, method_name), Handle<JSTemporalZonedDateTime>()); // 8. Let roundingIncrement be ? ToTemporalDateTimeRoundingIncrement(roundTo, // smallestUnit). double rounding_increment; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_increment, ToTemporalDateTimeRoundingIncrement(isolate, round_to, smallest_unit), Handle<JSTemporalZonedDateTime>()); // 9. Let timeZone be zonedDateTime.[[TimeZone]]. Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); // 10. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); // 11. Let calendar be zonedDateTime.[[Calendar]]. Handle<JSReceiver> calendar(zoned_date_time->calendar(), isolate); // 12. Let temporalDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, // instant, calendar). Handle<JSTemporalPlainDateTime> temporal_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor(isolate, time_zone, instant, calendar, method_name), JSTemporalZonedDateTime); // 13. Let isoCalendar be ! GetISO8601Calendar(). Handle<JSReceiver> iso_calendar = temporal::GetISO8601Calendar(isolate); // 14. Let dtStart be ? CreateTemporalDateTime(temporalDateTime.[[ISOYear]], // temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], 0, 0, 0, 0, 0, // 0, isoCalendar). Handle<JSTemporalPlainDateTime> dt_start; ASSIGN_RETURN_ON_EXCEPTION( isolate, dt_start, temporal::CreateTemporalDateTime( isolate, {{temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day()}, {0, 0, 0, 0, 0, 0}}, iso_calendar), JSTemporalZonedDateTime); // 15. Let instantStart be ? BuiltinTimeZoneGetInstantFor(timeZone, dtStart, // "compatible"). Handle<JSTemporalInstant> instant_start; ASSIGN_RETURN_ON_EXCEPTION( isolate, instant_start, BuiltinTimeZoneGetInstantFor(isolate, time_zone, dt_start, Disambiguation::kCompatible, method_name), JSTemporalZonedDateTime); // 16. Let startNs be instantStart.[[Nanoseconds]]. Handle<BigInt> start_ns(instant_start->nanoseconds(), isolate); // 17. Let endNs be ? AddZonedDateTime(startNs, timeZone, calendar, 0, 0, 0, // 1, 0, 0, 0, 0, 0, 0). Handle<BigInt> end_ns; ASSIGN_RETURN_ON_EXCEPTION( isolate, end_ns, AddZonedDateTime(isolate, start_ns, time_zone, calendar, {0, 0, 0, {1, 0, 0, 0, 0, 0, 0}}, method_name), JSTemporalZonedDateTime); // 18. Let dayLengthNs be ℝ(endNs - startNs). Handle<BigInt> day_length_ns = BigInt::Subtract(isolate, end_ns, start_ns).ToHandleChecked(); // 19. If dayLengthNs ≤ 0, then if (day_length_ns->IsNegative() || !day_length_ns->ToBoolean()) { // a. Throw a RangeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalZonedDateTime); } // 20. Let roundResult be ! RoundISODateTime(temporalDateTime.[[ISOYear]], // temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], // temporalDateTime.[[ISOHour]], temporalDateTime.[[ISOMinute]], // temporalDateTime.[[ISOSecond]], temporalDateTime.[[ISOMillisecond]], // temporalDateTime.[[ISOMicrosecond]], temporalDateTime.[[ISONanosecond]], // roundingIncrement, smallestUnit, roundingMode, dayLengthNs). DateTimeRecord round_result = RoundISODateTime( isolate, {{temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day()}, {temporal_date_time->iso_hour(), temporal_date_time->iso_minute(), temporal_date_time->iso_second(), temporal_date_time->iso_millisecond(), temporal_date_time->iso_microsecond(), temporal_date_time->iso_nanosecond()}}, rounding_increment, smallest_unit, rounding_mode, Object::Number(*BigInt::ToNumber(isolate, day_length_ns))); // 21. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant). int64_t offset_nanoseconds; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_nanoseconds, GetOffsetNanosecondsFor(isolate, time_zone, instant, method_name), Handle<JSTemporalZonedDateTime>()); // 22. Let epochNanoseconds be ? // InterpretISODateTimeOffset(roundResult.[[Year]], roundResult.[[Month]], // roundResult.[[Day]], roundResult.[[Hour]], roundResult.[[Minute]], // roundResult.[[Second]], roundResult.[[Millisecond]], // roundResult.[[Microsecond]], roundResult.[[Nanosecond]], option, // offsetNanoseconds, timeZone, "compatible", "prefer", match exactly). Handle<BigInt> epoch_nanoseconds; ASSIGN_RETURN_ON_EXCEPTION( isolate, epoch_nanoseconds, InterpretISODateTimeOffset( isolate, round_result, OffsetBehaviour::kOption, offset_nanoseconds, time_zone, Disambiguation::kCompatible, Offset::kPrefer, MatchBehaviour::kMatchExactly, method_name), JSTemporalZonedDateTime); // 23. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, // calendar). return CreateTemporalZonedDateTime(isolate, epoch_nanoseconds, time_zone, calendar) .ToHandleChecked(); } namespace { // #sec-temporal-adddurationtoOrsubtractdurationfromzoneddatetime MaybeHandle<JSTemporalZonedDateTime> AddDurationToOrSubtractDurationFromZonedDateTime( Isolate* isolate, Arithmetic operation, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> temporal_duration_like, Handle<Object> options_obj, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1. double sign = operation == Arithmetic::kSubtract ? -1.0 : 1.0; // 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike). DurationRecord duration; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, duration, temporal::ToTemporalDurationRecord(isolate, temporal_duration_like, method_name), Handle<JSTemporalZonedDateTime>()); TimeDurationRecord& time_duration = duration.time_duration; // 3. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), JSTemporalZonedDateTime); // 4. Let timeZone be zonedDateTime.[[TimeZone]]. Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); // 5. Let calendar be zonedDateTime.[[Calendar]]. Handle<JSReceiver> calendar(zoned_date_time->calendar(), isolate); // 6. Let epochNanoseconds be ? // AddZonedDateTime(zonedDateTime.[[Nanoseconds]], timeZone, calendar, // sign x duration.[[Years]], sign x duration.[[Months]], sign x // duration.[[Weeks]], sign x duration.[[Days]], sign x duration.[[Hours]], // sign x duration.[[Minutes]], sign x duration.[[Seconds]], sign x // duration.[[Milliseconds]], sign x duration.[[Microseconds]], sign x // duration.[[Nanoseconds]], options). Handle<BigInt> nanoseconds(zoned_date_time->nanoseconds(), isolate); duration.years *= sign; duration.months *= sign; duration.weeks *= sign; time_duration.days *= sign; time_duration.hours *= sign; time_duration.minutes *= sign; time_duration.seconds *= sign; time_duration.milliseconds *= sign; time_duration.microseconds *= sign; time_duration.nanoseconds *= sign; Handle<BigInt> epoch_nanoseconds; ASSIGN_RETURN_ON_EXCEPTION( isolate, epoch_nanoseconds, AddZonedDateTime(isolate, nanoseconds, time_zone, calendar, duration, options, method_name), JSTemporalZonedDateTime); // 7. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, // calendar). return CreateTemporalZonedDateTime(isolate, epoch_nanoseconds, time_zone, calendar); } } // namespace // #sec-temporal.zoneddatetime.prototype.add MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::Add( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> temporal_duration_like, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return AddDurationToOrSubtractDurationFromZonedDateTime( isolate, Arithmetic::kAdd, zoned_date_time, temporal_duration_like, options, "Temporal.ZonedDateTime.prototype.add"); } // #sec-temporal.zoneddatetime.prototype.subtract MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::Subtract( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> temporal_duration_like, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return AddDurationToOrSubtractDurationFromZonedDateTime( isolate, Arithmetic::kSubtract, zoned_date_time, temporal_duration_like, options, "Temporal.ZonedDateTime.prototype.subtract"); } namespace { // #sec-temporal-differencetemporalzoneddatetime MaybeHandle<JSTemporalDuration> DifferenceTemporalZonedDateTime( Isolate* isolate, TimePreposition operation, Handle<JSTemporalZonedDateTime> zoned_date_time, Handle<Object> other_obj, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If operation is since, let sign be -1. Otherwise, let sign be 1. double sign = operation == TimePreposition::kSince ? -1 : 1; // 2. Set other to ? ToTemporalZonedDateTime(other). Handle<JSTemporalZonedDateTime> other; ASSIGN_RETURN_ON_EXCEPTION( isolate, other, ToTemporalZonedDateTime(isolate, other_obj, method_name), JSTemporalDuration); // 3. If ? CalendarEquals(zonedDateTime.[[Calendar]], other.[[Calendar]]) is // false, then bool calendar_equals; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, calendar_equals, CalendarEqualsBool(isolate, handle(zoned_date_time->calendar(), isolate), handle(other->calendar(), isolate)), Handle<JSTemporalDuration>()); if (!calendar_equals) { // a. Throw a RangeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalDuration); } // 4. Let settings be ? GetDifferenceSettings(operation, options, datetime, « // », "nanosecond", "hour"). DifferenceSettings settings; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, settings, GetDifferenceSettings(isolate, operation, options, UnitGroup::kDateTime, DisallowedUnitsInDifferenceSettings::kNone, Unit::kNanosecond, Unit::kHour, method_name), Handle<JSTemporalDuration>()); // 5. If settings.[[LargestUnit]] is not one of "year", "month", "week", or // "day", then if (settings.largest_unit != Unit::kYear && settings.largest_unit != Unit::kMonth && settings.largest_unit != Unit::kWeek && settings.largest_unit != Unit::kDay) { // 1. Let result be ! DifferenceInstant(zonedDateTime.[[Nanoseconds]], // other.[[Nanoseconds]], settings.[[RoundingIncrement]], // settings.[[SmallestUnit]], settings.[[LargestUnit]], // settings.[[RoundingMode]]). TimeDurationRecord balance_result = DifferenceInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate), handle(other->nanoseconds(), isolate), settings.rounding_increment, settings.smallest_unit, settings.largest_unit, settings.rounding_mode, method_name); // d. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × // balanceResult.[[Hours]], sign × balanceResult.[[Minutes]], sign × // balanceResult.[[Seconds]], sign × balanceResult.[[Milliseconds]], sign × // balanceResult.[[Microseconds]], sign × balanceResult.[[Nanoseconds]]). return CreateTemporalDuration( isolate, {0, 0, 0, {0, sign * balance_result.hours, sign * balance_result.minutes, sign * balance_result.seconds, sign * balance_result.milliseconds, sign * balance_result.microseconds, sign * balance_result.nanoseconds}}) .ToHandleChecked(); } // 6. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is // false, then bool equals; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, equals, TimeZoneEquals(isolate, handle(zoned_date_time->time_zone(), isolate), handle(other->time_zone(), isolate)), Handle<JSTemporalDuration>()); if (!equals) { // a. Throw a RangeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalDuration); } // 7. Let untilOptions be ? MergeLargestUnitOption(settings.[[Options]], // settings.[[LargestUnit]]). Handle<JSReceiver> until_options; ASSIGN_RETURN_ON_EXCEPTION( isolate, until_options, MergeLargestUnitOption(isolate, settings.options, settings.largest_unit), JSTemporalDuration); // 8. Let difference be ? // DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], // other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], // zonedDateTime.[[Calendar]], settings.[[LargestUnit]], untilOptions). DurationRecord difference; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, difference, DifferenceZonedDateTime( isolate, handle(zoned_date_time->nanoseconds(), isolate), handle(other->nanoseconds(), isolate), handle(zoned_date_time->time_zone(), isolate), handle(zoned_date_time->calendar(), isolate), settings.largest_unit, until_options, method_name), Handle<JSTemporalDuration>()); // 9. Let roundResult be (? RoundDuration(difference.[[Years]], // difference.[[Months]], difference.[[Weeks]], difference.[[Days]], // difference.[[Hours]], difference.[[Minutes]], difference.[[Seconds]], // difference.[[Milliseconds]], difference.[[Microseconds]], // difference.[[Nanoseconds]], settings.[[RoundingIncrement]], // settings.[[SmallestUnit]], settings.[[RoundingMode]], // zonedDateTime)).[[DurationRecord]]. DurationRecordWithRemainder round_result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, round_result, RoundDuration(isolate, difference, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, zoned_date_time, method_name), Handle<JSTemporalDuration>()); // 10. Let result be ? AdjustRoundedDurationDays(roundResult.[[Years]], // roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], // roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], // roundResult.[[Milliseconds]], roundResult.[[Microseconds]], // roundResult.[[Nanoseconds]], settings.[[RoundingIncrement]], // settings.[[SmallestUnit]], settings.[[RoundingMode]], zonedDateTime). DurationRecord result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, AdjustRoundedDurationDays(isolate, round_result.record, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, zoned_date_time, method_name), Handle<JSTemporalDuration>()); // 11. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × // result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], sign × // result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], // sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × // result.[[Nanoseconds]]). return CreateTemporalDuration(isolate, {sign * result.years, sign * result.months, sign * result.weeks, {sign * result.time_duration.days, sign * result.time_duration.hours, sign * result.time_duration.minutes, sign * result.time_duration.seconds, sign * result.time_duration.milliseconds, sign * result.time_duration.microseconds, sign * result.time_duration.nanoseconds}}) .ToHandleChecked(); } } // namespace // #sec-temporal.zoneddatetime.prototype.until MaybeHandle<JSTemporalDuration> JSTemporalZonedDateTime::Until( Isolate* isolate, Handle<JSTemporalZonedDateTime> handle, Handle<Object> other, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return DifferenceTemporalZonedDateTime( isolate, TimePreposition::kUntil, handle, other, options, "Temporal.ZonedDateTime.prototype.until"); } // #sec-temporal.zoneddatetime.prototype.since MaybeHandle<JSTemporalDuration> JSTemporalZonedDateTime::Since( Isolate* isolate, Handle<JSTemporalZonedDateTime> handle, Handle<Object> other, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return DifferenceTemporalZonedDateTime( isolate, TimePreposition::kSince, handle, other, options, "Temporal.ZonedDateTime.prototype.since"); } // #sec-temporal.zoneddatetime.prototype.getisofields MaybeHandle<JSReceiver> JSTemporalZonedDateTime::GetISOFields( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.ZonedDateTime.prototype.getISOFields"; Factory* factory = isolate->factory(); // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Let fields be ! OrdinaryObjectCreate(%Object.prototype%). Handle<JSObject> fields = isolate->factory()->NewJSObject(isolate->object_function()); // 4. Let timeZone be zonedDateTime.[[TimeZone]]. Handle<JSReceiver> time_zone = Handle<JSReceiver>(zoned_date_time->time_zone(), isolate); // 5. Let instant be ? CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]). Handle<JSTemporalInstant> instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, instant, temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)), JSReceiver); // 6. Let calendar be zonedDateTime.[[Calendar]]. Handle<JSReceiver> calendar = Handle<JSReceiver>(zoned_date_time->calendar(), isolate); // 7. Let dateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, // instant, calendar). Handle<JSTemporalPlainDateTime> date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor(isolate, time_zone, instant, calendar, method_name), JSReceiver); // 8. Let offset be ? BuiltinTimeZoneGetOffsetStringFor(timeZone, instant). Handle<String> offset; ASSIGN_RETURN_ON_EXCEPTION(isolate, offset, BuiltinTimeZoneGetOffsetStringFor( isolate, time_zone, instant, method_name), JSReceiver); #define DEFINE_STRING_FIELD(obj, str, field) \ CHECK(JSReceiver::CreateDataProperty(isolate, obj, factory->str##_string(), \ field, Just(kThrowOnError)) \ .FromJust()); // 9. Perform ! CreateDataPropertyOrThrow(fields, "calendar", calendar). // 10. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", // 𝔽(dateTime.[[ISODay]])). // 11. Perform ! CreateDataPropertyOrThrow(fields, "isoHour", // 𝔽(temporalTime.[[ISOHour]])). // 12. Perform ! CreateDataPropertyOrThrow(fields, "isoMicrosecond", // 𝔽(temporalTime.[[ISOMicrosecond]])). // 13. Perform ! CreateDataPropertyOrThrow(fields, "isoMillisecond", // 𝔽(temporalTime.[[ISOMillisecond]])). // 14. Perform ! CreateDataPropertyOrThrow(fields, "isoMinute", // 𝔽(temporalTime.[[ISOMinute]])). // 15. Perform ! CreateDataPropertyOrThrow(fields, "isoMonth", // 𝔽(temporalTime.[[ISOMonth]])). // 16. Perform ! CreateDataPropertyOrThrow(fields, "isoNanosecond", // 𝔽(temporalTime.[[ISONanosecond]])). // 17. Perform ! CreateDataPropertyOrThrow(fields, "isoSecond", // 𝔽(temporalTime.[[ISOSecond]])). // 18. Perform ! CreateDataPropertyOrThrow(fields, "isoYear", // 𝔽(temporalTime.[[ISOYear]])). // 19. Perform ! CreateDataPropertyOrThrow(fields, "offset", offset). // 20. Perform ! CreateDataPropertyOrThrow(fields, "timeZone", timeZone). DEFINE_STRING_FIELD(fields, calendar, calendar) DEFINE_INT_FIELD(fields, isoDay, iso_day, date_time) DEFINE_INT_FIELD(fields, isoHour, iso_hour, date_time) DEFINE_INT_FIELD(fields, isoMicrosecond, iso_microsecond, date_time) DEFINE_INT_FIELD(fields, isoMillisecond, iso_millisecond, date_time) DEFINE_INT_FIELD(fields, isoMinute, iso_minute, date_time) DEFINE_INT_FIELD(fields, isoMonth, iso_month, date_time) DEFINE_INT_FIELD(fields, isoNanosecond, iso_nanosecond, date_time) DEFINE_INT_FIELD(fields, isoSecond, iso_second, date_time) DEFINE_INT_FIELD(fields, isoYear, iso_year, date_time) DEFINE_STRING_FIELD(fields, offset, offset) DEFINE_STRING_FIELD(fields, timeZone, time_zone) // 21. Return fields. return fields; } // #sec-temporal.now.instant MaybeHandle<JSTemporalInstant> JSTemporalInstant::Now(Isolate* isolate) { TEMPORAL_ENTER_FUNC(); return SystemInstant(isolate); } // #sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds MaybeHandle<Object> JSTemporalZonedDateTime::OffsetNanoseconds( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time) { TEMPORAL_ENTER_FUNC(); // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Let timeZone be zonedDateTime.[[TimeZone]]. Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); // 4. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); // 5. Return 𝔽(? GetOffsetNanosecondsFor(timeZone, instant)). int64_t result; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, result, GetOffsetNanosecondsFor( isolate, time_zone, instant, "Temporal.ZonedDateTime.prototype.offsetNanoseconds"), Handle<Object>()); return isolate->factory()->NewNumberFromInt64(result); } // #sec-get-temporal.zoneddatetime.prototype.offset MaybeHandle<String> JSTemporalZonedDateTime::Offset( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time) { TEMPORAL_ENTER_FUNC(); // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); // 4. Return ? BuiltinTimeZoneGetOffsetStringFor(zonedDateTime.[[TimeZone]], // instant). return BuiltinTimeZoneGetOffsetStringFor( isolate, handle(zoned_date_time->time_zone(), isolate), instant, "Temporal.ZonedDateTime.prototype.offset"); } // #sec-temporal.zoneddatetime.prototype.startofday MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::StartOfDay( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.ZonedDateTime.prototype.startOfDay"; // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Let timeZone be zonedDateTime.[[TimeZone]]. Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); // 4. Let calendar be zonedDateTime.[[Calendar]]. Handle<JSReceiver> calendar(zoned_date_time->calendar(), isolate); // 5. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); // 6. Let temporalDateTime be ? // BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar). Handle<JSTemporalPlainDateTime> temporal_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor(isolate, time_zone, instant, calendar, method_name), JSTemporalZonedDateTime); // 7. Let startDateTime be ? // CreateTemporalDateTime(temporalDateTime.[[ISOYear]], // temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], 0, 0, 0, 0, 0, // 0, calendar). Handle<JSTemporalPlainDateTime> start_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, start_date_time, temporal::CreateTemporalDateTime( isolate, {{temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day()}, {0, 0, 0, 0, 0, 0}}, calendar), JSTemporalZonedDateTime); // 8. Let startInstant be ? BuiltinTimeZoneGetInstantFor(timeZone, // startDateTime, "compatible"). Handle<JSTemporalInstant> start_instant; ASSIGN_RETURN_ON_EXCEPTION( isolate, start_instant, BuiltinTimeZoneGetInstantFor(isolate, time_zone, start_date_time, Disambiguation::kCompatible, method_name), JSTemporalZonedDateTime); // 9. Return ? CreateTemporalZonedDateTime(startInstant.[[Nanoseconds]], // timeZone, calendar). return CreateTemporalZonedDateTime( isolate, handle(start_instant->nanoseconds(), isolate), time_zone, calendar); } // #sec-temporal.zoneddatetime.prototype.toinstant MaybeHandle<JSTemporalInstant> JSTemporalZonedDateTime::ToInstant( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time) { TEMPORAL_ENTER_FUNC(); // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Return ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]). return temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); } namespace { // Function implment shared steps of toplaindate, toplaintime, toplaindatetime MaybeHandle<JSTemporalPlainDateTime> ZonedDateTimeToPlainDateTime( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, // [[InitializedTemporalZonedDateTime]]). // 3. Let timeZone be zonedDateTime.[[TimeZone]]. Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); // 4. Let instant be ! CreateTemporalInstant(zonedDateTime.[[Nanoseconds]]). Handle<JSTemporalInstant> instant = temporal::CreateTemporalInstant( isolate, handle(zoned_date_time->nanoseconds(), isolate)) .ToHandleChecked(); // 5. 5. Return ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, // zonedDateTime.[[Calendar]]). return temporal::BuiltinTimeZoneGetPlainDateTimeFor( isolate, time_zone, instant, handle(zoned_date_time->calendar(), isolate), method_name); } } // namespace // #sec-temporal.zoneddatetime.prototype.toplaindate MaybeHandle<JSTemporalPlainDate> JSTemporalZonedDateTime::ToPlainDate( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time) { // Step 1-6 are the same as toplaindatetime Handle<JSTemporalPlainDateTime> temporal_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_time, ZonedDateTimeToPlainDateTime( isolate, zoned_date_time, "Temporal.ZonedDateTime.prototype.toPlainDate"), JSTemporalPlainDate); // 7. Return ? CreateTemporalDate(temporalDateTime.[[ISOYear]], // temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], calendar). return CreateTemporalDate( isolate, {temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day()}, handle(zoned_date_time->calendar(), isolate)); } // #sec-temporal.zoneddatetime.prototype.toplaintime MaybeHandle<JSTemporalPlainTime> JSTemporalZonedDateTime::ToPlainTime( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time) { // Step 1-6 are the same as toplaindatetime Handle<JSTemporalPlainDateTime> temporal_date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_date_time, ZonedDateTimeToPlainDateTime( isolate, zoned_date_time, "Temporal.ZonedDateTime.prototype.toPlainTime"), JSTemporalPlainTime); // 7. Return ? CreateTemporalTime(temporalDateTime.[[ISOHour]], // temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], // temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], // temporalDateTime.[[ISONanosecond]]). return CreateTemporalTime( isolate, {temporal_date_time->iso_hour(), temporal_date_time->iso_minute(), temporal_date_time->iso_second(), temporal_date_time->iso_millisecond(), temporal_date_time->iso_microsecond(), temporal_date_time->iso_nanosecond()}); } // #sec-temporal.zoneddatetime.prototype.toplaindatetime MaybeHandle<JSTemporalPlainDateTime> JSTemporalZonedDateTime::ToPlainDateTime( Isolate* isolate, Handle<JSTemporalZonedDateTime> zoned_date_time) { return ZonedDateTimeToPlainDateTime( isolate, zoned_date_time, "Temporal.ZonedDateTime.prototype.toPlainDateTime"); } // #sec-temporal.instant MaybeHandle<JSTemporalInstant> JSTemporalInstant::Constructor( Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, Handle<Object> epoch_nanoseconds_obj) { TEMPORAL_ENTER_FUNC(); // 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")), JSTemporalInstant); } // 2. Let epochNanoseconds be ? ToBigInt(epochNanoseconds). Handle<BigInt> epoch_nanoseconds; ASSIGN_RETURN_ON_EXCEPTION(isolate, epoch_nanoseconds, BigInt::FromObject(isolate, epoch_nanoseconds_obj), JSTemporalInstant); // 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a // RangeError exception. if (!IsValidEpochNanoseconds(isolate, epoch_nanoseconds)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalInstant); } // 4. Return ? CreateTemporalInstant(epochNanoseconds, NewTarget). return temporal::CreateTemporalInstant(isolate, target, new_target, epoch_nanoseconds); } namespace { // The logic in Temporal.Instant.fromEpochSeconds and fromEpochMilliseconds, // are the same except a scaling factor, code all of them into the follow // function. MaybeHandle<JSTemporalInstant> ScaleNumberToNanosecondsVerifyAndMake( Isolate* isolate, Handle<BigInt> bigint, uint32_t scale) { TEMPORAL_ENTER_FUNC(); DCHECK(scale == 1 || scale == 1000 || scale == 1000000 || scale == 1000000000); // 2. Let epochNanoseconds be epochXseconds × scaleℤ. Handle<BigInt> epoch_nanoseconds; if (scale == 1) { epoch_nanoseconds = bigint; } else { ASSIGN_RETURN_ON_EXCEPTION( isolate, epoch_nanoseconds, BigInt::Multiply(isolate, BigInt::FromUint64(isolate, scale), bigint), JSTemporalInstant); } // 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a // RangeError exception. if (!IsValidEpochNanoseconds(isolate, epoch_nanoseconds)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), JSTemporalInstant); } return temporal::CreateTemporalInstant(isolate, epoch_nanoseconds); } MaybeHandle<JSTemporalInstant> ScaleNumberToNanosecondsVerifyAndMake( Isolate* isolate, Handle<Object> epoch_Xseconds, uint32_t scale) { TEMPORAL_ENTER_FUNC(); // 1. Set epochXseconds to ? ToNumber(epochXseconds). ASSIGN_RETURN_ON_EXCEPTION(isolate, epoch_Xseconds, Object::ToNumber(isolate, epoch_Xseconds), JSTemporalInstant); // 2. Set epochMilliseconds to ? NumberToBigInt(epochMilliseconds). Handle<BigInt> bigint; ASSIGN_RETURN_ON_EXCEPTION(isolate, bigint, BigInt::FromNumber(isolate, epoch_Xseconds), JSTemporalInstant); return ScaleNumberToNanosecondsVerifyAndMake(isolate, bigint, scale); } MaybeHandle<JSTemporalInstant> ScaleToNanosecondsVerifyAndMake( Isolate* isolate, Handle<Object> epoch_Xseconds, uint32_t scale) { TEMPORAL_ENTER_FUNC(); // 1. Set epochMicroseconds to ? ToBigInt(epochMicroseconds). Handle<BigInt> bigint; ASSIGN_RETURN_ON_EXCEPTION(isolate, bigint, BigInt::FromObject(isolate, epoch_Xseconds), JSTemporalInstant); return ScaleNumberToNanosecondsVerifyAndMake(isolate, bigint, scale); } } // namespace // #sec-temporal.instant.fromepochseconds MaybeHandle<JSTemporalInstant> JSTemporalInstant::FromEpochSeconds( Isolate* isolate, Handle<Object> epoch_seconds) { TEMPORAL_ENTER_FUNC(); return ScaleNumberToNanosecondsVerifyAndMake(isolate, epoch_seconds, 1000000000); } // #sec-temporal.instant.fromepochmilliseconds MaybeHandle<JSTemporalInstant> JSTemporalInstant::FromEpochMilliseconds( Isolate* isolate, Handle<Object> epoch_milliseconds) { TEMPORAL_ENTER_FUNC(); return ScaleNumberToNanosecondsVerifyAndMake(isolate, epoch_milliseconds, 1000000); } // #sec-temporal.instant.fromepochmicroseconds MaybeHandle<JSTemporalInstant> JSTemporalInstant::FromEpochMicroseconds( Isolate* isolate, Handle<Object> epoch_microseconds) { TEMPORAL_ENTER_FUNC(); return ScaleToNanosecondsVerifyAndMake(isolate, epoch_microseconds, 1000); } // #sec-temporal.instant.fromepochnanoeconds MaybeHandle<JSTemporalInstant> JSTemporalInstant::FromEpochNanoseconds( Isolate* isolate, Handle<Object> epoch_nanoseconds) { TEMPORAL_ENTER_FUNC(); return ScaleToNanosecondsVerifyAndMake(isolate, epoch_nanoseconds, 1); } // #sec-temporal.instant.compare MaybeHandle<Smi> JSTemporalInstant::Compare(Isolate* isolate, Handle<Object> one_obj, Handle<Object> two_obj) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.Instant.compare"; // 1. Set one to ? ToTemporalInstant(one). Handle<JSTemporalInstant> one; ASSIGN_RETURN_ON_EXCEPTION( isolate, one, ToTemporalInstant(isolate, one_obj, method_name), Smi); // 2. Set two to ? ToTemporalInstant(two). Handle<JSTemporalInstant> two; ASSIGN_RETURN_ON_EXCEPTION( isolate, two, ToTemporalInstant(isolate, two_obj, method_name), Smi); // 3. Return 𝔽(! CompareEpochNanoseconds(one.[[Nanoseconds]], // two.[[Nanoseconds]])). return CompareEpochNanoseconds(isolate, handle(one->nanoseconds(), isolate), handle(two->nanoseconds(), isolate)); } // #sec-temporal.instant.prototype.equals MaybeHandle<Oddball> JSTemporalInstant::Equals(Isolate* isolate, Handle<JSTemporalInstant> handle, Handle<Object> other_obj) { TEMPORAL_ENTER_FUNC(); // 1. Let instant be the this value. // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]). // 3. Set other to ? ToTemporalInstant(other). Handle<JSTemporalInstant> other; ASSIGN_RETURN_ON_EXCEPTION( isolate, other, ToTemporalInstant(isolate, other_obj, "Temporal.Instant.prototype.equals"), Oddball); // 4. If instant.[[Nanoseconds]] ≠ other.[[Nanoseconds]], return false. // 5. Return true. return isolate->factory()->ToBoolean( BigInt::EqualToBigInt(handle->nanoseconds(), other->nanoseconds())); } namespace { // #sec-temporal-totemporalroundingincrement Maybe<double> ToTemporalRoundingIncrement(Isolate* isolate, Handle<JSReceiver> normalized_options, double dividend, bool dividend_is_defined, bool inclusive) { double maximum; // 1. If dividend is undefined, then if (!dividend_is_defined) { // a. Let maximum be +∞. maximum = std::numeric_limits<double>::infinity(); // 2. Else if inclusive is true, then } else if (inclusive) { // a. Let maximum be 𝔽(dividend). maximum = dividend; // 3. Else if dividend is more than 1, then } else if (dividend > 1) { // a. Let maximum be 𝔽(dividend-1). maximum = dividend - 1; // 4. Else, } else { // a. Let maximum be 1. maximum = 1; } // 5. Let increment be ? GetOption(normalizedOptions, "roundingIncrement", « // Number », empty, 1). double increment; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, increment, GetNumberOptionAsDouble(isolate, normalized_options, isolate->factory()->roundingIncrement_string(), 1), Nothing<double>()); // 6. If increment < 1 or increment > maximum, throw a RangeError exception. if (increment < 1 || increment > maximum) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<double>()); } // 7. Set increment to floor(ℝ(increment)). increment = std::floor(increment); // 8. If dividend is not undefined and dividend modulo increment is not zero, // then if ((dividend_is_defined) && (std::fmod(dividend, increment) != 0)) { // a. Throw a RangeError exception. THROW_NEW_ERROR_RETURN_VALUE( isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Nothing<double>()); } // 9. Return increment. return Just(increment); } // #sec-temporal-roundtemporalinstant Handle<BigInt> RoundTemporalInstant(Isolate* isolate, Handle<BigInt> ns, double increment, Unit unit, RoundingMode rounding_mode) { TEMPORAL_ENTER_FUNC(); // 1. Assert: Type(ns) is BigInt. double increment_ns; switch (unit) { // 2. If unit is "hour", then case Unit::kHour: // a. Let incrementNs be increment × 3.6 × 10^12. increment_ns = increment * 3.6e12; break; // 3. Else if unit is "minute", then case Unit::kMinute: // a. Let incrementNs be increment × 6 × 10^10. increment_ns = increment * 6e10; break; // 4. Else if unit is "second", then case Unit::kSecond: // a. Let incrementNs be increment × 10^9. increment_ns = increment * 1e9; break; // 5. Else if unit is "millisecond", then case Unit::kMillisecond: // a. Let incrementNs be increment × 10^6. increment_ns = increment * 1e6; break; // 6. Else if unit is "microsecond", then case Unit::kMicrosecond: // a. Let incrementNs be increment × 10^3. increment_ns = increment * 1e3; break; // 7. Else, // a. Assert: unit is "nanosecond". case Unit::kNanosecond: // b. Let incrementNs be increment. increment_ns = increment; break; default: UNREACHABLE(); } // 8. Return ! RoundNumberToIncrementAsIfPositive(ℝ(ns), incrementNs, // roundingMode). return RoundNumberToIncrementAsIfPositive(isolate, ns, increment_ns, rounding_mode); } } // namespace // #sec-temporal.instant.prototype.round MaybeHandle<JSTemporalInstant> JSTemporalInstant::Round( Isolate* isolate, Handle<JSTemporalInstant> handle, Handle<Object> round_to_obj) { TEMPORAL_ENTER_FUNC(); 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_INVALID_ARG_TYPE_ERROR(), JSTemporalInstant); } Handle<JSReceiver> round_to; // 4. If Type(roundTo) is String, then if (IsString(*round_to_obj)) { // a. Let paramString be roundTo. Handle<String> param_string = Handle<String>::cast(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), JSTemporalInstant); } // 6. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, // required). Unit smallest_unit; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, smallest_unit, GetTemporalUnit(isolate, round_to, "smallestUnit", UnitGroup::kTime, Unit::kNotPresent, true, method_name), Handle<JSTemporalInstant>()); // 7. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand"). RoundingMode rounding_mode; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_mode, ToTemporalRoundingMode(isolate, round_to, RoundingMode::kHalfExpand, method_name), Handle<JSTemporalInstant>()); double maximum; switch (smallest_unit) { // 8. If smallestUnit is "hour", then case Unit::kHour: // a. Let maximum be 24. maximum = 24; break; // 9. Else if smallestUnit is "minute", then case Unit::kMinute: // a. Let maximum be 1440. maximum = 1440; break; // 10. Else if smallestUnit is "second", then case Unit::kSecond: // a. Let maximum be 86400. maximum = 86400; break; // 11. Else if smallestUnit is "millisecond", then case Unit::kMillisecond: // a. Let maximum be 8.64 × 10^7. maximum = 8.64e7; break; // 12. Else if smallestUnit is "microsecond", then case Unit::kMicrosecond: // a. Let maximum be 8.64 × 10^10. maximum = 8.64e10; break; // 13. Else, case Unit::kNanosecond: // b. Let maximum be nsPerDay. maximum = kNsPerDay; break; // a. Assert: smallestUnit is "nanosecond". default: UNREACHABLE(); } // 14. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo, // maximum, true). double rounding_increment; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_increment, ToTemporalRoundingIncrement(isolate, round_to, maximum, true, true), Handle<JSTemporalInstant>()); // 15. Let roundedNs be ! RoundTemporalInstant(instant.[[Nanoseconds]], // roundingIncrement, smallestUnit, roundingMode). Handle<BigInt> rounded_ns = RoundTemporalInstant( isolate, Handle<BigInt>(handle->nanoseconds(), isolate), rounding_increment, smallest_unit, rounding_mode); // 16. Return ! CreateTemporalInstant(roundedNs). return temporal::CreateTemporalInstant(isolate, rounded_ns).ToHandleChecked(); } // #sec-temporal.instant.from MaybeHandle<JSTemporalInstant> JSTemporalInstant::From(Isolate* isolate, Handle<Object> item) { TEMPORAL_ENTER_FUNC(); // 1. If Type(item) is Object and item has an [[InitializedTemporalInstant]] // internal slot, then if (IsJSTemporalInstant(*item)) { // a. Return ? CreateTemporalInstant(item.[[Nanoseconds]]). return temporal::CreateTemporalInstant( isolate, handle(JSTemporalInstant::cast(*item)->nanoseconds(), isolate)); } // 2. Return ? ToTemporalInstant(item). return ToTemporalInstant(isolate, item, "Temporal.Instant.from"); } // #sec-temporal.instant.prototype.tozoneddatetime MaybeHandle<JSTemporalZonedDateTime> JSTemporalInstant::ToZonedDateTime( Isolate* isolate, Handle<JSTemporalInstant> handle, Handle<Object> item_obj) { TEMPORAL_ENTER_FUNC(); const char* method_name = "Temporal.Instant.prototype.toZonedDateTime"; Factory* factory = isolate->factory(); // 1. Let instant be the this value. // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]). // 3. If Type(item) is not Object, then if (!IsJSReceiver(*item_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalZonedDateTime); } Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); // 4. Let calendarLike be ? Get(item, "calendar"). Handle<Object> calendar_like; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar_like, JSReceiver::GetProperty(isolate, item, factory->calendar_string()), JSTemporalZonedDateTime); // 5. If calendarLike is undefined, then if (IsUndefined(*calendar_like)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalZonedDateTime); } // 6. Let calendar be ? ToTemporalCalendar(calendarLike). Handle<JSReceiver> calendar; ASSIGN_RETURN_ON_EXCEPTION( isolate, calendar, temporal::ToTemporalCalendar(isolate, calendar_like, method_name), JSTemporalZonedDateTime); // 7. Let temporalTimeZoneLike be ? Get(item, "timeZone"). Handle<Object> temporal_time_zone_like; ASSIGN_RETURN_ON_EXCEPTION( isolate, temporal_time_zone_like, JSReceiver::GetProperty(isolate, item, factory->timeZone_string()), JSTemporalZonedDateTime); // 8. If temporalTimeZoneLike is undefined, then if (IsUndefined(*calendar_like)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), JSTemporalZonedDateTime); } // 9. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike). Handle<JSReceiver> time_zone; ASSIGN_RETURN_ON_EXCEPTION(isolate, time_zone, temporal::ToTemporalTimeZone( isolate, temporal_time_zone_like, method_name), JSTemporalZonedDateTime); // 10. Return ? CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, // calendar). return CreateTemporalZonedDateTime( isolate, Handle<BigInt>(handle->nanoseconds(), isolate), time_zone, calendar); } namespace { // #sec-temporal-temporalinstanttostring MaybeHandle<String> TemporalInstantToString(Isolate* isolate, Handle<JSTemporalInstant> instant, Handle<Object> time_zone_obj, Precision precision, const char* method_name) { IncrementalStringBuilder builder(isolate); // 1. Assert: Type(instant) is Object. // 2. Assert: instant has an [[InitializedTemporalInstant]] internal slot. // 3. Let outputTimeZone be timeZone. Handle<JSReceiver> output_time_zone; // 4. If outputTimeZone is undefined, then if (IsUndefined(*time_zone_obj)) { // a. Set outputTimeZone to ! CreateTemporalTimeZone("UTC"). output_time_zone = CreateTemporalTimeZoneUTC(isolate); } else { DCHECK(IsJSReceiver(*time_zone_obj)); output_time_zone = Handle<JSReceiver>::cast(time_zone_obj); } // 5. Let isoCalendar be ! GetISO8601Calendar(). Handle<JSTemporalCalendar> iso_calendar = temporal::GetISO8601Calendar(isolate); // 6. Let dateTime be ? // BuiltinTimeZoneGetPlainDateTimeFor(outputTimeZone, instant, // isoCalendar). Handle<JSTemporalPlainDateTime> date_time; ASSIGN_RETURN_ON_EXCEPTION( isolate, date_time, temporal::BuiltinTimeZoneGetPlainDateTimeFor( isolate, output_time_zone, instant, iso_calendar, method_name), String); // 7. Let dateTimeString be ? TemporalDateTimeToString(dateTime.[[ISOYear]], // dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], // dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], // dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], // dateTime.[[ISONanosecond]], undefined, precision, "never"). Handle<String> date_time_string; ASSIGN_RETURN_ON_EXCEPTION( isolate, date_time_string, TemporalDateTimeToString( isolate, {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()}}, iso_calendar, // Unimportant due to ShowCalendar::kNever precision, ShowCalendar::kNever), String); builder.AppendString(date_time_string); // 8. If timeZone is undefined, then if (IsUndefined(*time_zone_obj)) { // a. Let timeZoneString be "Z". builder.AppendCharacter('Z'); } else { // 9. Else, DCHECK(IsJSReceiver(*time_zone_obj)); Handle<JSReceiver> time_zone = Handle<JSReceiver>::cast(time_zone_obj); // a. Let offsetNs be ? GetOffsetNanosecondsFor(timeZone, instant). int64_t offset_ns; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, offset_ns, GetOffsetNanosecondsFor(isolate, time_zone, instant, method_name), Handle<String>()); // b. Let timeZoneString be ! FormatISOTimeZoneOffsetString(offsetNs). Handle<String> time_zone_string = FormatISOTimeZoneOffsetString(isolate, offset_ns); builder.AppendString(time_zone_string); } // 10. Return the string-concatenation of dateTimeString and timeZoneString. return builder.Finish(); } } // namespace // #sec-temporal.instant.prototype.tojson MaybeHandle<String> JSTemporalInstant::ToJSON( Isolate* isolate, Handle<JSTemporalInstant> instant) { TEMPORAL_ENTER_FUNC(); // 1. Let instant be the this value. // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]). // 3. Return ? TemporalInstantToString(instant, undefined, "auto"). return TemporalInstantToString( isolate, instant, isolate->factory()->undefined_value(), Precision::kAuto, "Temporal.Instant.prototype.toJSON"); } // #sec-temporal.instant.prototype.tolocalestring MaybeHandle<String> JSTemporalInstant::ToLocaleString( Isolate* isolate, Handle<JSTemporalInstant> instant, Handle<Object> locales, Handle<Object> options) { const char* method_name = "Temporal.Instant.prototype.toLocaleString"; #ifdef V8_INTL_SUPPORT return JSDateTimeFormat::TemporalToLocaleString(isolate, instant, locales, options, method_name); #else // V8_INTL_SUPPORT return TemporalInstantToString(isolate, instant, isolate->factory()->undefined_value(), Precision::kAuto, method_name); #endif // V8_INTL_SUPPORT } // #sec-temporal.instant.prototype.tostring MaybeHandle<String> JSTemporalInstant::ToString( Isolate* isolate, Handle<JSTemporalInstant> instant, Handle<Object> options_obj) { Factory* factory = isolate->factory(); const char* method_name = "Temporal.Instant.prototype.toString"; // 1. Let instant be the this value. // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]). // 3. Set options to ? GetOptionsObject(options). Handle<JSReceiver> options; ASSIGN_RETURN_ON_EXCEPTION( isolate, options, GetOptionsObject(isolate, options_obj, method_name), String); // 4. Let timeZone be ? Get(options, "timeZone"). Handle<Object> time_zone; ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone, JSReceiver::GetProperty(isolate, options, factory->timeZone_string()), String); // 5. If timeZone is not undefined, then if (!IsUndefined(*time_zone)) { // a. Set timeZone to ? ToTemporalTimeZone(timeZone). ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone, temporal::ToTemporalTimeZone(isolate, time_zone, method_name), String); } // 6. Let precision be ? ToSecondsStringPrecision(options). StringPrecision precision; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, precision, ToSecondsStringPrecision(isolate, options, method_name), Handle<String>()); // 7. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). RoundingMode rounding_mode; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, rounding_mode, ToTemporalRoundingMode(isolate, options, RoundingMode::kTrunc, method_name), Handle<String>()); // 8. Let roundedNs be ! RoundTemporalInstant(instant.[[Nanoseconds]], // precision.[[Increment]], precision.[[Unit]], roundingMode). Handle<BigInt> rounded_ns = RoundTemporalInstant(isolate, handle(instant->nanoseconds(), isolate), precision.increment, precision.unit, rounding_mode); // 9. Let roundedInstant be ! CreateTemporalInstant(roundedNs). Handle<JSTemporalInstant> rounded_instant = temporal::CreateTemporalInstant(isolate, rounded_ns).ToHandleChecked(); // 10. Return ? TemporalInstantToString(roundedInstant, timeZone, // precision.[[Precision]]). return TemporalInstantToString(isolate, rounded_instant, time_zone, precision.precision, "Temporal.Instant.prototype.toString"); } // #sec-temporal.instant.prototype.tozoneddatetimeiso MaybeHandle<JSTemporalZonedDateTime> JSTemporalInstant::ToZonedDateTimeISO( Isolate* isolate, Handle<JSTemporalInstant> handle, Handle<Object> item_obj) { TEMPORAL_ENTER_FUNC(); Factory* factory = isolate->factory(); // 1. Let instant be the this value. // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]). // 3. If Type(item) is Object, then if (IsJSReceiver(*item_obj)) { Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); // a. Let timeZoneProperty be ? Get(item, "timeZone"). Handle<Object> time_zone_property; ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone_property, JSReceiver::GetProperty(isolate, item, factory->timeZone_string()), JSTemporalZonedDateTime); // b. If timeZoneProperty is not undefined, then if (!IsUndefined(*time_zone_property)) { // i. Set item to timeZoneProperty. item_obj = time_zone_property; } } // 4. Let timeZone be ? ToTemporalTimeZone(item). Handle<JSReceiver> time_zone; ASSIGN_RETURN_ON_EXCEPTION( isolate, time_zone, temporal::ToTemporalTimeZone( isolate, item_obj, "Temporal.Instant.prototype.toZonedDateTimeISO"), JSTemporalZonedDateTime); // 5. Let calendar be ! GetISO8601Calendar(). Handle<JSTemporalCalendar> calendar = temporal::GetISO8601Calendar(isolate); // 6. Return ? CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, // calendar). return CreateTemporalZonedDateTime( isolate, Handle<BigInt>(handle->nanoseconds(), isolate), time_zone, calendar); } namespace { // #sec-temporal-adddurationtoorsubtractdurationfrominstant MaybeHandle<JSTemporalInstant> AddDurationToOrSubtractDurationFromInstant( Isolate* isolate, Arithmetic operation, Handle<JSTemporalInstant> handle, Handle<Object> temporal_duration_like, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1. double sign = operation == Arithmetic::kSubtract ? -1.0 : 1.0; // See https://github.com/tc39/proposal-temporal/pull/2253 // 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike). DurationRecord duration; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, duration, temporal::ToTemporalDurationRecord(isolate, temporal_duration_like, method_name), Handle<JSTemporalInstant>()); TimeDurationRecord& time_duration = duration.time_duration; if (time_duration.days != 0 || duration.months != 0 || duration.weeks != 0 || duration.years != 0) { THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Handle<JSTemporalInstant>()); } // 3. Let ns be ? AddInstant(instant.[[EpochNanoseconds]], sign x // duration.[[Hours]], sign x duration.[[Minutes]], sign x // duration.[[Seconds]], sign x duration.[[Milliseconds]], sign x // duration.[[Microseconds]], sign x duration.[[Nanoseconds]]). Handle<BigInt> ns; ASSIGN_RETURN_ON_EXCEPTION( isolate, ns, AddInstant( isolate, Handle<BigInt>(handle->nanoseconds(), isolate), {0, sign * time_duration.hours, sign * time_duration.minutes, sign * time_duration.seconds, sign * time_duration.milliseconds, sign * time_duration.microseconds, sign * time_duration.nanoseconds}), JSTemporalInstant); // 4. Return ! CreateTemporalInstant(ns). return temporal::CreateTemporalInstant(isolate, ns); } // #sec-temporal-negatetemporalroundingmode RoundingMode NegateTemporalRoundingMode(RoundingMode rounding_mode) { switch (rounding_mode) { // 1. If roundingMode is "ceil", return "floor". case RoundingMode::kCeil: return RoundingMode::kFloor; // 2. If roundingMode is "floor", return "ceil". case RoundingMode::kFloor: return RoundingMode::kCeil; // 3. If roundingMode is "halfCeil", return "halfFloor". case RoundingMode::kHalfCeil: return RoundingMode::kHalfFloor; // 4. If roundingMode is "halfFloor", return "halfCeil". case RoundingMode::kHalfFloor: return RoundingMode::kHalfCeil; // 5. Return roundingMode. default: return rounding_mode; } } // #sec-temporal-getdifferencesettings Maybe<DifferenceSettings> GetDifferenceSettings( Isolate* isolate, TimePreposition operation, Handle<Object> options, UnitGroup unit_group, DisallowedUnitsInDifferenceSettings disallowed_units, Unit fallback_smallest_unit, Unit smallest_largest_default_unit, const char* method_name) { DifferenceSettings record; // 1. Set options to ? GetOptionsObject(options). ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, record.options, GetOptionsObject(isolate, options, method_name), Nothing<DifferenceSettings>()); // 2. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", // unitGroup, fallbackSmallestUnit). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, record.smallest_unit, GetTemporalUnit(isolate, record.options, "smallestUnit", unit_group, fallback_smallest_unit, fallback_smallest_unit == Unit::kNotPresent, method_name), Nothing<DifferenceSettings>()); // 3. If disallowedUnits contains smallestUnit, throw a RangeError exception. if (disallowed_units == DisallowedUnitsInDifferenceSettings::kWeekAndDay) { if (record.smallest_unit == Unit::kWeek) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kInvalidUnit, isolate->factory()->smallestUnit_string(), isolate->factory()->week_string()), Nothing<DifferenceSettings>()); } if (record.smallest_unit == Unit::kDay) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kInvalidUnit, isolate->factory()->smallestUnit_string(), isolate->factory()->day_string()), Nothing<DifferenceSettings>()); } } // 4. Let defaultLargestUnit be ! // LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit). Unit default_largest_unit = LargerOfTwoTemporalUnits( smallest_largest_default_unit, record.smallest_unit); // 5. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", unitGroup, // "auto"). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, record.largest_unit, GetTemporalUnit(isolate, record.options, "largestUnit", unit_group, Unit::kAuto, false, method_name), Nothing<DifferenceSettings>()); // 6. If disallowedUnits contains largestUnit, throw a RangeError exception. if (disallowed_units == DisallowedUnitsInDifferenceSettings::kWeekAndDay) { if (record.largest_unit == Unit::kWeek) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kInvalidUnit, isolate->factory()->largestUnit_string(), isolate->factory()->week_string()), Nothing<DifferenceSettings>()); } if (record.largest_unit == Unit::kDay) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kInvalidUnit, isolate->factory()->largestUnit_string(), isolate->factory()->day_string()), Nothing<DifferenceSettings>()); } } // 7. If largestUnit is "auto", set largestUnit to defaultLargestUnit. if (record.largest_unit == Unit::kAuto) { record.largest_unit = default_largest_unit; } // 8. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not // largestUnit, throw a RangeError exception. if (LargerOfTwoTemporalUnits(record.largest_unit, record.smallest_unit) != record.largest_unit) { THROW_NEW_ERROR_RETURN_VALUE( isolate, NewRangeError(MessageTemplate::kInvalidArgumentForTemporal, isolate->factory()->largestUnit_string()), Nothing<DifferenceSettings>()); } // 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, record.rounding_mode, ToTemporalRoundingMode(isolate, record.options, RoundingMode::kTrunc, method_name), Nothing<DifferenceSettings>()); // 10. If operation is since, then if (operation == TimePreposition::kSince) { // a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode). record.rounding_mode = NegateTemporalRoundingMode(record.rounding_mode); } // 11. Let maximum be ! // MaximumTemporalDurationRoundingIncrement(smallestUnit). Maximum maximum = MaximumTemporalDurationRoundingIncrement(record.smallest_unit); // 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, // maximum, false). MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, record.rounding_increment, ToTemporalRoundingIncrement(isolate, record.options, maximum.value, maximum.defined, false), Nothing<DifferenceSettings>()); // 13. Return the Record { [[SmallestUnit]]: smallestUnit, [[LargestUnit]]: // largestUnit, [[RoundingMode]]: roundingMode, [[RoundingIncrement]]: // roundingIncrement, [[Options]]: options }. return Just(record); } // #sec-temporal-differenceinstant TimeDurationRecord DifferenceInstant(Isolate* isolate, Handle<BigInt> ns1, Handle<BigInt> ns2, double rounding_increment, Unit smallest_unit, Unit largest_unit, RoundingMode rounding_mode, const char* method_name) { // 1. Assert: Type(ns1) is BigInt. // 2. Assert: Type(ns2) is BigInt. // 3. Assert: The following step cannot fail due to overflow in the Number // domain because abs(ns2 - ns1) <= 2 x nsMaxInstant. // 4. Let roundResult be ! RoundDuration(0, 0, 0, 0, 0, 0, 0, 0, 0, ns2 - ns1, // roundingIncrement, smallestUnit, roundingMode).[[DurationRecord]]. Handle<BigInt> diff = BigInt::Subtract(isolate, ns2, ns1).ToHandleChecked(); // Note: Since diff could be very big and over the precision of double can // hold, break diff into diff_hours and diff_nanoseconds before pass into // RoundDuration. Handle<BigInt> nanoseconds_in_a_hour = BigInt::FromUint64(isolate, 3600000000000); double diff_hours = Object::Number(*BigInt::ToNumber( isolate, BigInt::Divide(isolate, diff, nanoseconds_in_a_hour).ToHandleChecked())); double diff_nanoseconds = Object::Number(*BigInt::ToNumber( isolate, BigInt::Remainder(isolate, diff, nanoseconds_in_a_hour) .ToHandleChecked())); DurationRecordWithRemainder round_record = RoundDuration( isolate, {0, 0, 0, {0, diff_hours, 0, 0, 0, 0, diff_nanoseconds}}, rounding_increment, smallest_unit, rounding_mode, method_name) .ToChecked(); // 5. Assert: roundResult.[[Days]] is 0. DCHECK_EQ(0, round_record.record.time_duration.days); // 6. Return ! BalanceDuration(0, roundResult.[[Hours]], // roundResult.[[Minutes]], roundResult.[[Seconds]], // roundResult.[[Milliseconds]], roundResult.[[Microseconds]], // roundResult.[[Nanoseconds]], largestUnit). return BalanceDuration(isolate, largest_unit, isolate->factory()->undefined_value(), round_record.record.time_duration, method_name) .ToChecked(); } // #sec-temporal-differencetemporalinstant MaybeHandle<JSTemporalDuration> DifferenceTemporalInstant( Isolate* isolate, TimePreposition operation, Handle<JSTemporalInstant> instant, Handle<Object> other_obj, Handle<Object> options, const char* method_name) { TEMPORAL_ENTER_FUNC(); // 1. If operation is since, let sign be -1. Otherwise, let sign be 1. double sign = operation == TimePreposition::kSince ? -1 : 1; // 2. Set other to ? ToTemporalInstant(other). Handle<JSTemporalInstant> other; ASSIGN_RETURN_ON_EXCEPTION(isolate, other, ToTemporalInstant(isolate, other_obj, method_name), JSTemporalDuration); // 3. Let settings be ? GetDifferenceSettings(operation, options, time, « », // "nanosecond", "second"). DifferenceSettings settings; MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, settings, GetDifferenceSettings(isolate, operation, options, UnitGroup::kTime, DisallowedUnitsInDifferenceSettings::kNone, Unit::kNanosecond, Unit::kSecond, method_name), Handle<JSTemporalDuration>()); // 4. Let result be ! DifferenceInstant(instant.[[Nanoseconds]], // other.[[Nanoseconds]], settings.[[RoundingIncrement]], // settings.[[SmallestUnit]], settings.[[LargestUnit]], // settings.[[RoundingMode]]). TimeDurationRecord result = DifferenceInstant( isolate, handle(instant->nanoseconds(), isolate), handle(other->nanoseconds(), isolate), settings.rounding_increment, settings.smallest_unit, settings.largest_unit, settings.rounding_mode, method_name); // 5. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], // sign × result.[[Minutes]], sign × result.[[Seconds]], sign × // result.[[Milliseconds]], sign × result.[[Microseconds]], sign × // result.[[Nanoseconds]]). return CreateTemporalDuration( isolate, {0, 0, 0, {0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds}}) .ToHandleChecked(); } } // namespace // #sec-temporal.instant.prototype.add MaybeHandle<JSTemporalInstant> JSTemporalInstant::Add( Isolate* isolate, Handle<JSTemporalInstant> handle, Handle<Object> temporal_duration_like) { TEMPORAL_ENTER_FUNC(); return AddDurationToOrSubtractDurationFromInstant( isolate, Arithmetic::kAdd, handle, temporal_duration_like, "Temporal.Instant.prototype.add"); } // #sec-temporal.instant.prototype.subtract MaybeHandle<JSTemporalInstant> JSTemporalInstant::Subtract( Isolate* isolate, Handle<JSTemporalInstant> handle, Handle<Object> temporal_duration_like) { TEMPORAL_ENTER_FUNC(); return AddDurationToOrSubtractDurationFromInstant( isolate, Arithmetic::kSubtract, handle, temporal_duration_like, "Temporal.Instant.prototype.subtract"); } // #sec-temporal.instant.prototype.until MaybeHandle<JSTemporalDuration> JSTemporalInstant::Until( Isolate* isolate, Handle<JSTemporalInstant> handle, Handle<Object> other, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return DifferenceTemporalInstant(isolate, TimePreposition::kUntil, handle, other, options, "Temporal.Instant.prototype.until"); } // #sec-temporal.instant.prototype.since MaybeHandle<JSTemporalDuration> JSTemporalInstant::Since( Isolate* isolate, Handle<JSTemporalInstant> handle, Handle<Object> other, Handle<Object> options) { TEMPORAL_ENTER_FUNC(); return DifferenceTemporalInstant(isolate, TimePreposition::kSince, handle, other, options, "Temporal.Instant.prototype.since"); } namespace temporal { // Step iii and iv of #sec-temporal.calendar.prototype.fields MaybeHandle<Oddball> IsInvalidTemporalCalendarField( Isolate* isolate, Handle<String> next_value, Handle<FixedArray> fields_name) { Factory* factory = isolate->factory(); // iii. iii. If fieldNames contains nextValue, then for (int i = 0; i < fields_name->length(); i++) { Tagged<Object> item = fields_name->get(i); DCHECK(IsString(item)); if (String::Equals(isolate, next_value, handle(String::cast(item), isolate))) { return isolate->factory()->true_value(); } } // iv. If nextValue is not one of "year", "month", "monthCode", "day", "hour", // "minute", "second", "millisecond", "microsecond", "nanosecond", then if (!(String::Equals(isolate, next_value, factory->year_string()) || String::Equals(isolate, next_value, factory->month_string()) || String::Equals(isolate, next_value, factory->monthCode_string()) || String::Equals(isolate, next_value, factory->day_string()) || String::Equals(isolate, next_value, factory->hour_string()) || String::Equals(isolate, next_value, factory->minute_string()) || String::Equals(isolate, next_value, factory->second_string()) || String::Equals(isolate, next_value, factory->millisecond_string()) || String::Equals(isolate, next_value, factory->microsecond_string()) || String::Equals(isolate, next_value, factory->nanosecond_string()))) { return isolate->factory()->true_value(); } return isolate->factory()->false_value(); } // #sec-temporal-getbuiltincalendar MaybeHandle<JSTemporalCalendar> GetBuiltinCalendar(Isolate* isolate, Handle<String> id) { return JSTemporalCalendar::Constructor(isolate, CONSTRUCTOR(calendar), CONSTRUCTOR(calendar), id); } // A simple conviention function to avoid the need to unnecessarily exposing // the definiation of enum Disambiguation. MaybeHandle<JSTemporalInstant> BuiltinTimeZoneGetInstantForCompatible( Isolate* isolate, Handle<JSReceiver> time_zone, Handle<JSTemporalPlainDateTime> date_time, const char* method_name) { return BuiltinTimeZoneGetInstantFor(isolate, time_zone, date_time, Disambiguation::kCompatible, method_name); } } // namespace temporal } // namespace internal } // namespace v8