| // 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_exception()); |
| isolate->clear_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 indirect_handle(builder.Finish().ToHandleChecked(), isolate); |
| } |
| |
| 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 indirect_handle(builder.Finish().ToHandleChecked(), isolate); |
| } |
| |
| 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(possible_instants->get(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(possible_instants->get(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(possible_instants->get(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(possible_instants->get(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(possible_instants->get(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(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 indirect_handle(builder.Finish().ToHandleChecked(), isolate); |
| } |
| |
| 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 indirect_handle(builder.Finish().ToHandleChecked(), isolate); |
| } |
| |
| 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 indirect_handle(builder.Finish().ToHandleChecked(), isolate); |
| } |
| |
| // #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 indirect_handle(builder.Finish().ToHandleChecked(), isolate); |
| } |
| |
| // #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 indirect_handle(builder.Finish().ToHandleChecked(), isolate); |
| } |
| |
| // #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 indirect_handle(builder.Finish().ToHandleChecked(), isolate); |
| } |
| |
| // #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_exception()); |
| isolate->clear_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 indirect_handle(builder.Finish(), isolate); |
| } |
| |
| // #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 indirect_handle(builder.Finish().ToHandleChecked(), isolate); |
| } |
| } // 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::RightTrimOrEmpty(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->RightTrim(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->RightTrim(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->RightTrim(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 indirect_handle(builder.Finish(), isolate); |
| } |
| |
| // #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 indirect_handle(builder.Finish(), isolate); |
| } |
| |
| } // 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 |