| // 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 |
| struct DateRecordCommon { |
| int32_t year; |
| int32_t month; |
| int32_t day; |
| }; |
| |
| struct TimeRecordCommon { |
| int32_t hour; |
| int32_t minute; |
| int32_t second; |
| int32_t millisecond; |
| int32_t microsecond; |
| int32_t nanosecond; |
| }; |
| |
| struct DateTimeRecordCommon { |
| DateRecordCommon date; |
| TimeRecordCommon time; |
| }; |
| |
| struct DateRecord { |
| DateRecordCommon date; |
| Handle<String> calendar; |
| }; |
| |
| struct TimeRecord { |
| TimeRecordCommon time; |
| Handle<String> calendar; |
| }; |
| |
| struct DateTimeRecord { |
| DateRecordCommon date; |
| TimeRecordCommon time; |
| Handle<String> calendar; |
| }; |
| |
| struct InstantRecord { |
| DateRecordCommon date; |
| TimeRecordCommon time; |
| Handle<String> offset_string; |
| }; |
| |
| // #sec-temporal-time-duration-records |
| struct TimeDurationRecord { |
| double days; |
| double hours; |
| double minutes; |
| double seconds; |
| double milliseconds; |
| double microseconds; |
| double nanoseconds; |
| |
| // #sec-temporal-createtimedurationrecord |
| static Maybe<TimeDurationRecord> Create(Isolate* isolate, double days, |
| double hours, double minutes, |
| double seconds, double milliseconds, |
| double microseconds, |
| double nanoseconds); |
| }; |
| |
| // #sec-temporal-duration-records |
| // Cannot reuse DateDurationRecord here due to duplicate days. |
| struct DurationRecord { |
| double years; |
| double months; |
| double weeks; |
| TimeDurationRecord time_duration; |
| // #sec-temporal-createdurationrecord |
| static Maybe<DurationRecord> Create(Isolate* isolate, double years, |
| double months, double weeks, double days, |
| double hours, double minutes, |
| double seconds, double milliseconds, |
| double microseconds, double nanoseconds); |
| }; |
| |
| // #sec-temporal-isvalidduration |
| bool IsValidDuration(Isolate* isolate, const DurationRecord& dur); |
| |
| // #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); |
| }; |
| |
| struct TimeZoneRecord { |
| bool z; |
| Handle<String> offset_string; |
| Handle<String> name; |
| }; |
| |
| struct ZonedDateTimeRecord { |
| DateTimeRecord date_time; |
| TimeZoneRecord time_zone; |
| }; |
| // 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 }; |
| |
| enum class Precision { k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, kAuto, kMinute }; |
| |
| // #sec-temporal-totemporaloffset |
| 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, kTrunc, kHalfExpand }; |
| |
| enum class MatchBehaviour { kMatchExactly, kMatchMinutes }; |
| |
| // ISO8601 String Parsing |
| |
| // #sec-temporal-parsetemporalcalendarstring |
| V8_WARN_UNUSED_RESULT MaybeHandle<String> ParseTemporalCalendarString( |
| Isolate* isolate, Handle<String> iso_string); |
| |
| // #sec-temporal-parsetemporaldatestring |
| V8_WARN_UNUSED_RESULT Maybe<DateRecord> ParseTemporalDateString( |
| Isolate* isolate, Handle<String> iso_string); |
| |
| // #sec-temporal-parsetemporaltimestring |
| Maybe<TimeRecord> 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-parsetemporaltimezone |
| V8_WARN_UNUSED_RESULT MaybeHandle<String> ParseTemporalTimeZone( |
| Isolate* isolate, Handle<String> 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); |
| |
| DateRecordCommon BalanceISODate(Isolate* isolate, const DateRecordCommon& 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); |
| |
| V8_WARN_UNUSED_RESULT Maybe<DurationRecord> DifferenceISODateTime( |
| Isolate* isolate, const DateTimeRecordCommon& date_time1, |
| const DateTimeRecordCommon& date_time2, Handle<JSReceiver> calendar, |
| Unit largest_unit, Handle<Object> relative_to, const char* method_name); |
| |
| // #sec-temporal-adddatetime |
| V8_WARN_UNUSED_RESULT Maybe<DateTimeRecordCommon> AddDateTime( |
| Isolate* isolate, const DateTimeRecordCommon& 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); |
| |
| V8_WARN_UNUSED_RESULT Maybe<NanosecondsToDaysResult> NanosecondsToDays( |
| Isolate* isolate, double 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) { |
| return GetStringOption<RoundingMode>( |
| isolate, options, "roundingMode", method_name, |
| {"ceil", "floor", "trunc", "halfExpand"}, |
| {RoundingMode::kCeil, RoundingMode::kFloor, RoundingMode::kTrunc, |
| RoundingMode::kHalfExpand}, |
| fallback); |
| } |
| |
| V8_WARN_UNUSED_RESULT |
| Handle<BigInt> GetEpochFromISOParts(Isolate* isolate, |
| const DateTimeRecordCommon& 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 TimeRecordCommon& time); |
| |
| // #sec-temporal-isvalidisodate |
| bool IsValidISODate(Isolate* isolate, const DateRecordCommon& date); |
| |
| // #sec-temporal-compareisodate |
| int32_t CompareISODate(const DateRecordCommon& date1, |
| const DateRecordCommon& date2); |
| |
| // #sec-temporal-balanceisoyearmonth |
| void BalanceISOYearMonth(Isolate* isolate, int32_t* year, int32_t* month); |
| |
| // #sec-temporal-balancetime |
| V8_WARN_UNUSED_RESULT DateTimeRecordCommon |
| BalanceTime(const TimeRecordCommon& time); |
| |
| // #sec-temporal-differencetime |
| V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> DifferenceTime( |
| Isolate* isolate, const TimeRecordCommon& time1, |
| const TimeRecordCommon& time2); |
| |
| // #sec-temporal-addtime |
| V8_WARN_UNUSED_RESULT DateTimeRecordCommon |
| AddTime(Isolate* isolate, const TimeRecordCommon& time, |
| const TimeDurationRecord& addend); |
| |
| // #sec-temporal-totaldurationnanoseconds |
| double TotalDurationNanoseconds(Isolate* isolate, |
| const TimeDurationRecord& duration, |
| double offset_shift); |
| |
| // #sec-temporal-totemporaltimerecord |
| Maybe<TimeRecordCommon> 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); |
| |
| // #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 |
| Handle<String> DefaultTimeZone(Isolate* isolate) { |
| TEMPORAL_ENTER_FUNC(); |
| // For now, always return "UTC" |
| // TODO(ftang) implement behavior specified in #sup-defaulttimezone |
| return isolate->factory()->UTC_string(); |
| } |
| |
| // #sec-temporal-isodatetimewithinlimits |
| bool ISODateTimeWithinLimits(Isolate* isolate, |
| const DateTimeRecordCommon& 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 Jan 26, -271819 and |
| * 100000001 days after Jan 1 1970 is around Nov 4, 275760. |
| */ |
| if (date_time.date.year > -271819 && date_time.date.year < 275760) |
| return true; |
| if (date_time.date.year < -271819 || date_time.date.year > 275760) |
| return false; |
| if (date_time.date.year == -271819) { |
| if (date_time.date.month > 11) return true; |
| if (date_time.date.month < 11) return false; |
| return (date_time.date.day > 4); |
| } else { |
| DCHECK_EQ(date_time.date.year, 275760); |
| if (date_time.date.month > 1) return false; |
| if (date_time.date.month < 1) return true; |
| return (date_time.date.day > 26); |
| } |
| // 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()->CurrentClockTimeMillis(); |
| // 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 DateRecordCommon& 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 DateRecordCommon& 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 DateTimeRecordCommon& 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 DateTimeRecordCommon& 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 DateTimeRecordCommon& 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 TimeRecordCommon& 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); |
| } |
| |
| // 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) |
| Handle<JSTemporalCalendar> calendar = temporal::GetISO8601Calendar(isolate); |
| 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 TimeRecordCommon& 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 |
| // a RangeError exception. |
| if (!IsValidISODate(isolate, {reference_iso_year, iso_month, iso_day})) { |
| THROW_INVALID_RANGE(JSTemporalPlainMonthDay); |
| } |
| // 4. If newTarget is not present, set it to %Temporal.PlainMonthDay%. |
| // 5. 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); |
| // 6. Set object.[[ISOMonth]] to isoMonth. |
| object->set_iso_month(iso_month); |
| // 7. Set object.[[ISODay]] to isoDay. |
| object->set_iso_day(iso_day); |
| // 8. Set object.[[Calendar]] to calendar. |
| object->set_calendar(*calendar); |
| // 9. Set object.[[ISOYear]] to referenceISOYear. |
| object->set_iso_year(reference_iso_year); |
| // 10. 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); |
| } |
| |
| // #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); |
| } |
| |
| // #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; |
| } |
| |
| MaybeHandle<JSTemporalTimeZone> CreateTemporalTimeZoneUTC( |
| Isolate* isolate, Handle<JSFunction> target, |
| Handle<HeapObject> new_target) { |
| TEMPORAL_ENTER_FUNC(); |
| return CreateTemporalTimeZoneFromIndex(isolate, target, new_target, 0); |
| } |
| |
| bool IsUTC(Isolate* isolate, Handle<String> time_zone); |
| |
| // #sec-temporal-createtemporaltimezone |
| MaybeHandle<JSTemporalTimeZone> CreateTemporalTimeZone( |
| Isolate* isolate, Handle<JSFunction> target, Handle<HeapObject> new_target, |
| Handle<String> identifier) { |
| TEMPORAL_ENTER_FUNC(); |
| // 1. If newTarget is not present, set it to %Temporal.TimeZone%. |
| // 2. Let object be ? OrdinaryCreateFromConstructor(newTarget, |
| // "%Temporal.TimeZone.prototype%", « [[InitializedTemporalTimeZone]], |
| // [[Identifier]], [[OffsetNanoseconds]] »). |
| |
| // 3. Let offsetNanosecondsResult be ParseTimeZoneOffsetString(identifier). |
| Maybe<int64_t> maybe_offset_nanoseconds = |
| ParseTimeZoneOffsetString(isolate, identifier); |
| // 4. If offsetNanosecondsResult is an abrupt completion, then |
| if (maybe_offset_nanoseconds.IsNothing()) { |
| DCHECK(isolate->has_pending_exception()); |
| isolate->clear_pending_exception(); |
| // a. Assert: ! CanonicalizeTimeZoneName(identifier) is identifier. |
| DCHECK(String::Equals(isolate, identifier, |
| CanonicalizeTimeZoneName(isolate, identifier))); |
| |
| // b. Set object.[[Identifier]] to identifier. |
| // c. Set object.[[OffsetNanoseconds]] to undefined. |
| if (IsUTC(isolate, identifier)) { |
| return CreateTemporalTimeZoneUTC(isolate, target, new_target); |
| } |
| #ifdef V8_INTL_SUPPORT |
| int32_t time_zone_index = Intl::GetTimeZoneIndex(isolate, identifier); |
| DCHECK_GE(time_zone_index, 0); |
| return CreateTemporalTimeZoneFromIndex(isolate, target, new_target, |
| time_zone_index); |
| #else |
| UNREACHABLE(); |
| #endif // V8_INTL_SUPPORT |
| // 5. Else, |
| } else { |
| // a. Set object.[[Identifier]] to ! |
| // FormatTimeZoneOffsetString(offsetNanosecondsResult.[[Value]]). b. Set |
| // object.[[OffsetNanoseconds]] to offsetNanosecondsResult.[[Value]]. |
| ORDINARY_CREATE_FROM_CONSTRUCTOR(object, target, new_target, |
| JSTemporalTimeZone) |
| object->set_flags(0); |
| object->set_details(0); |
| |
| object->set_is_offset(true); |
| object->set_offset_nanoseconds(maybe_offset_nanoseconds.FromJust()); |
| return object; |
| } |
| // 6. Return object. |
| } |
| |
| MaybeHandle<JSTemporalTimeZone> CreateTemporalTimeZoneDefaultTarget( |
| Isolate* isolate, Handle<String> identifier) { |
| TEMPORAL_ENTER_FUNC(); |
| return CreateTemporalTimeZone(isolate, CONSTRUCTOR(time_zone), |
| CONSTRUCTOR(time_zone), identifier); |
| } |
| |
| } // namespace |
| |
| namespace temporal { |
| MaybeHandle<JSTemporalTimeZone> CreateTemporalTimeZone( |
| Isolate* isolate, Handle<String> identifier) { |
| return CreateTemporalTimeZoneDefaultTarget(isolate, identifier); |
| } |
| } // namespace temporal |
| |
| namespace { |
| |
| // #sec-temporal-systeminstant |
| Handle<JSTemporalInstant> SystemInstant(Isolate* isolate) { |
| TEMPORAL_ENTER_FUNC(); |
| // 1. Let ns be ! SystemUTCEpochNanoseconds(). |
| Handle<BigInt> ns = SystemUTCEpochNanoseconds(isolate); |
| // 2. Return ? CreateTemporalInstant(ns). |
| return temporal::CreateTemporalInstant(isolate, ns).ToHandleChecked(); |
| } |
| |
| // #sec-temporal-systemtimezone |
| Handle<JSTemporalTimeZone> SystemTimeZone(Isolate* isolate) { |
| TEMPORAL_ENTER_FUNC(); |
| Handle<String> default_time_zone = DefaultTimeZone(isolate); |
| return temporal::CreateTemporalTimeZone(isolate, default_time_zone) |
| .ToHandleChecked(); |
| } |
| |
| DateTimeRecordCommon GetISOPartsFromEpoch(Isolate* isolate, |
| Handle<BigInt> epoch_nanoseconds) { |
| TEMPORAL_ENTER_FUNC(); |
| DateTimeRecordCommon result; |
| // 1. Let remainderNs be epochNanoseconds modulo 10^6. |
| Handle<BigInt> million = BigInt::FromInt64(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(); |
| } |
| |
| // 2. 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); |
| |
| // 3. Let year be ! YearFromTime(epochMilliseconds). |
| result.date.year = year; |
| // 4. Let month be ! MonthFromTime(epochMilliseconds) + 1. |
| result.date.month = month + 1; |
| DCHECK_GE(result.date.month, 1); |
| DCHECK_LE(result.date.month, 12); |
| // 5. Let day be ! DateFromTime(epochMilliseconds). |
| result.date.day = day; |
| DCHECK_GE(result.date.day, 1); |
| DCHECK_LE(result.date.day, 31); |
| // 6. Let hour be ! HourFromTime(epochMilliseconds). |
| result.time.hour = hour; |
| DCHECK_GE(result.time.hour, 0); |
| DCHECK_LE(result.time.hour, 23); |
| // 7. Let minute be ! MinFromTime(epochMilliseconds). |
| result.time.minute = min; |
| DCHECK_GE(result.time.minute, 0); |
| DCHECK_LE(result.time.minute, 59); |
| // 8. Let second be ! SecFromTime(epochMilliseconds). |
| result.time.second = sec; |
| DCHECK_GE(result.time.second, 0); |
| DCHECK_LE(result.time.second, 59); |
| // 9. Let millisecond be ! msFromTime(epochMilliseconds). |
| result.time.millisecond = ms; |
| DCHECK_GE(result.time.millisecond, 0); |
| DCHECK_LE(result.time.millisecond, 999); |
| // 10. 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); |
| DCHECK_LE(result.time.microsecond, 999); |
| // 11. Let nanosecond be remainderNs modulo 1000. |
| result.time.nanosecond = remainder % 1000; |
| DCHECK_GE(result.time.nanosecond, 0); |
| DCHECK_LE(result.time.nanosecond, 999); |
| return result; |
| } |
| |
| // #sec-temporal-balanceisodatetime |
| DateTimeRecordCommon BalanceISODateTime(Isolate* isolate, |
| const DateTimeRecordCommon& 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). |
| DateTimeRecordCommon balanced_time = BalanceTime(date_time.time); |
| // 3. Let balancedDate be ! BalanceISODate(year, month, day + |
| // balancedTime.[[Days]]). |
| DateRecordCommon added_date = date_time.date; |
| added_date.day += balanced_time.date.day; |
| DateRecordCommon 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-temporaldurationtostring |
| Handle<String> TemporalDurationToString(Isolate* isolate, |
| Handle<JSTemporalDuration> duration, |
| Precision precision) { |
| IncrementalStringBuilder builder(isolate); |
| DurationRecord dur = { |
| duration->years().Number(), |
| duration->months().Number(), |
| duration->weeks().Number(), |
| {duration->days().Number(), duration->hours().Number(), |
| duration->minutes().Number(), duration->seconds().Number(), |
| duration->milliseconds().Number(), duration->microseconds().Number(), |
| duration->nanoseconds().Number()}}; |
| // 1. Assert: precision is not "minute". |
| DCHECK(precision != Precision::kMinute); |
| // 2. Set seconds to the mathematical value of seconds. |
| // 3. Set milliseconds to the mathematical value of milliseconds. |
| // 4. Set microseconds to the mathematical value of microseconds. |
| // 5. Set nanoseconds to the mathematical value of nanoseconds. |
| // 6. Let sign be ! DurationSign(years, months, weeks, days, hours, minutes, |
| // seconds, milliseconds, microseconds, nanoseconds). |
| int32_t sign = DurationSign(isolate, dur); |
| |
| // 7. Set microseconds to microseconds + the integral part of nanoseconds / |
| // 1000. |
| dur.time_duration.microseconds += |
| static_cast<int>(dur.time_duration.nanoseconds / 1000); |
| // 8. Set nanoseconds to remainder(nanoseconds, 1000). |
| dur.time_duration.nanoseconds = |
| std::remainder(dur.time_duration.nanoseconds, 1000); |
| // 9. Set milliseconds to milliseconds + the integral part of microseconds / |
| // 1000. |
| dur.time_duration.milliseconds += |
| static_cast<int>(dur.time_duration.microseconds / 1000); |
| // 10. Set microseconds to microseconds modulo 1000. |
| dur.time_duration.microseconds = |
| std::remainder(dur.time_duration.microseconds, 1000); |
| // 11. Set seconds to seconds + the integral part of milliseconds / 1000. |
| dur.time_duration.seconds += |
| static_cast<int>(dur.time_duration.milliseconds / 1000); |
| // 12. Set milliseconds to milliseconds modulo 1000. |
| dur.time_duration.milliseconds = |
| std::remainder(dur.time_duration.milliseconds, 1000); |
| if (sign < 0) { |
| builder.AppendCharacter('-'); |
| } |
| builder.AppendCharacter('P'); |
| // 13. Let datePart be "". |
| // 14. If years is not 0, then |
| // a. Set datePart to the string concatenation of abs(years) formatted as a |
| // decimal number and the code unit 0x0059 (LATIN CAPITAL LETTER Y). |
| if (dur.years != 0) { |
| builder.AppendInt(static_cast<int32_t>(std::abs(dur.years))); |
| builder.AppendCharacter('Y'); |
| } |
| // 15. If months is not 0, then |
| // 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). |
| if (dur.months != 0) { |
| builder.AppendInt(static_cast<int32_t>(std::abs(dur.months))); |
| builder.AppendCharacter('M'); |
| } |
| // 16. If weeks is not 0, then |
| // 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). |
| if (dur.weeks != 0) { |
| builder.AppendInt(static_cast<int32_t>(std::abs(dur.weeks))); |
| builder.AppendCharacter('W'); |
| } |
| // 17. If days is not 0, then |
| // 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). |
| if (dur.time_duration.days != 0) { |
| builder.AppendInt(static_cast<int32_t>(std::abs(dur.time_duration.days))); |
| builder.AppendCharacter('D'); |
| } |
| // 18. Let timePart be "". |
| IncrementalStringBuilder time_part(isolate); |
| // 19. If hours is not 0, then |
| // a. Set timePart to the string concatenation of abs(hours) formatted as a |
| // decimal number and the code unit 0x0048 (LATIN CAPITAL LETTER H). |
| if (dur.time_duration.hours != 0) { |
| time_part.AppendInt( |
| static_cast<int32_t>(std::abs(dur.time_duration.hours))); |
| time_part.AppendCharacter('H'); |
| } |
| // 20. If minutes is not 0, then |
| // 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). |
| if (dur.time_duration.minutes != 0) { |
| time_part.AppendInt( |
| static_cast<int32_t>(std::abs(dur.time_duration.minutes))); |
| time_part.AppendCharacter('M'); |
| } |
| // 21. If any of seconds, milliseconds, microseconds, and nanoseconds are not |
| // 0; or years, months, weeks, days, hours, and minutes are all 0, then |
| if ((dur.time_duration.seconds != 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)) { |
| // a. Let fraction be abs(milliseconds) × 10^6 + abs(microseconds) × 10^3 + |
| // abs(nanoseconds). |
| int64_t fraction = std::abs(dur.time_duration.milliseconds) * 1000000 + |
| std::abs(dur.time_duration.microseconds) * 1000 + |
| 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. |
| // e. Let secondsPart be abs(seconds) formatted as a decimal number. |
| { |
| int32_t seconds = std::abs(dur.time_duration.seconds); |
| time_part.AppendInt(seconds); |
| } |
| // 7. If precision is "auto", then |
| int64_t divisor = 100000000; |
| bool output_period = true; |
| if (precision == Precision::kAuto) { |
| // a. 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) { |
| if (output_period) { |
| time_part.AppendCharacter('.'); |
| output_period = false; |
| } |
| time_part.AppendInt(static_cast<int32_t>(fraction / divisor)); |
| fraction %= divisor; |
| divisor /= 10; |
| } |
| } else { |
| // 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++, divisor /= 10, fraction %= divisor) { |
| if (output_period) { |
| time_part.AppendCharacter('.'); |
| output_period = false; |
| } |
| time_part.AppendInt(static_cast<int32_t>(fraction / divisor)); |
| } |
| } |
| // g. Set timePart to the string concatenation of timePart, secondsPart, and |
| // the code unit 0x0053 (LATIN CAPITAL LETTER S). |
| time_part.AppendCharacter('S'); |
| } |
| // 22. Let signPart be the code unit 0x002D (HYPHEN-MINUS) if sign < 0, and |
| // otherwise the empty String. |
| // 23. Let result be the string concatenation of signPart, the code unit |
| // 0x0050 (LATIN CAPITAL LETTER P) and datePart. |
| // 24. If timePart is not "", then |
| if (time_part.Length() > 0) { |
| // a. Set result to the string concatenation of result, the code unit 0x0054 |
| // (LATIN CAPITAL LETTER T), and timePart. |
| builder.AppendCharacter('T'); |
| builder.AppendString(time_part.Finish().ToHandleChecked()); |
| } |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| void ToZeroPaddedDecimalString(IncrementalStringBuilder* builder, int32_t n, |
| int32_t min_length); |
| // #sec-temporal-formatsecondsstringpart |
| void FormatSecondsStringPart(IncrementalStringBuilder* builder, int32_t second, |
| int32_t millisecond, int32_t microsecond, |
| int32_t nanosecond, Precision precision) { |
| // 1. Assert: second, millisecond, microsecond and nanosecond are integers. |
| // 2. If precision is "minute", return "". |
| if (precision == Precision::kMinute) { |
| return; |
| } |
| // 3. Let secondsString be the string-concatenation of the code unit 0x003A |
| // (COLON) and second formatted as a two-digit decimal number, padded to the |
| // left with zeroes if necessary. |
| builder->AppendCharacter(':'); |
| ToZeroPaddedDecimalString(builder, second, 2); |
| // 4. Let fraction be millisecond × 10^6 + microsecond × 10^3 + nanosecond. |
| int32_t fraction = millisecond * 1000000 + microsecond * 1000 + nanosecond; |
| // 5. If fraction is 0, return secondsString. |
| if (fraction == 0) return; |
| // 6. Set fraction to fraction formatted as a nine-digit decimal number, |
| // padded to the left with zeroes if necessary. |
| builder->AppendCharacter('.'); |
| // 7. If precision is "auto", then |
| int32_t divisor = 100000000; |
| if (precision == Precision::kAuto) { |
| // a. Set fraction to the |
| // longest possible substring of fraction starting at position 0 and not |
| // ending with the code unit 0x0030 (DIGIT ZERO). |
| do { |
| builder->AppendInt(fraction / divisor); |
| fraction %= divisor; |
| divisor /= 10; |
| } while (fraction > 0); |
| } else { |
| // 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++, fraction %= divisor, divisor /= 10) { |
| builder->AppendInt(fraction / divisor); |
| } |
| } |
| // 7. Return the string-concatenation of secondsString, the code unit 0x002E |
| // (FULL STOP), and fraction. |
| } |
| |
| // #sec-temporal-temporaltimetostring |
| Handle<String> TemporalTimeToString(Isolate* isolate, |
| const TimeRecordCommon& time, |
| Precision precision) { |
| // 1. Assert: hour, minute, second, millisecond, microsecond and nanosecond |
| // are integers. |
| IncrementalStringBuilder builder(isolate); |
| // 2. Let hour be ToZeroPaddedDecimalString(hour, 2). |
| ToZeroPaddedDecimalString(&builder, time.hour, 2); |
| builder.AppendCharacter('-'); |
| // 3. Let minute be ToZeroPaddedDecimalString(minute, 2). |
| ToZeroPaddedDecimalString(&builder, time.minute, 2); |
| // 4. Let seconds be ! FormatSecondsStringPart(second, millisecond, |
| // microsecond, nanosecond, precision). |
| FormatSecondsStringPart(&builder, time.second, time.millisecond, |
| time.microsecond, time.nanosecond, precision); |
| // 5. Return the string-concatenation of hour, the code unit 0x003A (COLON), |
| // minute, and seconds. |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| Handle<String> TemporalTimeToString(Isolate* isolate, |
| Handle<JSTemporalPlainTime> temporal_time, |
| Precision precision) { |
| return TemporalTimeToString( |
| isolate, |
| {temporal_time->iso_hour(), temporal_time->iso_minute(), |
| temporal_time->iso_second(), temporal_time->iso_millisecond(), |
| temporal_time->iso_microsecond(), temporal_time->iso_nanosecond()}, |
| precision); |
| } |
| |
| } // namespace |
| |
| namespace temporal { |
| MaybeHandle<JSTemporalPlainDateTime> BuiltinTimeZoneGetPlainDateTimeFor( |
| Isolate* isolate, Handle<JSReceiver> time_zone, |
| Handle<JSTemporalInstant> instant, Handle<JSReceiver> calendar, |
| const char* method_name) { |
| TEMPORAL_ENTER_FUNC(); |
| // 1. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant). |
| int64_t offset_nanoseconds; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, offset_nanoseconds, |
| GetOffsetNanosecondsFor(isolate, time_zone, instant, method_name), |
| Handle<JSTemporalPlainDateTime>()); |
| // 2. Let result be ! GetISOPartsFromEpoch(instant.[[Nanoseconds]]). |
| DateTimeRecordCommon 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 (!function->IsCallable()) { |
| 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(possible_instants->IsFixedArray()); |
| // 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(date_time_obj->IsJSTemporalPlainDateTime()); |
| Handle<JSTemporalPlainDateTime> date_time = |
| Handle<JSTemporalPlainDateTime>::cast(date_time_obj); |
| |
| // 2. Let n be possibleInstants's length. |
| int32_t n = possible_instants->length(); |
| |
| // 3. If n = 1, then |
| if (n == 1) { |
| // a. Return possibleInstants[0]. |
| Handle<Object> ret_obj = FixedArray::get(*possible_instants, 0, isolate); |
| DCHECK(ret_obj->IsJSTemporalInstant()); |
| return Handle<JSTemporalInstant>::cast(ret_obj); |
| } |
| // 4. If n ≠ 0, then |
| if (n != 0) { |
| // a. If disambiguation is "earlier" or "compatible", then |
| if (disambiguation == Disambiguation::kEarlier || |
| disambiguation == Disambiguation::kCompatible) { |
| // i. Return possibleInstants[0]. |
| Handle<Object> ret_obj = FixedArray::get(*possible_instants, 0, isolate); |
| DCHECK(ret_obj->IsJSTemporalInstant()); |
| return Handle<JSTemporalInstant>::cast(ret_obj); |
| } |
| // b. If disambiguation is "later", then |
| if (disambiguation == Disambiguation::kLater) { |
| // i. Return possibleInstants[n − 1]. |
| Handle<Object> ret_obj = |
| FixedArray::get(*possible_instants, n - 1, isolate); |
| DCHECK(ret_obj->IsJSTemporalInstant()); |
| 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 dayBefore be ! CreateTemporalInstant(epochNanoseconds − 8.64 × |
| // 10^13). |
| 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(); |
| Handle<JSTemporalInstant> day_before = |
| temporal::CreateTemporalInstant(isolate, day_before_ns).ToHandleChecked(); |
| // 9. Let dayAfter be ! CreateTemporalInstant(epochNanoseconds + 8.64 × |
| // 10^13). |
| Handle<BigInt> day_after_ns = |
| BigInt::Add(isolate, epoch_nanoseconds, one_day_in_ns).ToHandleChecked(); |
| 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). |
| DateTimeRecordCommon earlier; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, earlier, |
| AddDateTime( |
| isolate, |
| {{date_time->iso_year(), date_time->iso_month(), |
| date_time->iso_day()}, |
| {date_time->iso_hour(), date_time->iso_minute(), |
| date_time->iso_second(), date_time->iso_millisecond(), |
| date_time->iso_microsecond(), date_time->iso_nanosecond()}}, |
| handle(date_time->calendar(), isolate), |
| {0, 0, 0, {0, 0, 0, 0, 0, 0, -nanoseconds}}, |
| isolate->factory()->undefined_value()), |
| Handle<JSTemporalInstant>()); |
| // See https://github.com/tc39/proposal-temporal/issues/1816 |
| // b. Let earlierDateTime be ? CreateTemporalDateTime(earlier.[[Year]], |
| // earlier.[[Month]], earlier.[[Day]], earlier.[[Hour]], earlier.[[Minute]], |
| // earlier.[[Second]], earlier.[[Millisecond]], earlier.[[Microsecond]], |
| // earlier.[[Nanosecond]], dateTime.[[Calendar]]). |
| Handle<JSTemporalPlainDateTime> earlier_date_time; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, earlier_date_time, |
| temporal::CreateTemporalDateTime( |
| isolate, earlier, handle(date_time->calendar(), isolate)), |
| JSTemporalInstant); |
| |
| // c. Set possibleInstants to ? GetPossibleInstantsFor(timeZone, |
| // earlierDateTime). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, possible_instants, |
| GetPossibleInstantsFor(isolate, time_zone, earlier_date_time), |
| JSTemporalInstant); |
| |
| // d. If possibleInstants is empty, throw a RangeError exception. |
| if (possible_instants->length() == 0) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), |
| JSTemporalInstant); |
| } |
| // e. Return possibleInstants[0]. |
| Handle<Object> ret_obj = FixedArray::get(*possible_instants, 0, isolate); |
| DCHECK(ret_obj->IsJSTemporalInstant()); |
| 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). |
| DateTimeRecordCommon later; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, later, |
| AddDateTime(isolate, |
| {{date_time->iso_year(), date_time->iso_month(), |
| date_time->iso_day()}, |
| {date_time->iso_hour(), date_time->iso_minute(), |
| date_time->iso_second(), date_time->iso_millisecond(), |
| date_time->iso_microsecond(), date_time->iso_nanosecond()}}, |
| handle(date_time->calendar(), isolate), |
| {0, 0, 0, {0, 0, 0, 0, 0, 0, nanoseconds}}, |
| isolate->factory()->undefined_value()), |
| Handle<JSTemporalInstant>()); |
| |
| // See https://github.com/tc39/proposal-temporal/issues/1816 |
| // 16. Let laterDateTime be ? CreateTemporalDateTime(later.[[Year]], |
| // later.[[Month]], later.[[Day]], later.[[Hour]], later.[[Minute]], |
| // later.[[Second]], later.[[Millisecond]], later.[[Microsecond]], |
| // later.[[Nanosecond]], dateTime.[[Calendar]]). |
| |
| Handle<JSTemporalPlainDateTime> later_date_time; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, later_date_time, |
| temporal::CreateTemporalDateTime(isolate, later, |
| handle(date_time->calendar(), isolate)), |
| JSTemporalInstant); |
| // 17. Set possibleInstants to ? GetPossibleInstantsFor(timeZone, |
| // laterDateTime). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, possible_instants, |
| GetPossibleInstantsFor(isolate, time_zone, later_date_time), |
| JSTemporalInstant); |
| // 18. Set n to possibleInstants's length. |
| n = possible_instants->length(); |
| // 19. If n = 0, throw a RangeError exception. |
| if (n == 0) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), |
| JSTemporalInstant); |
| } |
| // 20. Return possibleInstants[n − 1]. |
| Handle<Object> ret_obj = FixedArray::get(*possible_instants, n - 1, isolate); |
| DCHECK(ret_obj->IsJSTemporalInstant()); |
| 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 (item->IsJSTemporalPlainDate()) { |
| return handle(Handle<JSTemporalPlainDate>::cast(item)->calendar(), isolate); |
| } |
| if (item->IsJSTemporalPlainDateTime()) { |
| return handle(Handle<JSTemporalPlainDateTime>::cast(item)->calendar(), |
| isolate); |
| } |
| if (item->IsJSTemporalPlainMonthDay()) { |
| return handle(Handle<JSTemporalPlainMonthDay>::cast(item)->calendar(), |
| isolate); |
| } |
| if (item->IsJSTemporalPlainTime()) { |
| return handle(Handle<JSTemporalPlainTime>::cast(item)->calendar(), isolate); |
| } |
| if (item->IsJSTemporalPlainYearMonth()) { |
| return handle(Handle<JSTemporalPlainYearMonth>::cast(item)->calendar(), |
| isolate); |
| } |
| if (item->IsJSTemporalZonedDateTime()) { |
| 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 }; |
| |
| // The common part of PrepareTemporalFields and PreparePartialTemporalFields |
| // #sec-temporal-preparetemporalfields |
| // #sec-temporal-preparepartialtemporalfields |
| V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> PrepareTemporalFieldsOrPartial( |
| Isolate* isolate, Handle<JSReceiver> fields, Handle<FixedArray> field_names, |
| RequiredFields required, bool partial) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| Factory* factory = isolate->factory(); |
| // 1. Assert: Type(fields) is Object. |
| // 2. Let result be ! OrdinaryObjectCreate(%Object.prototype%). |
| Handle<JSObject> result = |
| isolate->factory()->NewJSObject(isolate->object_function()); |
| // 3. For each value property of fieldNames, do |
| int length = field_names->length(); |
| bool any = false; |
| for (int i = 0; i < length; i++) { |
| Handle<Object> property_obj = Handle<Object>(field_names->get(i), isolate); |
| Handle<String> property = Handle<String>::cast(property_obj); |
| // a. Let value be ? Get(fields, property). |
| Handle<Object> value; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, value, JSReceiver::GetProperty(isolate, fields, property), |
| JSObject); |
| |
| // b. If value is undefined, then |
| if (value->IsUndefined()) { |
| // 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 && |
| 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()))) { |
| // 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<JSObject> 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<JSObject> 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 (!function->IsCallable()) { |
| 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 ((!result->IsHeapObject()) || |
| 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); |
| } |
| |
| // #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); |
| } |
| |
| // #sec-temporal-totemporaloverflow |
| Maybe<ShowOverflow> ToTemporalOverflow(Isolate* isolate, Handle<Object> options, |
| const char* method_name) { |
| // 1. If options is undefined, return "constrain". |
| if (options->IsUndefined()) return Just(ShowOverflow::kConstrain); |
| DCHECK(options->IsJSReceiver()); |
| // 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); |
| } |
| |
| // TODO(adamk): Remove this and replace with direct usage of |
| // MAYBE_RETURN_ON_EXCEPTION_VALUE(). |
| Maybe<bool> ToTemporalOverflowForSideEffects(Isolate* isolate, |
| Handle<Object> options, |
| const char* method_name) { |
| MAYBE_RETURN_ON_EXCEPTION_VALUE( |
| isolate, ToTemporalOverflow(isolate, options, method_name), |
| Nothing<bool>()); |
| return Just(true); |
| } |
| |
| // #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 (options->IsUndefined()) return Just(fallback); |
| DCHECK(options->IsJSReceiver()); |
| |
| // 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 (options->IsUndefined()) return Just(Disambiguation::kCompatible); |
| DCHECK(options->IsJSReceiver()); |
| // 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 (item->IsJSTemporalInstant()) { |
| // i. Return item. |
| return Handle<JSTemporalInstant>::cast(item); |
| } |
| // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then |
| if (item->IsJSTemporalZonedDateTime()) { |
| // 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 (temporal_calendar_like->IsJSReceiver()) { |
| // 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 (obj->IsJSTemporal##T()) { \ |
| 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 (temporal_calendar_like->IsJSReceiver()) { |
| 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. If ! IsBuiltinCalendar(identifier) is false, then |
| if (!IsBuiltinCalendar(isolate, identifier)) { |
| // a. Let identifier be ? ParseTemporalCalendarString(identifier). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, identifier, |
| ParseTemporalCalendarString(isolate, identifier), |
| JSReceiver); |
| } |
| // 4. 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 (temporal_calendar_like->IsUndefined()) { |
| // 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; |
| } |
| |
| // #sec-temporal-totemporaldate |
| MaybeHandle<JSTemporalPlainDate> ToTemporalDate(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(options->IsJSReceiver() || options->IsUndefined()); |
| // 3. If Type(item) is Object, then |
| if (item_obj->IsJSReceiver()) { |
| Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); |
| // a. If item has an [[InitializedTemporalDate]] internal slot, then |
| // i. Return item. |
| if (item->IsJSTemporalPlainDate()) { |
| return Handle<JSTemporalPlainDate>::cast(item); |
| } |
| // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, |
| // then |
| if (item->IsJSTemporalZonedDateTime()) { |
| // i. 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(); |
| // ii. 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); |
| // iii. 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 (item->IsJSTemporalPlainDateTime()) { |
| // i. 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(ToTemporalOverflowForSideEffects(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). |
| DateRecord 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<Object> calendar_string; |
| if (result.calendar->length() == 0) { |
| calendar_string = factory->undefined_value(); |
| } else { |
| calendar_string = result.calendar; |
| } |
| Handle<JSReceiver> calendar; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar, |
| ToTemporalCalendarWithISODefault(isolate, calendar_string, 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 (!argument->IsNumber()) return false; |
| // 2. If argument is NaN, +∞𝔽, or -∞𝔽, return false. |
| double number = argument->Number(); |
| 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 (number->IsNaN() || 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(number->Number()); |
| } |
| |
| } // namespace |
| |
| namespace temporal { |
| |
| // #sec-temporal-regulatetime |
| Maybe<TimeRecordCommon> RegulateTime(Isolate* isolate, |
| const TimeRecordCommon& 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: { |
| TimeRecordCommon 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<TimeRecordCommon>()); |
| } |
| // 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, |
| ShowOverflow overflow, |
| const char* method_name) { |
| Factory* factory = isolate->factory(); |
| TimeRecord result; |
| // 2. Assert: overflow is either "constrain" or "reject". |
| // 3. If Type(item) is Object, then |
| if (item_obj->IsJSReceiver()) { |
| Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); |
| // a. If item has an [[InitializedTemporalTime]] internal slot, then |
| // i. Return item. |
| if (item->IsJSTemporalPlainTime()) { |
| return Handle<JSTemporalPlainTime>::cast(item); |
| } |
| // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, |
| // then |
| if (item->IsJSTemporalZonedDateTime()) { |
| // 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 (item->IsJSTemporalPlainDateTime()) { |
| // 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 |
| if ((result.calendar->length() > 0) /* not undefined */ && |
| !String::Equals(isolate, 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 (!temporal_duration_like_obj->IsJSReceiver()) { |
| // 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 (temporal_duration_like->IsJSTemporalDuration()) { |
| // 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, duration->years().Number(), duration->months().Number(), |
| duration->weeks().Number(), duration->days().Number(), |
| duration->hours().Number(), duration->minutes().Number(), |
| duration->seconds().Number(), duration->milliseconds().Number(), |
| duration->microseconds().Number(), duration->nanoseconds().Number()); |
| } |
| // 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 (val->IsUndefined()) { |
| // 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 (item->IsJSTemporalDuration()) { |
| // 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 (temporal_time_zone_like->IsJSReceiver()) { |
| // a. If temporalTimeZoneLike has an [[InitializedTemporalZonedDateTime]] |
| // internal slot, then |
| if (temporal_time_zone_like->IsJSTemporalZonedDateTime()) { |
| // 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 (temporal_time_zone_like->IsJSReceiver()) { |
| // 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 result be ? ParseTemporalTimeZone(identifier). |
| Handle<String> result; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result, ParseTemporalTimeZone(isolate, identifier), JSReceiver); |
| |
| // 4. Return ? CreateTemporalTimeZone(result). |
| return temporal::CreateTemporalTimeZone(isolate, result); |
| } |
| |
| } // 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 (temporal_time_zone_like->IsUndefined()) { |
| // 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 (temporal_time_zone_like->IsUndefined()) { |
| // 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) { |
| switch (r) { |
| case ComparisonResult::kEqual: |
| return 0; |
| case ComparisonResult::kLessThan: |
| return -1; |
| case ComparisonResult::kGreaterThan: |
| return 1; |
| case ComparisonResult::kUndefined: |
| UNREACHABLE(); |
| } |
| } |
| |
| // #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); |
| // 3. Let nanoseconds be offsetNanoseconds modulo 10^9. |
| int64_t nanoseconds = offset_nanoseconds % 1000000000; |
| // 4. Let seconds be floor(offsetNanoseconds / 10^9) modulo 60. |
| int64_t seconds = (offset_nanoseconds / 1000000000) % 60; |
| // 5. Let minutes be floor(offsetNanoseconds / (6 × 10^10)) modulo 60. |
| int64_t minutes = (offset_nanoseconds / 60000000000) % 60; |
| // 6. Let hours be floor(offsetNanoseconds / (3.6 × 10^12)). |
| int64_t hours = offset_nanoseconds / 3600000000000; |
| // 7. Let h be hours, formatted as a two-digit decimal number, padded to the |
| // left with a zero if necessary. |
| if (hours < 10) { |
| builder.AppendCharacter('0'); |
| } |
| builder.AppendInt(static_cast<int32_t>(hours)); |
| // 8. Let m be minutes, formatted as a two-digit decimal number, padded to the |
| // left with a zero if necessary. |
| builder.AppendCharacter(':'); |
| if (minutes < 10) { |
| builder.AppendCharacter('0'); |
| } |
| builder.AppendInt(static_cast<int>(minutes)); |
| // 9. Let s be seconds, formatted as a two-digit decimal number, padded to the |
| // left with a zero if necessary. |
| // 10. If nanoseconds ≠ 0, then |
| if (nanoseconds != 0) { |
| builder.AppendCharacter(':'); |
| if (seconds < 10) { |
| builder.AppendCharacter('0'); |
| } |
| builder.AppendInt(static_cast<int>(seconds)); |
| builder.AppendCharacter('.'); |
| // a. Let fraction be nanoseconds, formatted as a nine-digit decimal number, |
| // padded to the left with zeroes if necessary. |
| // b. Set fraction to the longest possible substring of fraction starting at |
| // position 0 and not ending with the code unit 0x0030 (DIGIT ZERO). |
| int64_t divisor = 100000000; |
| do { |
| builder.AppendInt(static_cast<int>(nanoseconds / divisor)); |
| nanoseconds %= divisor; |
| divisor /= 10; |
| } while (nanoseconds > 0); |
| // c. Let post be the string-concatenation of the code unit 0x003A (COLON), |
| // s, the code unit 0x002E (FULL STOP), and fraction. |
| // 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(':'); |
| if (seconds < 10) { |
| builder.AppendCharacter('0'); |
| } |
| builder.AppendInt(static_cast<int>(seconds)); |
| } |
| // 12. Return the string-concatenation of sign, h, the code unit 0x003A |
| // (COLON), m, and post. |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| int32_t DecimalLength(int32_t n) { |
| int32_t i = 1; |
| while (n >= 10) { |
| n /= 10; |
| i++; |
| } |
| return i; |
| } |
| |
| // #sec-tozeropaddeddecimalstring |
| void ToZeroPaddedDecimalString(IncrementalStringBuilder* builder, int32_t n, |
| int32_t min_length) { |
| for (int32_t pad = min_length - DecimalLength(n); pad > 0; pad--) { |
| builder->AppendCharacter('0'); |
| } |
| builder->AppendInt(n); |
| } |
| |
| // #sec-temporal-padisoyear |
| void PadISOYear(IncrementalStringBuilder* builder, int32_t y) { |
| // 1. Assert: y is an integer. |
| // 2. If y ≥ 0 and y ≤ 9999, then |
| if (y >= 0 && y <= 9999) { |
| // a. Return ToZeroPaddedDecimalString(y, 4). |
| ToZeroPaddedDecimalString(builder, y, 4); |
| return; |
| } |
| // 3. If y > 0, let yearSign be "+"; otherwise, let yearSign be "-". |
| if (y > 0) { |
| builder->AppendCharacter('+'); |
| } else { |
| builder->AppendCharacter('-'); |
| } |
| // 4. Let year be ToZeroPaddedDecimalString(abs(y), 6). |
| ToZeroPaddedDecimalString(builder, std::abs(y), 6); |
| // 5. Return the string-concatenation of yearSign and year. |
| } |
| |
| // #sec-temporal-formatcalendarannotation |
| Handle<String> FormatCalendarAnnotation(Isolate* isolate, Handle<String> id, |
| ShowCalendar show_calendar) { |
| // 1.Assert: showCalendar is "auto", "always", or "never". |
| // 2. If showCalendar is "never", return the empty String. |
| if (show_calendar == ShowCalendar::kNever) { |
| return isolate->factory()->empty_string(); |
| } |
| // 3. If showCalendar is "auto" and id is "iso8601", return the empty String. |
| if (show_calendar == ShowCalendar::kAuto && |
| String::Equals(isolate, id, isolate->factory()->iso8601_string())) { |
| return isolate->factory()->empty_string(); |
| } |
| // 4. Return the string-concatenation of "[u-ca=", id, and "]". |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendCStringLiteral("[u-ca="); |
| builder.AppendString(id); |
| builder.AppendCharacter(']'); |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| // #sec-temporal-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 calendarID be ? ToString(temporalDate.[[Calendar]]). |
| Handle<String> calendar_id; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar_id, |
| Object::ToString(isolate, handle(temporal_date->calendar(), isolate)), |
| String); |
| |
| // 7. Let calendar be ! FormatCalendarAnnotation(calendarID, |
| // showCalendar). |
| Handle<String> calendar_string = |
| FormatCalendarAnnotation(isolate, calendar_id, show_calendar); |
| // 8. 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_string); |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| // #sec-temporal-temporalmonthdaytostring |
| MaybeHandle<String> TemporalMonthDayToString( |
| Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day, |
| ShowCalendar show_calendar) { |
| // 1. Assert: Type(monthDay) is Object. |
| // 2. Assert: monthDay has an [[InitializedTemporalMonthDay]] internal slot. |
| IncrementalStringBuilder builder(isolate); |
| // 6. Let calendarID be ? ToString(monthDay.[[Calendar]]). |
| Handle<String> calendar_id; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar_id, |
| Object::ToString(isolate, handle(month_day->calendar(), isolate)), |
| String); |
| // 7. If showCalendar is "always" or if calendarID is not "iso8601", then |
| if (show_calendar == ShowCalendar::kAlways || |
| !String::Equals(isolate, calendar_id, |
| isolate->factory()->iso8601_string())) { |
| // a. Let year be ! PadISOYear(monthDay.[[ISOYear]]). |
| PadISOYear(&builder, month_day->iso_year()); |
| // b. Set result to the string-concatenation of year, the code unit |
| // 0x002D (HYPHEN-MINUS), and result. |
| builder.AppendCharacter('-'); |
| } |
| // 3. Let month be ToZeroPaddedDecimalString(monthDay.[[ISOMonth]], 2). |
| ToZeroPaddedDecimalString(&builder, month_day->iso_month(), 2); |
| // 5. Let result be the string-concatenation of month, the code unit 0x002D |
| // (HYPHEN-MINUS), and day. |
| builder.AppendCharacter('-'); |
| // 4. Let day be ToZeroPaddedDecimalString(monthDay.[[ISODay]], 2). |
| ToZeroPaddedDecimalString(&builder, month_day->iso_day(), 2); |
| // 8. Let calendarString be ! FormatCalendarAnnotation(calendarID, |
| // showCalendar). |
| Handle<String> calendar_string = |
| FormatCalendarAnnotation(isolate, calendar_id, show_calendar); |
| // 9. Set result to the string-concatenation of result and calendarString. |
| builder.AppendString(calendar_string); |
| // 10. Return result. |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| // #sec-temporal-temporalyearmonthtostring |
| MaybeHandle<String> TemporalYearMonthToString( |
| Isolate* isolate, Handle<JSTemporalPlainYearMonth> year_month, |
| ShowCalendar show_calendar) { |
| // 1. Assert: Type(yearMonth) is Object. |
| // 2. Assert: yearMonth has an [[InitializedTemporalYearMonth]] internal slot. |
| IncrementalStringBuilder builder(isolate); |
| // 3. Let year be ! PadISOYear(yearMonth.[[ISOYear]]). |
| PadISOYear(&builder, year_month->iso_year()); |
| // 4. Let month be ToZeroPaddedDecimalString(yearMonth.[[ISOMonth]], 2). |
| // 5. Let result be the string-concatenation of year, the code unit 0x002D |
| // (HYPHEN-MINUS), and month. |
| builder.AppendCharacter('-'); |
| ToZeroPaddedDecimalString(&builder, year_month->iso_month(), 2); |
| // 6. Let calendarID be ? ToString(yearMonth.[[Calendar]]). |
| Handle<String> calendar_id; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar_id, |
| Object::ToString(isolate, handle(year_month->calendar(), isolate)), |
| String); |
| // 7. If showCalendar is "always" or if *_calendarID_ is not *"iso8601", then |
| if (show_calendar == ShowCalendar::kAlways || |
| !String::Equals(isolate, calendar_id, |
| isolate->factory()->iso8601_string())) { |
| // a. Let day be ToZeroPaddedDecimalString(yearMonth.[[ISODay]], 2). |
| // b. Set result to the string-concatenation of result, the code unit 0x002D |
| // (HYPHEN-MINUS), and day. |
| builder.AppendCharacter('-'); |
| ToZeroPaddedDecimalString(&builder, year_month->iso_day(), 2); |
| } |
| // 8. Let calendarString be ! FormatCalendarAnnotation(calendarID, |
| // showCalendar). |
| Handle<String> calendar_string = |
| FormatCalendarAnnotation(isolate, calendar_id, show_calendar); |
| // 9. Set result to the string-concatenation of result and calendarString. |
| builder.AppendString(calendar_string); |
| // 10. Return result. |
| return builder.Finish().ToHandleChecked(); |
| } |
| |
| // #sec-temporal-builtintimezonegetoffsetstringfor |
| MaybeHandle<String> BuiltinTimeZoneGetOffsetStringFor( |
| Isolate* isolate, Handle<JSReceiver> time_zone, |
| Handle<JSTemporalInstant> instant, const char* method_name) { |
| TEMPORAL_ENTER_FUNC(); |
| // 1. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant). |
| int64_t offset_nanoseconds; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, offset_nanoseconds, |
| GetOffsetNanosecondsFor(isolate, time_zone, instant, method_name), |
| Handle<String>()); |
| |
| // 2. Return ! FormatTimeZoneOffsetString(offsetNanoseconds). |
| return FormatTimeZoneOffsetString(isolate, offset_nanoseconds); |
| } |
| |
| // #sec-temporal-parseisodatetime |
| Maybe<DateTimeRecord> ParseISODateTime(Isolate* isolate, |
| Handle<String> iso_string, |
| const ParsedISO8601Result& parsed) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| DateTimeRecord result; |
| // 5. Set year to ! ToIntegerOrInfinity(year). |
| result.date.year = parsed.date_year; |
| // 6. If month is undefined, then |
| if (parsed.date_month_is_undefined()) { |
| // a. Set month to 1. |
| result.date.month = 1; |
| // 7. Else, |
| } else { |
| // a. Set month to ! ToIntegerOrInfinity(month). |
| result.date.month = parsed.date_month; |
| } |
| |
| // 8. If day is undefined, then |
| if (parsed.date_day_is_undefined()) { |
| // a. Set day to 1. |
| result.date.day = 1; |
| // 9. Else, |
| } else { |
| // a. Set day to ! ToIntegerOrInfinity(day). |
| result.date.day = parsed.date_day; |
| } |
| // 10. Set hour to ! ToIntegerOrInfinity(hour). |
| result.time.hour = parsed.time_hour_is_undefined() ? 0 : parsed.time_hour; |
| // 11. Set minute to ! ToIntegerOrInfinity(minute). |
| result.time.minute = |
| parsed.time_minute_is_undefined() ? 0 : parsed.time_minute; |
| // 12. Set second to ! ToIntegerOrInfinity(second). |
| result.time.second = |
| parsed.time_second_is_undefined() ? 0 : parsed.time_second; |
| // 13. If second is 60, then |
| if (result.time.second == 60) { |
| // a. Set second to 59. |
| result.time.second = 59; |
| } |
| // 14. If fraction is not undefined, then |
| if (!parsed.time_nanosecond_is_undefined()) { |
| // a. Set fraction to the string-concatenation of the previous value of |
| // fraction and the string "000000000". |
| // b. Let millisecond be the String value equal to the substring of fraction |
| // from 0 to 3. c. Set millisecond to ! ToIntegerOrInfinity(millisecond). |
| result.time.millisecond = parsed.time_nanosecond / 1000000; |
| // d. Let microsecond be the String value equal to the substring of fraction |
| // from 3 to 6. e. Set microsecond to ! ToIntegerOrInfinity(microsecond). |
| result.time.microsecond = (parsed.time_nanosecond / 1000) % 1000; |
| // f. Let nanosecond be the String value equal to the substring of fraction |
| // from 6 to 9. g. Set nanosecond to ! ToIntegerOrInfinity(nanosecond). |
| result.time.nanosecond = (parsed.time_nanosecond % 1000); |
| // 15. Else, |
| } else { |
| // a. Let millisecond be 0. |
| result.time.millisecond = 0; |
| // b. Let microsecond be 0. |
| result.time.microsecond = 0; |
| // c. Let nanosecond be 0. |
| result.time.nanosecond = 0; |
| } |
| // 16. If ! IsValidISODate(year, month, day) is false, throw a RangeError |
| // exception. |
| if (!IsValidISODate(isolate, result.date)) { |
| THROW_NEW_ERROR_RETURN_VALUE(isolate, |
| NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), |
| Nothing<DateTimeRecord>()); |
| } |
| // 17. If ! IsValidTime(hour, minute, second, millisecond, microsecond, |
| // 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<DateTimeRecord>()); |
| } |
| // 18. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day, |
| // [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: |
| // millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond, |
| // [[Calendar]]: calendar }. |
| if (parsed.calendar_name_length == 0) { |
| result.calendar = isolate->factory()->empty_string(); |
| } else { |
| result.calendar = isolate->factory()->NewSubString( |
| iso_string, parsed.calendar_name_start, |
| parsed.calendar_name_start + parsed.calendar_name_length); |
| } |
| return Just(result); |
| } |
| |
| // #sec-temporal-parsetemporaldatestring |
| Maybe<DateRecord> ParseTemporalDateString(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 TemporalDateString |
| // (see 13.33), then |
| base::Optional<ParsedISO8601Result> parsed = |
| TemporalParser::ParseTemporalDateString(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<DateRecord>()); |
| } |
| |
| // 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<DateRecord>()); |
| } |
| // 3. Let result be ? ParseISODateTime(isoString). |
| DateTimeRecord result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result, ParseISODateTime(isolate, iso_string, *parsed), |
| Nothing<DateRecord>()); |
| // 4. Return the Record { [[Year]]: result.[[Year]], [[Month]]: |
| // result.[[Month]], [[Day]]: result.[[Day]], [[Calendar]]: |
| // result.[[Calendar]] }. |
| DateRecord ret = {result.date, result.calendar}; |
| return Just(ret); |
| } |
| |
| // #sec-temporal-parsetemporaltimestring |
| Maybe<TimeRecord> 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<TimeRecord>()); |
| } |
| |
| // 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<TimeRecord>()); |
| } |
| |
| // 3. Let result be ? ParseISODateTime(isoString). |
| DateTimeRecord result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result, ParseISODateTime(isolate, iso_string, *parsed), |
| Nothing<TimeRecord>()); |
| // 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]] }. |
| TimeRecord ret = {result.time, result.calendar}; |
| return Just(ret); |
| } |
| |
| // #sec-temporal-parsetemporalinstantstring |
| Maybe<InstantRecord> ParseTemporalInstantString(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 TemporalInstantString |
| // (see 13.33), then |
| 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>()); |
| } |
| |
| // 3. Let result be ! ParseISODateTime(isoString). |
| DateTimeRecord result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result, ParseISODateTime(isolate, iso_string, *parsed), |
| Nothing<InstantRecord>()); |
| |
| // 4. Let timeZoneResult be ? ParseTemporalTimeZoneString(isoString). |
| TimeZoneRecord time_zone_result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, time_zone_result, |
| ParseTemporalTimeZoneString(isolate, iso_string), |
| Nothing<InstantRecord>()); |
| // 5. Let offsetString be timeZoneResult.[[OffsetString]]. |
| Handle<String> offset_string = time_zone_result.offset_string; |
| // 6. If timeZoneResult.[[Z]] is true, then |
| if (time_zone_result.z) { |
| // a. Set offsetString to "+00:00". |
| offset_string = isolate->factory()->NewStringFromStaticChars("+00:00"); |
| } |
| // 7. Assert: offsetString is not undefined. |
| DCHECK_GT(offset_string->length(), 0); |
| |
| // 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-parsetemporalinstant |
| MaybeHandle<BigInt> ParseTemporalInstant(Isolate* isolate, |
| Handle<String> iso_string) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| Factory* factory = isolate->factory(); |
| // 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_NE(result.offset_string->length(), 0); |
| |
| // 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. If utc < −8.64 × 10^21 or utc > 8.64 × 10^21, then |
| if ((BigInt::CompareToNumber(utc, factory->NewNumber(-8.64e21)) == |
| ComparisonResult::kLessThan) || |
| (BigInt::CompareToNumber(utc, factory->NewNumber(8.64e21)) == |
| ComparisonResult::kGreaterThan)) { |
| // a. Throw a RangeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), BigInt); |
| } |
| // 7. Let offsetNanoseconds be ? ParseTimeZoneOffsetString(offsetString). |
| int64_t offset_nanoseconds; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, offset_nanoseconds, |
| ParseTimeZoneOffsetString(isolate, result.offset_string), |
| Handle<BigInt>()); |
| |
| // 8. Return utc − offsetNanoseconds. |
| return BigInt::Subtract(isolate, utc, |
| BigInt::FromInt64(isolate, offset_nanoseconds)); |
| } |
| |
| // #sec-temporal-parsetemporalzoneddatetimestring |
| Maybe<ZonedDateTimeRecord> 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<ZonedDateTimeRecord>()); |
| } |
| |
| // 2. Let result be ? ParseISODateTime(isoString). |
| ZonedDateTimeRecord result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result.date_time, ParseISODateTime(isolate, iso_string, *parsed), |
| Nothing<ZonedDateTimeRecord>()); |
| |
| // 3. Let timeZoneResult be ? ParseTemporalTimeZoneString(isoString). |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result.time_zone, |
| ParseTemporalTimeZoneString(isolate, iso_string), |
| Nothing<ZonedDateTimeRecord>()); |
| // 4. Return the 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]], |
| // [[Calendar]]: result.[[Calendar]], [[TimeZoneZ]]: timeZoneResult.[[Z]], |
| // [[TimeZoneOffsetString]]: timeZoneResult.[[OffsetString]], |
| // [[TimeZoneName]]: timeZoneResult.[[Name]] }. |
| return Just(result); |
| } |
| |
| // #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 int64_t IfEmptyReturnZero(int64_t 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 |
| // except the three three units < seconds. For all others, we use 'double' |
| // 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. |
| // For milliseconds_mv, microseconds_mv, and nanoseconds_mv, we use int32_t |
| // instead because their maximum number during calculation is 999999999, |
| // which can be encoded in 30 bits and the parsed.seconds_fraction return from |
| // the ISO8601 parser are stored in an integer, in the unit of nanoseconds. |
| // Therefore, use "int32_t" will avoid rounding error for the final |
| // calculating of nanoseconds_mv. |
| // |
| // 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; |
| } |
| int32_t milliseconds_mv; |
| int32_t nanoseconds_mv; |
| // 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 = |
| static_cast<int32_t>(IfEmptyReturnZero(parsed->seconds_fraction)); |
| // 15. Else, |
| } else { |
| // a. Let millisecondsMV be remainder(secondsMV, 1) × 1000. |
| nanoseconds_mv = (seconds_mv - std::floor(seconds_mv)) * 1000000000; |
| } |
| milliseconds_mv = nanoseconds_mv / 1000000; |
| // 16. Let microsecondsMV be remainder(millisecondsMV, 1) × 1000. |
| int32_t microseconds_mv = (nanoseconds_mv / 1000) % 1000; |
| // 17. Let nanosecondsMV be remainder(microsecondsMV, 1) × 1000. |
| nanoseconds_mv = nanoseconds_mv % 1000; |
| |
| DCHECK_LE(milliseconds_mv, 1000); |
| DCHECK_LE(microseconds_mv, 1000); |
| DCHECK_LE(nanoseconds_mv, 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> iso_string) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| // 1. Assert: Type(isoString) is String. |
| // 2. If isoString does not satisfy the syntax of a TemporalTimeZoneString |
| // (see 13.33), then |
| base::Optional<ParsedISO8601Result> parsed = |
| TemporalParser::ParseTemporalTimeZoneString(isolate, iso_string); |
| if (!parsed.has_value()) { |
| THROW_NEW_ERROR_RETURN_VALUE(isolate, |
| NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), |
| Nothing<TimeZoneRecord>()); |
| } |
| // 3. Let z, sign, hours, minutes, seconds, fraction and name be the parts of |
| // isoString produced respectively by the UTCDesignator, |
| // TimeZoneUTCOffsetSign, TimeZoneUTCOffsetHour, TimeZoneUTCOffsetMinute, |
| // TimeZoneUTCOffsetSecond, TimeZoneUTCOffsetFraction, and TimeZoneIANAName |
| // productions, or undefined if not present. |
| // 4. If z is not undefined, then |
| if (parsed->utc_designator) { |
| // a. Return the Record { [[Z]]: true, [[OffsetString]]: undefined, |
| // [[Name]]: name }. |
| if (parsed->tzi_name_length > 0) { |
| Handle<String> name = isolate->factory()->NewSubString( |
| iso_string, parsed->tzi_name_start, |
| parsed->tzi_name_start + parsed->tzi_name_length); |
| TimeZoneRecord ret({true, isolate->factory()->empty_string(), name}); |
| return Just(ret); |
| } |
| TimeZoneRecord ret({true, isolate->factory()->empty_string(), |
| isolate->factory()->empty_string()}); |
| return Just(ret); |
| } |
| |
| // 5. If hours is undefined, then |
| // a. Let offsetString be undefined. |
| // 6. Else, |
| Handle<String> offset_string; |
| bool offset_string_is_defined = false; |
| if (!parsed->tzuo_hour_is_undefined()) { |
| // a. Assert: sign is not undefined. |
| DCHECK(!parsed->tzuo_sign_is_undefined()); |
| // b. Set hours to ! ToIntegerOrInfinity(hours). |
| int64_t hours = parsed->tzuo_hour; |
| // c. If sign is the code unit 0x002D (HYPHEN-MINUS) or the code unit 0x2212 |
| // (MINUS SIGN), then i. Set sign to −1. d. Else, i. Set sign to 1. |
| int64_t sign = parsed->tzuo_sign; |
| // e. Set minutes to ! ToIntegerOrInfinity(minutes). |
| int64_t minutes = |
| parsed->tzuo_minute_is_undefined() ? 0 : parsed->tzuo_minute; |
| // f. Set seconds to ! ToIntegerOrInfinity(seconds). |
| int64_t seconds = |
| parsed->tzuo_second_is_undefined() ? 0 : parsed->tzuo_second; |
| // g. If fraction is not undefined, then |
| int64_t nanoseconds; |
| if (!parsed->tzuo_nanosecond_is_undefined()) { |
| // i. Set fraction to the string-concatenation of the previous value of |
| // fraction and the string "000000000". |
| // ii. Let nanoseconds be the String value equal to the substring of |
| // fraction from 0 to 9. iii. Set nanoseconds to ! |
| // ToIntegerOrInfinity(nanoseconds). |
| nanoseconds = parsed->tzuo_nanosecond; |
| // h. Else, |
| } else { |
| // i. Let nanoseconds be 0. |
| nanoseconds = 0; |
| } |
| // i. Let offsetNanoseconds be sign × (((hours × 60 + minutes) × 60 + |
| // seconds) × 10^9 + nanoseconds). |
| int64_t offset_nanoseconds = |
| sign * |
| (((hours * 60 + minutes) * 60 + seconds) * 1000000000 + nanoseconds); |
| // j. Let offsetString be ! FormatTimeZoneOffsetString(offsetNanoseconds). |
| offset_string = FormatTimeZoneOffsetString(isolate, offset_nanoseconds); |
| offset_string_is_defined = true; |
| } |
| // 7. If name is not undefined, then |
| Handle<String> name; |
| if (parsed->tzi_name_length > 0) { |
| name = isolate->factory()->NewSubString( |
| iso_string, parsed->tzi_name_start, |
| parsed->tzi_name_start + parsed->tzi_name_length); |
| |
| // a. If ! IsValidTimeZoneName(name) is false, throw a RangeError exception. |
| if (!IsValidTimeZoneName(isolate, name)) { |
| THROW_NEW_ERROR_RETURN_VALUE(isolate, |
| NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), |
| Nothing<TimeZoneRecord>()); |
| } |
| // b. Set name to ! CanonicalizeTimeZoneName(name). |
| name = CanonicalizeTimeZoneName(isolate, name); |
| // 8. Return the Record { [[Z]]: false, [[OffsetString]]: offsetString, |
| // [[Name]]: name }. |
| TimeZoneRecord ret({false, |
| offset_string_is_defined |
| ? offset_string |
| : isolate->factory()->empty_string(), |
| name}); |
| return Just(ret); |
| } |
| // 8. Return the Record { [[Z]]: false, [[OffsetString]]: offsetString, |
| // [[Name]]: name }. |
| TimeZoneRecord ret({false, |
| offset_string_is_defined |
| ? offset_string |
| : isolate->factory()->empty_string(), |
| isolate->factory()->empty_string()}); |
| return Just(ret); |
| } |
| |
| // #sec-temporal-parsetemporaltimezone |
| MaybeHandle<String> ParseTemporalTimeZone(Isolate* isolate, |
| Handle<String> string) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| // 2. Let result be ? ParseTemporalTimeZoneString(string). |
| TimeZoneRecord result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result, ParseTemporalTimeZoneString(isolate, string), |
| Handle<String>()); |
| |
| // 3. If result.[[Name]] is not undefined, return result.[[Name]]. |
| if (result.name->length() > 0) { |
| return result.name; |
| } |
| |
| // 4. If result.[[Z]] is true, return "UTC". |
| if (result.z) { |
| return isolate->factory()->UTC_string(); |
| } |
| |
| // 5. Return result.[[OffsetString]]. |
| return result.offset_string; |
| } |
| |
| 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. Assert: Type(isoString) is String. |
| // 2. If isoString does not satisfy the syntax of a TemporalCalendarString |
| // (see 13.33), then a. Throw a RangeError exception. |
| base::Optional<ParsedISO8601Result> parsed = |
| TemporalParser::ParseTemporalCalendarString(isolate, iso_string); |
| if (!parsed.has_value()) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), String); |
| } |
| // 3. Let id be the part of isoString produced by the CalendarName production, |
| // or undefined if not present. |
| // 4. If id is undefined, then |
| if (parsed->calendar_name_length == 0) { |
| // a. Return "iso8601". |
| return isolate->factory()->iso8601_string(); |
| } |
| Handle<String> id = isolate->factory()->NewSubString( |
| iso_string, parsed->calendar_name_start, |
| parsed->calendar_name_start + parsed->calendar_name_length); |
| // 5. If ! IsBuiltinCalendar(id) is false, then |
| if (!IsBuiltinCalendar(isolate, id)) { |
| // a. Throw a RangeError exception. |
| THROW_NEW_ERROR( |
| isolate, NewRangeError(MessageTemplate::kInvalidCalendar, id), String); |
| } |
| // 6. Return id. |
| return id; |
| } |
| |
| // #sec-temporal-calendarequals |
| MaybeHandle<Oddball> CalendarEquals(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 isolate->factory()->true_value(); |
| } |
| // 2. Let calendarOne be ? ToString(one). |
| Handle<String> calendar_one; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, calendar_one, |
| Object::ToString(isolate, one), Oddball); |
| // 3. Let calendarTwo be ? ToString(two). |
| Handle<String> calendar_two; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, calendar_two, |
| Object::ToString(isolate, two), Oddball); |
| // 4. If calendarOne is calendarTwo, return true. |
| if (String::Equals(isolate, calendar_one, calendar_two)) { |
| return isolate->factory()->true_value(); |
| } |
| // 5. Return false. |
| return isolate->factory()->false_value(); |
| } |
| |
| // #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(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 (!fields->IsUndefined()) { |
| // 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(fields_array->IsFixedArray()); |
| return Handle<FixedArray>::cast(fields_array); |
| } |
| |
| MaybeHandle<JSTemporalPlainDate> CalendarDateAdd(Isolate* isolate, |
| Handle<JSReceiver> calendar, |
| Handle<Object> date, |
| Handle<Object> duration, |
| Handle<Object> options) { |
| return CalendarDateAdd(isolate, calendar, date, duration, options, |
| 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) { |
| // 1. Assert: Type(options) is Object or Undefined. |
| DCHECK(options->IsJSReceiver() || options->IsUndefined()); |
| // 2. If dateAdd is not present, set dateAdd to ? GetMethod(calendar, |
| // "dateAdd"). |
| if (date_add->IsUndefined()) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, date_add, |
| Object::GetMethod(calendar, isolate->factory()->dateAdd_string()), |
| JSTemporalPlainDate); |
| } |
| // 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 (!added_date->IsJSTemporalPlainDate()) { |
| 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 (date_until->IsUndefined()) { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, date_until, |
| Object::GetMethod(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 (!duration->IsJSTemporalDuration()) { |
| 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(next_key->IsString()); |
| 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, |
| JSReceiver::GetPropertyOrElement(isolate, fields, next_key_string), |
| JSReceiver); |
| // ii. If propValue is not undefined, then |
| if (!prop_value->IsUndefined()) { |
| // 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(next_key->IsString()); |
| 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, |
| JSReceiver::GetPropertyOrElement( |
| isolate, additional_fields, next_key_string), |
| JSReceiver); |
| // b. If propValue is not undefined, then |
| if (!prop_value->IsUndefined()) { |
| // 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 (!month->IsUndefined()) { |
| // 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 (!month_code->IsUndefined()) { |
| // 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(time_zone_obj, |
| isolate->factory()->getOffsetNanosecondsFor_string()), |
| Nothing<int64_t>()); |
| if (!get_offset_nanoseconds_for->IsCallable()) { |
| 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 (!offset_nanoseconds_obj->IsNumber()) { |
| 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 = offset_nanoseconds_obj->Number(); |
| |
| // 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->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 (!function->IsCallable()) { |
| 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 (result->IsUndefined()) { \ |
| 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(result->Number()), 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 (result->IsUndefined()) { |
| 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 (!result->IsUndefined()) { |
| 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 (!result->IsUndefined()) { |
| 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) |
| |
| // #sec-temporal-isbuiltincalendar |
| bool IsBuiltinCalendar(Isolate* isolate, const std::string& id) { |
| return GetCalendarMap()->Contains(id); |
| } |
| |
| bool IsBuiltinCalendar(Isolate* isolate, Handle<String> id) { |
| return IsBuiltinCalendar(isolate, 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) { |
| 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) { |
| // 1. If id is not "iso8601", return false. |
| // 2. Return true |
| return isolate->factory()->iso8601_string()->Equals(*id); |
| } |
| |
| 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<TimeRecordCommon> ToTemporalTimeRecordOrPartialTime( |
| Isolate* isolate, Handle<JSReceiver> temporal_time_like, |
| const TimeRecordCommon& time, bool skip_undefined, |
| const char* method_name) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| TimeRecordCommon 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<TimeRecordCommon>()); |
| // c. If value is not undefined, then |
| if (!value->IsUndefined()) { |
| // 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<TimeRecordCommon>()); |
| // e. / iii. Set result's internal slot whose name is the Internal Slot |
| // value of the current row to value. |
| *(row.second) = value->Number(); |
| } |
| |
| // 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<TimeRecordCommon>()); |
| } |
| // 4. Return result. |
| return Just(result); |
| } |
| |
| // #sec-temporal-topartialtime |
| Maybe<TimeRecordCommon> ToPartialTime(Isolate* isolate, |
| Handle<JSReceiver> temporal_time_like, |
| const TimeRecordCommon& time, |
| const char* method_name) { |
| return ToTemporalTimeRecordOrPartialTime(isolate, temporal_time_like, time, |
| true, method_name); |
| } |
| |
| // #sec-temporal-totemporaltimerecord |
| Maybe<TimeRecordCommon> 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-tolargesttemporalunit |
| Maybe<Unit> ToSmallestTemporalUnit(Isolate* isolate, |
| Handle<JSReceiver> normalized_options, |
| const std::set<Unit>& disallowed_units, |
| Unit fallback, const char* method_name) { |
| // 1. Assert: disallowedUnits does not contain fallback. |
| DCHECK_EQ(disallowed_units.find(fallback), disallowed_units.end()); |
| // 2. Let smallestUnit be ? GetOption(normalizedOptions, "smallestUnit", « |
| // String », « "year", "years", "month", "months", "week", "weeks", "day", |
| // "days", "hour", "hours", "minute", "minutes", "second", "seconds", |
| // "millisecond", "milliseconds", "microsecond", "microseconds", "nanosecond", |
| // "nanoseconds" », fallback). |
| Unit smallest_unit; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, smallest_unit, |
| GetStringOption<Unit>( |
| isolate, normalized_options, "smallestUnit", method_name, |
| {"year", "years", "month", "months", |
| "week", "weeks", "day", "days", |
| "hour", "hours", "minute", "minutes", |
| "second", "seconds", "millisecond", "milliseconds", |
| "microsecond", "microseconds", "nanosecond", "nanoseconds"}, |
| {Unit::kYear, Unit::kYear, Unit::kMonth, |
| Unit::kMonth, Unit::kWeek, Unit::kWeek, |
| Unit::kDay, Unit::kDay, Unit::kHour, |
| Unit::kHour, Unit::kMinute, Unit::kMinute, |
| Unit::kSecond, Unit::kSecond, Unit::kMillisecond, |
| Unit::kMillisecond, Unit::kMicrosecond, Unit::kMicrosecond, |
| Unit::kNanosecond, Unit::kNanosecond}, |
| fallback), |
| Nothing<Unit>()); |
| // 3. If smallestUnit is in the Plural column of Table 12, then |
| // a. Set smallestUnit to the corresponding Singular value of the same row. |
| // 4. If disallowedUnits contains smallestUnit, then |
| if (disallowed_units.find(smallest_unit) != disallowed_units.end()) { |
| // a. Throw a RangeError exception. |
| THROW_NEW_ERROR_RETURN_VALUE( |
| isolate, |
| NewRangeError( |
| MessageTemplate::kInvalidUnit, |
| isolate->factory()->NewStringFromAsciiChecked(method_name), |
| isolate->factory()->smallestUnit_string()), |
| Nothing<Unit>()); |
| } |
| // 5. Return smallestUnit. |
| return Just(smallest_unit); |
| } |
| |
| // #sec-temporal-mergelargestunitoption |
| MaybeHandle<JSObject> MergeLargestUnitOption(Isolate* isolate, |
| Handle<Object> options_obj, |
| Unit largest_unit) { |
| TEMPORAL_ENTER_FUNC(); |
| DCHECK(options_obj->IsUndefined() || options_obj->IsJSReceiver()); |
| // 1. If options is undefined, set options to OrdinaryObjectCreate(null). |
| Handle<Object> options; |
| if (options_obj->IsUndefined()) { |
| options = isolate->factory()->NewJSObjectWithNullProto(); |
| } else { |
| options = Handle<JSReceiver>::cast(options_obj); |
| } |
| // 2. Let merged be ! OrdinaryObjectCreate(%Object.prototype%). |
| Handle<JSObject> merged = |
| isolate->factory()->NewJSObject(isolate->object_function()); |
| // 3. Let keys be ? EnumerableOwnPropertyNames(options, key). |
| // 4. 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(); |
| |
| // 5. 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-tolargesttemporalunit |
| Maybe<Unit> ToLargestTemporalUnit(Isolate* isolate, |
| Handle<JSReceiver> normalized_options, |
| std::set<Unit> disallowed_units, |
| Unit fallback, Unit auto_value, |
| const char* method_name) { |
| // 1. Assert: disallowedUnits does not contain fallback or "auto". |
| DCHECK_EQ(disallowed_units.find(fallback), disallowed_units.end()); |
| DCHECK_EQ(disallowed_units.find(Unit::kAuto), disallowed_units.end()); |
| // 2. Assert: If autoValue is present, fallback is "auto", and disallowedUnits |
| // does not contain autoValue. |
| DCHECK(auto_value == Unit::kNotPresent || fallback == Unit::kAuto); |
| DCHECK(auto_value == Unit::kNotPresent || |
| disallowed_units.find(auto_value) == disallowed_units.end()); |
| // 3. Let largestUnit be ? GetOption(normalizedOptions, "largestUnit", « |
| // String », « "auto", "year", "years", "month", "months", "week", "weeks", |
| // "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", |
| // "millisecond", "milliseconds", "microsecond", "microseconds", "nanosecond", |
| // "nanoseconds" », fallback). |
| Unit largest_unit; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, largest_unit, |
| GetStringOption<Unit>( |
| isolate, normalized_options, "largestUnit", method_name, |
| {"auto", "year", "years", "month", |
| "months", "week", "weeks", "day", |
| "days", "hour", "hours", "minute", |
| "minutes", "second", "seconds", "millisecond", |
| "milliseconds", "microsecond", "microseconds", "nanosecond", |
| "nanoseconds"}, |
| {Unit::kAuto, Unit::kYear, Unit::kYear, |
| Unit::kMonth, Unit::kMonth, Unit::kWeek, |
| Unit::kWeek, Unit::kDay, Unit::kDay, |
| Unit::kHour, Unit::kHour, Unit::kMinute, |
| Unit::kMinute, Unit::kSecond, Unit::kSecond, |
| Unit::kMillisecond, Unit::kMillisecond, Unit::kMicrosecond, |
| Unit::kMicrosecond, Unit::kNanosecond, Unit::kNanosecond}, |
| fallback), |
| Nothing<Unit>()); |
| // 4. If largestUnit is "auto" and autoValue is present, then |
| if (largest_unit == Unit::kAuto && auto_value != Unit::kNotPresent) { |
| // a. Return autoValue. |
| return Just(auto_value); |
| } |
| // 5. If largestUnit is in the Plural column of Table 12, then |
| // a. Set largestUnit to the corresponding Singular value of the same row. |
| // 6. If disallowedUnits contains largestUnit, then |
| if (disallowed_units.find(largest_unit) != disallowed_units.end()) { |
| // a. Throw a RangeError exception. |
| THROW_NEW_ERROR_RETURN_VALUE( |
| isolate, |
| NewRangeError( |
| MessageTemplate::kInvalidUnit, |
| isolate->factory()->NewStringFromAsciiChecked(method_name), |
| isolate->factory()->largestUnit_string()), |
| Nothing<Unit>()); |
| } |
| // 7. Return largestUnit. |
| return Just(largest_unit); |
| } |
| |
| // #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(argument->Number())) { |
| // a. Throw a RangeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), Object); |
| } |
| return argument; |
| } |
| |
| // #sec-temporal-largeroftwotemporalunits |
| Unit LargerOfTwoTemporalUnits(Isolate* isolate, 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-balanceisodate |
| DateRecordCommon BalanceISODate(Isolate* isolate, |
| const DateRecordCommon& date) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| DateRecordCommon result = date; |
| // 1. Assert: year, month, and day are integers. |
| // 2. Let balancedYearMonth be ! BalanceISOYearMonth(year, month). |
| // 3. Set month to balancedYearMonth.[[Month]]. |
| // 4. Set year to balancedYearMonth.[[Year]]. |
| BalanceISOYearMonth(isolate, &(result.year), &(result.month)); |
| // 5. NOTE: To deal with negative numbers of days whose absolute value is |
| // greater than the number of days in a year, the following section subtracts |
| // years and adds days until the number of days is greater than −366 or −365. |
| // 6. If month > 2, then |
| // a. Let testYear be year. |
| // 7. Else, |
| // a. Let testYear be year − 1. |
| int32_t test_year = (date.month > 2) ? date.year : date.year - 1; |
| // 8. Repeat, while day < −1 × ! ISODaysInYear(testYear), |
| int32_t iso_days_in_year; |
| while (result.day < -(iso_days_in_year = ISODaysInYear(isolate, test_year))) { |
| // a. Set day to day + ! ISODaysInYear(testYear). |
| result.day += iso_days_in_year; |
| // b. Set year to year − 1. |
| (result.year)--; |
| // c. Set testYear to testYear − 1. |
| test_year--; |
| } |
| // 9. NOTE: To deal with numbers of days greater than the number of days in a |
| // year, the following section adds years and subtracts days until the number |
| // of days is less than 366 or 365. |
| // 10. Let testYear be year + 1. |
| test_year = (result.year) + 1; |
| // 11. Repeat, while day > ! ISODaysInYear(testYear), |
| while (result.day > (iso_days_in_year = ISODaysInYear(isolate, test_year))) { |
| // a. Set day to day − ! ISODaysInYear(testYear). |
| result.day -= iso_days_in_year; |
| // b. Set year to year + 1. |
| result.year++; |
| // c. Set testYear to testYear + 1. |
| test_year++; |
| } |
| // 12. NOTE: To deal with negative numbers of days whose absolute value is |
| // greater than the number of days in the current month, the following section |
| // subtracts months and adds days until the number of days is greater than 0. |
| // 13. Repeat, while day < 1, |
| while (result.day < 1) { |
| // a. Set balancedYearMonth to ! BalanceISOYearMonth(year, month − 1). |
| // b. Set year to balancedYearMonth.[[Year]]. |
| // c. Set month to balancedYearMonth.[[Month]]. |
| result.month -= 1; |
| BalanceISOYearMonth(isolate, &(result.year), &(result.month)); |
| // d. Set day to day + ! ISODaysInMonth(year, month). |
| result.day += ISODaysInMonth(isolate, result.year, result.month); |
| } |
| // 14. NOTE: To deal with numbers of days greater than the number of days in |
| // the current month, the following section adds months and subtracts days |
| // until the number of days is less than the number of days in the month. |
| // 15. Repeat, while day > ! ISODaysInMonth(year, month), |
| int32_t iso_days_in_month; |
| while (result.day > (iso_days_in_month = ISODaysInMonth(isolate, result.year, |
| result.month))) { |
| // a. Set day to day − ! ISODaysInMonth(year, month). |
| result.day -= iso_days_in_month; |
| // b. Set balancedYearMonth to ! BalanceISOYearMonth(year, month + 1). |
| // c. Set year to balancedYearMonth.[[Year]]. |
| // d. Set month to balancedYearMonth.[[Month]]. |
| result.month += 1; |
| BalanceISOYearMonth(isolate, &(result.year), &(result.month)); |
| } |
| // 16. Return the new Record { [[Year]]: year, [[Month]]: month, [[Day]]: day |
| // }. |
| return result; |
| } |
| |
| // #sec-temporal-adddatetime |
| Maybe<DateTimeRecordCommon> AddDateTime(Isolate* isolate, |
| const DateTimeRecordCommon& date_time, |
| Handle<JSReceiver> calendar, |
| const DurationRecord& dur, |
| Handle<Object> options) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| // 1. Assert: year, month, day, hour, minute, second, millisecond, |
| // microsecond, and nanosecond are integers. |
| // 2. Let timeResult be ! AddTime(hour, minute, second, millisecond, |
| // microsecond, nanosecond, hours, minutes, seconds, milliseconds, |
| // microseconds, nanoseconds). |
| const TimeDurationRecord& time = dur.time_duration; |
| DateTimeRecordCommon 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<DateTimeRecordCommon>()); |
| // 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<DateTimeRecordCommon>()); |
| // 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<DateTimeRecordCommon>()); |
| // 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); |
| } |
| |
| 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<Object> relative_to_obj, |
| const TimeDurationRecord& value, |
| const char* method_name) { |
| TEMPORAL_ENTER_FUNC(); |
| TimeDurationRecord duration = value; |
| |
| // 2. If Type(relativeTo) is Object and relativeTo has an |
| // [[InitializedTemporalZonedDateTime]] internal slot, then |
| if (relative_to_obj->IsJSTemporalZonedDateTime()) { |
| 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<TimeDurationRecord>()); |
| // b. Set nanoseconds to endNs − relativeTo.[[Nanoseconds]]. |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, end_ns, |
| BigInt::Subtract(isolate, end_ns, |
| handle(relative_to->nanoseconds(), isolate)), |
| Nothing<TimeDurationRecord>()); |
| duration.nanoseconds = end_ns->AsInt64(); |
| // 3. Else, |
| } else { |
| // a. Set nanoseconds to ℤ(! TotalDurationNanoseconds(days, hours, minutes, |
| // seconds, milliseconds, microseconds, nanoseconds, 0)). |
| duration.nanoseconds = TotalDurationNanoseconds(isolate, duration, 0); |
| } |
| // 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, duration.nanoseconds, relative_to_obj, |
| method_name), |
| Nothing<TimeDurationRecord>()); |
| duration.days = result.days; |
| // b. Set days to result.[[Days]]. |
| // c. Set nanoseconds to result.[[Nanoseconds]]. |
| duration.nanoseconds = result.nanoseconds; |
| // 5. Else, |
| } else { |
| // a. Set days to 0. |
| duration.days = 0; |
| } |
| // 6. Set hours, minutes, seconds, milliseconds, and microseconds to 0. |
| duration.hours = duration.minutes = duration.seconds = duration.milliseconds = |
| duration.microseconds = 0; |
| // 7. If nanoseconds < 0, let sign be −1; else, let sign be 1. |
| int32_t sign = (duration.nanoseconds < 0) ? -1 : 1; |
| // 8. Set nanoseconds to abs(nanoseconds). |
| duration.nanoseconds = std::abs(duration.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). |
| duration.microseconds = std::floor(duration.nanoseconds / 1000); |
| // b. Set nanoseconds to nanoseconds modulo 1000. |
| duration.nanoseconds = modulo(duration.nanoseconds, 1000); |
| // c. Set milliseconds to floor(microseconds / 1000). |
| duration.milliseconds = std::floor(duration.microseconds / 1000); |
| // d. Set microseconds to microseconds modulo 1000. |
| duration.microseconds = modulo(duration.microseconds, 1000); |
| // e. Set seconds to floor(milliseconds / 1000). |
| duration.seconds = std::floor(duration.milliseconds / 1000); |
| // f. Set milliseconds to milliseconds modulo 1000. |
| duration.milliseconds = modulo(duration.milliseconds, 1000); |
| // g. Set minutes to floor(seconds, 60). |
| duration.minutes = std::floor(duration.seconds / 60); |
| // h. Set seconds to seconds modulo 60. |
| duration.seconds = modulo(duration.seconds, 60); |
| // i. Set hours to floor(minutes / 60). |
| duration.hours = std::floor(duration.minutes / 60); |
| // j. Set minutes to minutes modulo 60. |
| duration.minutes = modulo(duration.minutes, 60); |
| break; |
| // 10. Else if largestUnit is "minute", then |
| case Unit::kMinute: |
| // a. Set microseconds to floor(nanoseconds / 1000). |
| duration.microseconds = std::floor(duration.nanoseconds / 1000); |
| // b. Set nanoseconds to nanoseconds modulo 1000. |
| duration.nanoseconds = modulo(duration.nanoseconds, 1000); |
| // c. Set milliseconds to floor(microseconds / 1000). |
| duration.milliseconds = std::floor(duration.microseconds / 1000); |
| // d. Set microseconds to microseconds modulo 1000. |
| duration.microseconds = modulo(duration.microseconds, 1000); |
| // e. Set seconds to floor(milliseconds / 1000). |
| duration.seconds = std::floor(duration.milliseconds / 1000); |
| // f. Set milliseconds to milliseconds modulo 1000. |
| duration.milliseconds = modulo(duration.milliseconds, 1000); |
| // g. Set minutes to floor(seconds / 60). |
| duration.minutes = std::floor(duration.seconds / 60); |
| // h. Set seconds to seconds modulo 60. |
| duration.seconds = modulo(duration.seconds, 60); |
| break; |
| // 11. Else if largestUnit is "second", then |
| case Unit::kSecond: |
| // a. Set microseconds to floor(nanoseconds / 1000). |
| duration.microseconds = std::floor(duration.nanoseconds / 1000); |
| // b. Set nanoseconds to nanoseconds modulo 1000. |
| duration.nanoseconds = modulo(duration.nanoseconds, 1000); |
| // c. Set milliseconds to floor(microseconds / 1000). |
| duration.milliseconds = std::floor(duration.microseconds / 1000); |
| // d. Set microseconds to microseconds modulo 1000. |
| duration.microseconds = modulo(duration.microseconds, 1000); |
| // e. Set seconds to floor(milliseconds / 1000). |
| duration.seconds = std::floor(duration.milliseconds / 1000); |
| // f. Set milliseconds to milliseconds modulo 1000. |
| duration.milliseconds = modulo(duration.milliseconds, 1000); |
| break; |
| // 12. Else if largestUnit is "millisecond", then |
| case Unit::kMillisecond: |
| // a. Set microseconds to floor(nanoseconds / 1000). |
| duration.microseconds = std::floor(duration.nanoseconds / 1000); |
| // b. Set nanoseconds to nanoseconds modulo 1000. |
| duration.nanoseconds = modulo(duration.nanoseconds, 1000); |
| // c. Set milliseconds to floor(microseconds / 1000). |
| duration.milliseconds = std::floor(duration.microseconds / 1000); |
| // d. Set microseconds to microseconds modulo 1000. |
| duration.microseconds = modulo(duration.microseconds, 1000); |
| break; |
| // 13. Else if largestUnit is "microsecond", then |
| case Unit::kMicrosecond: |
| // a. Set microseconds to floor(nanoseconds / 1000). |
| duration.microseconds = std::floor(duration.nanoseconds / 1000); |
| // b. Set nanoseconds to nanoseconds modulo 1000. |
| duration.nanoseconds = modulo(duration.nanoseconds, 1000); |
| break; |
| // 15. Else, |
| case Unit::kNanosecond: |
| // a. Assert: largestUnit is "nanosecond". |
| break; |
| case Unit::kAuto: |
| case Unit::kNotPresent: |
| UNREACHABLE(); |
| } |
| // 15. Return ? CreateTimeDurationRecord(days, hours × sign, minutes × sign, |
| // seconds × sign, milliseconds × sign, microseconds × sign, nanoseconds × |
| // sign). |
| return TimeDurationRecord::Create( |
| isolate, duration.days, duration.hours * sign, duration.minutes * sign, |
| duration.seconds * sign, duration.milliseconds * sign, |
| duration.microseconds * sign, duration.nanoseconds * sign); |
| } |
| |
| // #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) |
| .ToHandleChecked(); |
| } |
| // 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) |
| .ToHandleChecked(); |
| } |
| |
| // #sec-temporal-nanosecondstodays |
| Maybe<NanosecondsToDaysResult> NanosecondsToDays(Isolate* isolate, |
| double nanoseconds, |
| Handle<Object> relative_to_obj, |
| const char* method_name) { |
| return NanosecondsToDays(isolate, BigInt::FromInt64(isolate, nanoseconds), |
| relative_to_obj, method_name); |
| } |
| |
| Maybe<NanosecondsToDaysResult> NanosecondsToDays(Isolate* isolate, |
| Handle<BigInt> nanoseconds, |
| Handle<Object> relative_to_obj, |
| const char* method_name) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| // 1. Assert: Type(nanoseconds) is BigInt. |
| // 2. Set nanoseconds to ℝ(nanoseconds). |
| // 3. Let sign be ! ℝ(Sign(𝔽(nanoseconds))). |
| ComparisonResult compare_result = |
| BigInt::CompareToBigInt(nanoseconds, BigInt::FromInt64(isolate, 0)); |
| double sign = CompareResultToSign(compare_result); |
| // 4. Let dayLengthNs be 8.64 × 10^13. |
| Handle<BigInt> day_length_ns = BigInt::FromInt64(isolate, 86400000000000LLU); |
| // 5. If sign is 0, then |
| if (sign == 0) { |
| // a. Return the new Record { [[Days]]: 0, [[Nanoseconds]]: 0, |
| // [[DayLength]]: dayLengthNs }. |
| NanosecondsToDaysResult result({0, 0, day_length_ns->AsInt64()}); |
| return Just(result); |
| } |
| // 6. If Type(relativeTo) is not Object or relativeTo does not have an |
| // [[InitializedTemporalZonedDateTime]] internal slot, then |
| if (!relative_to_obj->IsJSTemporalZonedDateTime()) { |
| // Return the Record { |
| // [[Days]]: the integral part of nanoseconds / dayLengthNs, |
| // [[Nanoseconds]]: (abs(nanoseconds) modulo dayLengthNs) × sign, |
| // [[DayLength]]: dayLengthNs }. |
| Handle<BigInt> days_bigint; |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, days_bigint, |
| BigInt::Divide(isolate, nanoseconds, day_length_ns), |
| Nothing<NanosecondsToDaysResult>()); |
| |
| if (sign < 0) { |
| nanoseconds = BigInt::UnaryMinus(isolate, nanoseconds); |
| } |
| |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, nanoseconds, |
| BigInt::Remainder(isolate, nanoseconds, day_length_ns), |
| Nothing<NanosecondsToDaysResult>()); |
| NanosecondsToDaysResult result( |
| {static_cast<double>(days_bigint->AsInt64()), |
| static_cast<double>(nanoseconds->AsInt64() * sign), |
| day_length_ns->AsInt64()}); |
| return Just(result); |
| } |
| Handle<JSTemporalZonedDateTime> relative_to = |
| Handle<JSTemporalZonedDateTime>::cast(relative_to_obj); |
| // 7. Let startNs be ℝ(relativeTo.[[Nanoseconds]]). |
| Handle<BigInt> start_ns = handle(relative_to->nanoseconds(), isolate); |
| // 8. Let startInstant be ! CreateTemporalInstant(ℤ(sartNs)). |
| Handle<JSTemporalInstant> start_instant = |
| temporal::CreateTemporalInstant( |
| isolate, handle(relative_to->nanoseconds(), isolate)) |
| .ToHandleChecked(); |
| |
| // 9. 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>()); |
| |
| // 10. 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>()); |
| |
| // 11. Let endInstant be ! CreateTemporalInstant(ℤ(endNs)). |
| Handle<JSTemporalInstant> end_instant = |
| temporal::CreateTemporalInstant(isolate, end_ns).ToHandleChecked(); |
| // 12. 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>()); |
| |
| // 13. 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"). |
| 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, relative_to, method_name), |
| Nothing<NanosecondsToDaysResult>()); |
| |
| // 14. Let days be dateDifference.[[Days]]. |
| double days = date_difference.time_duration.days; |
| |
| // 15. 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>()); |
| |
| // 16. 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>()); |
| } |
| } |
| |
| // 17. Set nanoseconds to endNs − intermediateNs. |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, nanoseconds, BigInt::Subtract(isolate, end_ns, intermediate_ns), |
| Nothing<NanosecondsToDaysResult>()); |
| |
| // 18. Let done be false. |
| bool done = false; |
| |
| // 19. 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. |
| Handle<BigInt> day_length_ns; |
| 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 |
| compare_result = BigInt::CompareToBigInt(nanoseconds, day_length_ns); |
| if (sign * CompareResultToSign(compare_result) >= 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, |
| static_cast<double>(nanoseconds->AsInt64()), |
| std::abs(day_length_ns->AsInt64())}); |
| return Just(result); |
| } |
| |
| // #sec-temporal-differenceisodatetime |
| Maybe<DurationRecord> DifferenceISODateTime( |
| Isolate* isolate, const DateTimeRecordCommon& date_time1, |
| const DateTimeRecordCommon& date_time2, Handle<JSReceiver> calendar, |
| Unit largest_unit, Handle<Object> options, const char* method_name) { |
| TEMPORAL_ENTER_FUNC(); |
| DurationRecord result; |
| // 2. Assert: Type(options) is Object or Undefined. |
| DCHECK(options->IsJSReceiver() || options->IsUndefined()); |
| // 3. Let timeDifference be ! DifferenceTime(h1, min1, s1, ms1, mus1, ns1, h2, |
| // min2, s2, ms2, mus2, ns2). |
| TimeDurationRecord time_difference; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, time_difference, |
| DifferenceTime(isolate, date_time1.time, date_time2.time), |
| Nothing<DurationRecord>()); |
| |
| result.time_duration = time_difference; |
| result.time_duration.days = 0; |
| |
| // 4. Let timeSign be ! DurationSign(0, 0, 0, timeDifference.[[Days]], |
| // timeDifference.[[Hours]], timeDifference.[[Minutes]], |
| // timeDifference.[[Seconds]], timeDifference.[[Milliseconds]], |
| // timeDifference.[[Microseconds]], timeDifference.[[Nanoseconds]]). |
| 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 balanceResult be ! BalanceISODate(y1, mon1, d1 + |
| // timeDifference.[[Days]]). |
| DateRecordCommon balanced = BalanceISODate( |
| isolate, |
| {date_time1.date.year, date_time1.date.month, |
| date_time1.date.day + static_cast<int32_t>(time_difference.days)}); |
| |
| // 7. If timeSign is -dateSign, then |
| if (time_sign == -date_sign) { |
| // a. Set balanceResult be ! BalanceISODate(balanceResult.[[Year]], |
| // balanceResult.[[Month]], balanceResult.[[Day]] - timeSign). |
| balanced.day -= time_sign; |
| balanced = BalanceISODate(isolate, balanced); |
| // b. Set timeDifference to ? BalanceDuration(-timeSign, |
| // timeDifference.[[Hours]], timeDifference.[[Minutes]], |
| // timeDifference.[[Seconds]], timeDifference.[[Milliseconds]], |
| // timeDifference.[[Microseconds]], timeDifference.[[Nanoseconds]], |
| // largestUnit). |
| |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result.time_duration, |
| BalanceDuration( |
| isolate, largest_unit, |
| {-time_sign, time_difference.hours, time_difference.minutes, |
| time_difference.seconds, time_difference.milliseconds, |
| time_difference.microseconds, time_difference.nanoseconds}, |
| method_name), |
| Nothing<DurationRecord>()); |
| } |
| // 8. Let date1 be ? CreateTemporalDate(balanceResult.[[Year]], |
| // balanceResult.[[Month]], balanceResult.[[Day]], calendar). |
| Handle<JSTemporalPlainDate> date1; |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, date1, CreateTemporalDate(isolate, balanced, calendar), |
| Nothing<DurationRecord>()); |
| // 9. Let date2 be ? CreateTemporalDate(y2, mon2, d2, calendar). |
| Handle<JSTemporalPlainDate> date2; |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, date2, CreateTemporalDate(isolate, date_time2.date, calendar), |
| Nothing<DurationRecord>()); |
| // 10. Let dateLargestUnit be ! LargerOfTwoTemporalUnits("day", largestUnit). |
| Unit date_largest_unit = |
| LargerOfTwoTemporalUnits(isolate, Unit::kDay, largest_unit); |
| |
| // 11. Let untilOptions be ? MergeLargestUnitOption(options, dateLargestUnit). |
| Handle<JSObject> 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). |
| |
| TimeDurationRecord balance_result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, balance_result, |
| BalanceDuration( |
| isolate, largest_unit, |
| {date_difference->days().Number(), time_difference.hours, |
| time_difference.minutes, time_difference.seconds, |
| time_difference.milliseconds, time_difference.microseconds, |
| time_difference.nanoseconds}, |
| method_name), |
| Nothing<DurationRecord>()); |
| |
| result.time_duration = balance_result; |
| // 14. Return the Record { [[Years]]: dateDifference.[[Years]], [[Months]]: |
| // dateDifference.[[Months]], [[Weeks]]: dateDifference.[[Weeks]], [[Days]]: |
| // balanceResult.[[Days]], [[Hours]]: balanceResult.[[Hours]], [[Minutes]]: |
| // balanceResult.[[Minutes]], [[Seconds]]: balanceResult.[[Seconds]], |
| // [[Milliseconds]]: balanceResult.[[Milliseconds]], [[Microseconds]]: |
| // balanceResult.[[Microseconds]], [[Nanoseconds]]: |
| // balanceResult.[[Nanoseconds]] }. |
| result.years = date_difference->years().Number(); |
| result.months = date_difference->months().Number(); |
| result.weeks = date_difference->weeks().Number(); |
| return Just(result); |
| } |
| |
| // #sec-temporal-addinstant |
| MaybeHandle<BigInt> AddInstant(Isolate* isolate, |
| Handle<BigInt> epoch_nanoseconds, |
| const TimeDurationRecord& addend) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| // 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ℤ. |
| Handle<BigInt> result; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, result, |
| BigInt::Add(isolate, epoch_nanoseconds, |
| BigInt::FromInt64(isolate, addend.nanoseconds)), |
| BigInt); |
| Handle<BigInt> temp; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, temp, |
| BigInt::Multiply(isolate, BigInt::FromInt64(isolate, addend.microseconds), |
| BigInt::FromInt64(isolate, 1000)), |
| BigInt); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, result, |
| BigInt::Add(isolate, result, temp), BigInt); |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, temp, |
| BigInt::Multiply(isolate, BigInt::FromInt64(isolate, addend.milliseconds), |
| BigInt::FromInt64(isolate, 1000000)), |
| BigInt); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, result, |
| BigInt::Add(isolate, result, temp), BigInt); |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, temp, |
| BigInt::Multiply(isolate, BigInt::FromInt64(isolate, addend.seconds), |
| BigInt::FromInt64(isolate, 1000000000)), |
| BigInt); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, result, |
| BigInt::Add(isolate, result, temp), BigInt); |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, temp, |
| BigInt::Multiply(isolate, BigInt::FromInt64(isolate, addend.minutes), |
| BigInt::FromInt64(isolate, 1000000000)), |
| BigInt); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, temp, |
| BigInt::Multiply(isolate, temp, BigInt::FromInt64(isolate, 60)), BigInt); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, result, |
| BigInt::Add(isolate, result, temp), BigInt); |
| |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, temp, |
| BigInt::Multiply(isolate, BigInt::FromInt64(isolate, addend.hours), |
| BigInt::FromInt64(isolate, 1000000000)), |
| BigInt); |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, temp, |
| BigInt::Multiply(isolate, temp, BigInt::FromInt64(isolate, 3600)), |
| BigInt); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, result, |
| BigInt::Add(isolate, result, temp), BigInt); |
| |
| // 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::CompareToDouble(epoch_nanoseconds, kNsMinInstant) == |
| ComparisonResult::kLessThan || |
| BigInt::CompareToDouble(epoch_nanoseconds, kNsMaxInstant) == |
| ComparisonResult::kGreaterThan) { |
| // a. Return false. |
| return false; |
| } |
| return true; |
| } |
| |
| Handle<BigInt> GetEpochFromISOParts(Isolate* isolate, |
| const DateTimeRecordCommon& 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; |
| } |
| |
| // #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; |
| 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))); |
| } |
| |
| // #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 TimeRecordCommon& 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 DateRecordCommon& 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 DateRecordCommon& one, |
| const DateRecordCommon& 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 TimeRecordCommon& time1, |
| const TimeRecordCommon& time2); |
| |
| // #sec-temporal-compareisodatetime |
| int32_t CompareISODateTime(const DateTimeRecordCommon& one, |
| const DateTimeRecordCommon& 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 |
| DateTimeRecordCommon BalanceTime(const TimeRecordCommon& input) { |
| TEMPORAL_ENTER_FUNC(); |
| TimeRecordCommon time(input); |
| |
| // 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); |
| // 3. Set nanosecond to nanosecond modulo 1000. |
| time.nanosecond = modulo(time.nanosecond, 1000); |
| // 4. Set millisecond to millisecond + floor(microsecond / 1000). |
| time.millisecond += std::floor(time.microsecond / 1000); |
| // 5. Set microsecond to microsecond modulo 1000. |
| time.microsecond = modulo(time.microsecond, 1000); |
| // 6. Set second to second + floor(millisecond / 1000). |
| time.second += std::floor(time.millisecond / 1000); |
| // 7. Set millisecond to millisecond modulo 1000. |
| time.millisecond = modulo(time.millisecond, 1000); |
| // 8. Set minute to minute + floor(second / 60). |
| time.minute += std::floor(time.second / 60); |
| // 9. Set second to second modulo 60. |
| time.second = modulo(time.second, 60); |
| // 10. Set hour to hour + floor(minute / 60). |
| time.hour += std::floor(time.minute / 60); |
| // 11. Set minute to minute modulo 60. |
| time.minute = modulo(time.minute, 60); |
| // 12. Let days be floor(hour / 24). |
| DateRecordCommon date = {0, 0, |
| static_cast<int32_t>(std::floor(time.hour / 24))}; |
| // 13. Set hour to hour modulo 24. |
| time.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 {date, time}; |
| } |
| |
| // #sec-temporal-differencetime |
| Maybe<TimeDurationRecord> DifferenceTime(Isolate* isolate, |
| const TimeRecordCommon& time1, |
| const TimeRecordCommon& 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). |
| DateTimeRecordCommon bt = |
| BalanceTime({static_cast<int32_t>(dur.hours * sign), |
| static_cast<int32_t>(dur.minutes * sign), |
| static_cast<int32_t>(dur.seconds * sign), |
| static_cast<int32_t>(dur.milliseconds * sign), |
| static_cast<int32_t>(dur.microseconds * sign), |
| static_cast<int32_t>(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 |
| DateTimeRecordCommon AddTime(Isolate* isolate, const TimeRecordCommon& 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 + static_cast<int32_t>(addend.hours), |
| // 3. Let minute be minute + minutes. |
| time.minute + static_cast<int32_t>(addend.minutes), |
| // 4. Let second be second + seconds. |
| time.second + static_cast<int32_t>(addend.seconds), |
| // 5. Let millisecond be millisecond + milliseconds. |
| time.millisecond + static_cast<int32_t>(addend.milliseconds), |
| // 6. Let microsecond be microsecond + microseconds. |
| time.microsecond + static_cast<int32_t>(addend.microseconds), |
| // 7. Let nanosecond be nanosecond + nanoseconds. |
| time.nanosecond + static_cast<int32_t>(addend.nanoseconds)}); |
| // 8. Return ! BalanceTime(hour, minute, second, millisecond, microsecond, |
| // nanosecond). |
| } |
| |
| // #sec-temporal-totaldurationnanoseconds |
| double TotalDurationNanoseconds(Isolate* isolate, |
| const TimeDurationRecord& value, |
| double offset_shift) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| TimeDurationRecord duration(value); |
| |
| // 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. |
| duration.nanoseconds -= offset_shift; |
| } |
| |
| // 4. Set hours to ℝ(hours) + ℝ(days) × 24. |
| duration.hours += duration.days * 24; |
| |
| // 5. Set minutes to ℝ(minutes) + hours × 60. |
| duration.minutes += duration.hours * 60; |
| |
| // 6. Set seconds to ℝ(seconds) + minutes × 60. |
| duration.seconds += duration.minutes * 60; |
| |
| // 7. Set milliseconds to ℝ(milliseconds) + seconds × 1000. |
| duration.milliseconds += duration.seconds * 1000; |
| |
| // 8. Set microseconds to ℝ(microseconds) + milliseconds × 1000. |
| duration.microseconds += duration.milliseconds * 1000; |
| |
| // 9. Return nanoseconds + microseconds × 1000. |
| return duration.nanoseconds + duration.microseconds * 1000; |
| } |
| |
| Maybe<DateRecordCommon> RegulateISODate(Isolate* isolate, ShowOverflow overflow, |
| const DateRecordCommon& date); |
| Maybe<int32_t> ResolveISOMonth(Isolate* isolate, Handle<JSReceiver> fields); |
| |
| // #sec-temporal-isomonthdayfromfields |
| Maybe<DateRecordCommon> ISOMonthDayFromFields(Isolate* isolate, |
| Handle<JSReceiver> fields, |
| Handle<JSReceiver> options, |
| const char* method_name) { |
| Factory* factory = isolate->factory(); |
| // 1. Assert: Type(fields) is Object. |
| // 2. Let overflow be ? ToTemporalOverflow(options). |
| ShowOverflow overflow; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, overflow, ToTemporalOverflow(isolate, options, method_name), |
| Nothing<DateRecordCommon>()); |
| |
| // 3. Set fields to ? PrepareTemporalFields(fields, « "day", "month", |
| // "monthCode", "year" », «»). |
| Handle<FixedArray> field_names = 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()); |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, fields, |
| PrepareTemporalFields(isolate, fields, field_names, |
| RequiredFields::kNone), |
| Nothing<DateRecordCommon>()); |
| // 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 (!month_obj->IsUndefined(isolate) && |
| month_code_obj->IsUndefined(isolate) && year_obj->IsUndefined(isolate)) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), |
| Nothing<DateRecordCommon>()); |
| } |
| // 8. Set month to ? ResolveISOMonth(fields). |
| DateRecordCommon result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, result.month, |
| ResolveISOMonth(isolate, fields), |
| Nothing<DateRecordCommon>()); |
| |
| // 9. Let day be ! Get(fields, "day"). |
| Handle<Object> day_obj = |
| JSReceiver::GetProperty(isolate, fields, factory->day_string()) |
| .ToHandleChecked(); |
| // 10. If day is undefined, throw a TypeError exception. |
| if (day_obj->IsUndefined(isolate)) { |
| THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), |
| Nothing<DateRecordCommon>()); |
| } |
| result.day = FastD2I(floor(day_obj->Number())); |
| // 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 (month_code_obj->IsUndefined(isolate)) { |
| result.year = FastD2I(floor(year_obj->Number())); |
| // 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<DateRecordCommon>()); |
| // 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 (new_target->IsUndefined()) { |
| // 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}}); |
| } |
| |
| // #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 (item->IsJSTemporalDuration()) { |
| // 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, |
| {duration->years().Number(), |
| duration->months().Number(), |
| duration->weeks().Number(), |
| {duration->days().Number(), duration->hours().Number(), |
| duration->minutes().Number(), duration->seconds().Number(), |
| duration->milliseconds().Number(), duration->microseconds().Number(), |
| duration->nanoseconds().Number()}}); |
| } |
| // 2. Return ? ToTemporalDuration(item). |
| return temporal::ToTemporalDuration(isolate, item, "Temporal.Duration.from"); |
| } |
| |
| 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 (!temporal_duration_like_obj->IsJSReceiver()) { |
| // 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 (!val->IsUndefined()) { |
| // 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, |
| {duration->years().Number(), |
| duration->months().Number(), |
| duration->weeks().Number(), |
| {duration->days().Number(), duration->hours().Number(), |
| duration->minutes().Number(), duration->seconds().Number(), |
| duration->milliseconds().Number(), |
| duration->microseconds().Number(), |
| duration->nanoseconds().Number()}}), |
| 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, {duration->years().Number(), |
| duration->months().Number(), |
| duration->weeks().Number(), |
| {duration->days().Number(), duration->hours().Number(), |
| duration->minutes().Number(), duration->seconds().Number(), |
| duration->milliseconds().Number(), |
| duration->microseconds().Number(), |
| duration->nanoseconds().Number()}})), |
| 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, |
| {duration->years().Number(), |
| duration->months().Number(), |
| duration->weeks().Number(), |
| {duration->days().Number(), duration->hours().Number(), |
| duration->minutes().Number(), duration->seconds().Number(), |
| duration->milliseconds().Number(), duration->microseconds().Number(), |
| duration->nanoseconds().Number()}}); |
| return sign == 0 ? isolate->factory()->true_value() |
| : isolate->factory()->false_value(); |
| } |
| |
| namespace { |
| |
| // #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, |
| {-duration->years().Number(), |
| -duration->months().Number(), |
| -duration->weeks().Number(), |
| {-duration->days().Number(), -duration->hours().Number(), |
| -duration->minutes().Number(), -duration->seconds().Number(), |
| -duration->milliseconds().Number(), |
| -duration->microseconds().Number(), |
| -duration->nanoseconds().Number()}}) |
| .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(duration->years().Number()), |
| std::abs(duration->months().Number()), |
| std::abs(duration->weeks().Number()), |
| {std::abs(duration->days().Number()), |
| std::abs(duration->hours().Number()), |
| std::abs(duration->minutes().Number()), |
| std::abs(duration->seconds().Number()), |
| std::abs(duration->milliseconds().Number()), |
| std::abs(duration->microseconds().Number()), |
| std::abs(duration->nanoseconds().Number())}}); |
| } |
| |
| // #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"). |
| |
| return TemporalDurationToString(isolate, duration, 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"). |
| |
| // TODO(ftang) Implement #sup-temporal.duration.prototype.tolocalestring |
| return TemporalDurationToString(isolate, duration, Precision::kAuto); |
| } |
| |
| // #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 (new_target->IsUndefined(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 DateRecordCommon& date) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| // 1. Assert: year is an integer. |
| // 2. Assert: month is an integer. |
| // 3. Assert: day is an integer. |
| // 4. Let date be the date given by year, month, and day. |
| // 5. Return date's ordinal date in the year according to ISO-8601. |
| // 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 temporal_date_like->IsJSTemporalPlainDate() || |
| temporal_date_like->IsJSTemporalPlainDateTime() || |
| temporal_date_like->IsJSTemporalPlainYearMonth(); |
| } |
| |
| // #sec-temporal-toisodayofweek |
| int32_t ToISODayOfWeek(Isolate* isolate, int32_t year, int32_t month, |
| int32_t day) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| // 1. Assert: year is an integer. |
| // 2. Assert: month is an integer. |
| // 3. Assert: day is an integer. |
| // 4. Let date be the date given by year, month, and day. |
| // 5. Return date's day of the week according to ISO-8601. |
| // 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. |
| int32_t weekday = isolate->date_cache()->Weekday( |
| isolate->date_cache()->DaysFromYearMonth(year, month - 1) + day - 1); |
| // Note: In ISO 8601, Sun: weekday=7 Mon: weekday=1 |
| // In DateCache API, Sun: weekday=0 Mon: weekday=1 |
| return weekday == 0 ? 7 : weekday; |
| } |
| |
| // #sec-temporal-regulateisodate |
| Maybe<DateRecordCommon> RegulateISODate(Isolate* isolate, ShowOverflow overflow, |
| const DateRecordCommon& 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<DateRecordCommon>()); |
| } |
| // b. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day |
| // }. |
| return Just(date); |
| // 4. If overflow is "constrain", then |
| case ShowOverflow::kConstrain: |
| DateRecordCommon 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 (month_code_obj->IsUndefined(isolate)) { |
| // a. If month is undefined, throw a TypeError exception. |
| if (month_obj->IsUndefined(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(month_obj->IsSmi() || month_obj->IsHeapNumber()); |
| return Just(FastD2I(month_obj->Number())); |
| } |
| // 4. Assert: Type(monthCode) is String. |
| DCHECK(month_code_obj->IsString()); |
| 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 (!month_obj->IsUndefined() && |
| FastD2I(month_obj->Number()) != 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<DateRecordCommon> ISODateFromFields(Isolate* isolate, |
| Handle<JSReceiver> fields, |
| Handle<JSReceiver> options, |
| const char* method_name) { |
| Factory* factory = isolate->factory(); |
| |
| // 1. Assert: Type(fields) is Object. |
| // 2. Let overflow be ? ToTemporalOverflow(options). |
| ShowOverflow overflow; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, overflow, ToTemporalOverflow(isolate, options, method_name), |
| Nothing<DateRecordCommon>()); |
| // 3. Set fields to ? PrepareTemporalFields(fields, « "day", "month", |
| // "monthCode", "year" », «»). |
| Handle<FixedArray> field_names = DayMonthMonthCodeYearInFixedArray(isolate); |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, fields, |
| PrepareTemporalFields(isolate, fields, field_names, |
| RequiredFields::kNone), |
| Nothing<DateRecordCommon>()); |
| |
| // 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 (year_obj->IsUndefined(isolate)) { |
| THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), |
| Nothing<DateRecordCommon>()); |
| } |
| // Note: "year" in fields is always converted by |
| // ToIntegerThrowOnInfinity inside the PrepareTemporalFields above. |
| // Therefore the year_obj is always an integer. |
| DCHECK(year_obj->IsSmi() || year_obj->IsHeapNumber()); |
| |
| // 6. Let month be ? ResolveISOMonth(fields). |
| int32_t month; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, month, |
| ResolveISOMonth(isolate, fields), |
| Nothing<DateRecordCommon>()); |
| |
| // 7. Let day be ! Get(fields, "day"). |
| Handle<Object> day_obj = |
| JSReceiver::GetProperty(isolate, fields, factory->day_string()) |
| .ToHandleChecked(); |
| // 8. If day is undefined, throw a TypeError exception. |
| if (day_obj->IsUndefined(isolate)) { |
| THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), |
| Nothing<DateRecordCommon>()); |
| } |
| // Note: "day" in fields is always converted by |
| // ToIntegerThrowOnInfinity inside the PrepareTemporalFields above. |
| // Therefore the day_obj is always an integer. |
| DCHECK(day_obj->IsSmi() || day_obj->IsHeapNumber()); |
| // 9. Return ? RegulateISODate(year, month, day, overflow). |
| return RegulateISODate( |
| isolate, overflow, |
| {FastD2I(year_obj->Number()), month, FastD2I(day_obj->Number())}); |
| } |
| |
| // #sec-temporal-addisodate |
| Maybe<DateRecordCommon> AddISODate(Isolate* isolate, |
| const DateRecordCommon& 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). |
| DateRecordCommon 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<DateRecordCommon>()); |
| |
| // 5. Set days to days + 7 × weeks. |
| // 6. Let d be intermediate.[[Day]] + days. |
| intermediate.day += duration.days + 7 * duration.weeks; |
| // 7. Let intermediate be ! BalanceISODate(intermediate.[[Year]], |
| // intermediate.[[Month]], d). |
| intermediate = BalanceISODate(isolate, intermediate); |
| // 8. Return ? RegulateISODate(intermediate.[[Year]], intermediate.[[Month]], |
| // intermediate.[[Day]], overflow). |
| return RegulateISODate(isolate, overflow, intermediate); |
| } |
| |
| // #sec-temporal-differenceisodate |
| Maybe<DateDurationRecord> DifferenceISODate(Isolate* isolate, |
| const DateRecordCommon& date1, |
| const DateRecordCommon& 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 |
| // }. |
| DateRecordCommon start = date1; |
| // d. Let end be the new Record { [[Year]]: y2, [[Month]]: m2, [[Day]]: |
| // d2 }. |
| DateRecordCommon 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"). |
| DateRecordCommon 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]]. |
| CHECK_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: { |
| DateRecordCommon 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<DateRecordCommon> ISOYearMonthFromFields(Isolate* isolate, |
| Handle<JSReceiver> fields, |
| Handle<JSReceiver> options, |
| const char* method_name) { |
| Factory* factory = isolate->factory(); |
| // 1. Assert: Type(fields) is Object. |
| // 2. Let overflow be ? ToTemporalOverflow(options). |
| ShowOverflow overflow; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, overflow, ToTemporalOverflow(isolate, options, method_name), |
| Nothing<DateRecordCommon>()); |
| // 3. 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<DateRecordCommon>()); |
| |
| // 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 (year_obj->IsUndefined(isolate)) { |
| THROW_NEW_ERROR_RETURN_VALUE(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), |
| Nothing<DateRecordCommon>()); |
| } |
| DateRecordCommon result; |
| result.year = FastD2I(floor(year_obj->Number())); |
| // 6. Let month be ? ResolveISOMonth(fields). |
| int32_t month; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, month, |
| ResolveISOMonth(isolate, fields), |
| Nothing<DateRecordCommon>()); |
| // 7. Let result be ? RegulateISOYearMonth(year, month, overflow). |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result.month, RegulateISOYearMonth(isolate, overflow, month), |
| Nothing<DateRecordCommon>()); |
| // 8. Return the new Record { [[Year]]: result.[[Year]], [[Month]]: |
| // result.[[Month]], [[ReferenceISODay]]: 1 }. |
| result.day = 1; |
| return Just(result); |
| } |
| |
| } // 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, |
| {duration->days().Number(), duration->hours().Number(), |
| duration->minutes().Number(), duration->seconds().Number(), |
| duration->milliseconds().Number(), duration->microseconds().Number(), |
| duration->nanoseconds().Number()}, |
| method_name), |
| Handle<JSTemporalPlainDate>()); |
| DateRecordCommon 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()}, |
| {duration->years().Number(), duration->months().Number(), |
| duration->weeks().Number(), 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 (temporal_date_like->IsJSTemporalPlainDate()) { |
| year = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_year(); |
| } else if (temporal_date_like->IsJSTemporalPlainDateTime()) { |
| year = |
| Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_year(); |
| } else { |
| DCHECK(temporal_date_like->IsJSTemporalPlainYearMonth()); |
| 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 (temporal_date_like->IsJSTemporalPlainDate()) { |
| year = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_year(); |
| month = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_month(); |
| } else if (temporal_date_like->IsJSTemporalPlainDateTime()) { |
| year = |
| Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_year(); |
| month = |
| Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_month(); |
| } else { |
| DCHECK(temporal_date_like->IsJSTemporalPlainYearMonth()); |
| 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 (temporal_date_like->IsJSTemporalPlainDate()) { |
| year = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_year(); |
| } else if (temporal_date_like->IsJSTemporalPlainDateTime()) { |
| year = |
| Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_year(); |
| } else { |
| DCHECK(temporal_date_like->IsJSTemporalPlainYearMonth()); |
| 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 (temporal_date_like->IsJSTemporalPlainDate()) { |
| year = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_year(); |
| } else if (temporal_date_like->IsJSTemporalPlainDateTime()) { |
| year = |
| Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_year(); |
| } else { |
| DCHECK(temporal_date_like->IsJSTemporalPlainYearMonth()); |
| 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 (!fields_obj->IsJSReceiver()) { |
| 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). |
| DateRecordCommon 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 ? ToLargestTemporalUnit(options, « "hour", "minute", |
| // "second", "millisecond", "microsecond", "nanosecond" », "auto", "day"). |
| Unit largest_unit; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, largest_unit, |
| ToLargestTemporalUnit( |
| isolate, options, |
| std::set<Unit>({Unit::kHour, Unit::kMinute, Unit::kSecond, |
| Unit::kMillisecond, Unit::kMicrosecond, |
| Unit::kNanosecond}), |
| Unit::kAuto, Unit::kDay, method_name), |
| Handle<JSTemporalDuration>()); |
| |
| // 8. 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>()); |
| |
| // 9. 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 (!(temporal_date_like->IsJSTemporalPlainDate() || |
| temporal_date_like->IsJSTemporalPlainDateTime() || |
| temporal_date_like->IsJSTemporalPlainMonthDay())) { |
| // 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 (temporal_date_like->IsJSTemporalPlainDate()) { |
| day = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_day(); |
| } else if (temporal_date_like->IsJSTemporalPlainDateTime()) { |
| day = Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_day(); |
| } else { |
| DCHECK(temporal_date_like->IsJSTemporalPlainMonthDay()); |
| 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) || |
| temporal_date_like->IsJSTemporalPlainMonthDay())) { |
| // 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 (temporal_date_like->IsJSTemporalPlainDate()) { |
| month = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_month(); |
| } else if (temporal_date_like->IsJSTemporalPlainDateTime()) { |
| month = |
| Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_month(); |
| } else if (temporal_date_like->IsJSTemporalPlainMonthDay()) { |
| month = |
| Handle<JSTemporalPlainMonthDay>::cast(temporal_date_like)->iso_month(); |
| } else { |
| DCHECK(temporal_date_like->IsJSTemporalPlainYearMonth()); |
| month = |
| Handle<JSTemporalPlainYearMonth>::cast(temporal_date_like)->iso_month(); |
| } |
| IncrementalStringBuilder builder(isolate); |
| builder.AppendCharacter('M'); |
| if (month < 10) { |
| builder.AppendCharacter('0'); |
| } |
| builder.AppendInt(month); |
| |
| return builder.Finish(); |
| } |
| |
| // #sec-temporal.calendar.prototype.month |
| MaybeHandle<Smi> JSTemporalCalendar::Month(Isolate* isolate, |
| Handle<JSTemporalCalendar> calendar, |
| Handle<Object> temporal_date_like) { |
| // 4. If Type(temporalDateLike) is Object and temporalDateLike has an |
| // [[InitializedTemporalMonthDay]] internal slot, then |
| if (temporal_date_like->IsJSTemporalPlainMonthDay()) { |
| // 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 (temporal_date_like->IsJSTemporalPlainDate()) { |
| month = Handle<JSTemporalPlainDate>::cast(temporal_date_like)->iso_month(); |
| } else if (temporal_date_like->IsJSTemporalPlainDateTime()) { |
| month = |
| Handle<JSTemporalPlainDateTime>::cast(temporal_date_like)->iso_month(); |
| } else { |
| DCHECK(temporal_date_like->IsJSTemporalPlainYearMonth()); |
| 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 (!fields_obj->IsJSReceiver()) { |
| 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) { |
| DateRecordCommon 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 (!fields_obj->IsJSReceiver()) { |
| 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) { |
| DateRecordCommon 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.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 (new_target->IsUndefined(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 { |
| |
| // #sec-temporal-getianatimezonenexttransition |
| MaybeHandle<Object> GetIANATimeZoneNextTransition(Isolate* isolate, |
| Handle<BigInt> nanoseconds, |
| int32_t time_zone_index) { |
| #ifdef V8_INTL_SUPPORT |
| // TODO(ftang) Add support for non UTC timezone |
| DCHECK_EQ(time_zone_index, JSTemporalTimeZone::kUTCTimeZoneIndex); |
| return isolate->factory()->null_value(); |
| #else // V8_INTL_SUPPORT |
| return isolate->factory()->null_value(); |
| #endif // V8_INTL_SUPPORT |
| } |
| |
| // #sec-temporal-getianatimezoneprevioustransition |
| MaybeHandle<Object> GetIANATimeZonePreviousTransition( |
| Isolate* isolate, Handle<BigInt> nanoseconds, int32_t time_zone_index) { |
| #ifdef V8_INTL_SUPPORT |
| // TODO(ftang) Add support for non UTC timezone |
| DCHECK_EQ(time_zone_index, JSTemporalTimeZone::kUTCTimeZoneIndex); |
| return isolate->factory()->null_value(); |
| #else // V8_INTL_SUPPORT |
| return isolate->factory()->null_value(); |
| #endif // V8_INTL_SUPPORT |
| } |
| |
| MaybeHandle<Object> GetIANATimeZoneOffsetNanoseconds(Isolate* isolate, |
| Handle<BigInt> nanoseconds, |
| int32_t time_zone_index) { |
| #ifdef V8_INTL_SUPPORT |
| // TODO(ftang) Handle offset for other timezone |
| if (time_zone_index == JSTemporalTimeZone::kUTCTimeZoneIndex) { |
| return handle(Smi::zero(), isolate); |
| } |
| UNREACHABLE(); |
| #else // V8_INTL_SUPPORT |
| 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 <MaybeHandle<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; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, transition_obj, |
| iana_func(isolate, handle(starting_point->nanoseconds(), isolate), |
| time_zone->time_zone_index()), |
| Object); |
| // 6. If transition is null, return null. |
| if (transition_obj->IsNull()) { |
| return isolate->factory()->null_value(); |
| } |
| DCHECK(transition_obj->IsBigInt()); |
| 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> GetIANATimeZoneEpochValueAsArrayOfInstant( |
| Isolate* isolate, int32_t time_zone_index, |
| const DateTimeRecordCommon& 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; |
| if (time_zone_index == JSTemporalTimeZone::kUTCTimeZoneIndex) { |
| fixed_array = factory->NewFixedArray(1); |
| // 7. For each value epochNanoseconds in possibleEpochNanoseconds, do |
| // a. Let instant be ! CreateTemporalInstant(epochNanoseconds). |
| Handle<JSTemporalInstant> instant = |
| temporal::CreateTemporalInstant(isolate, epoch_nanoseconds) |
| .ToHandleChecked(); |
| // b. Append instant to possibleInstants. |
| fixed_array->set(0, *instant); |
| } else { |
| #ifdef V8_INTL_SUPPORT |
| // TODO(ftang) put in 402 version of implementation calling intl classes. |
| UNIMPLEMENTED(); |
| #else |
| UNREACHABLE(); |
| #endif // V8_INTL_SUPPORT |
| } |
| |
| // 8. Return ! CreateArrayFromList(possibleInstants). |
| return factory->NewJSArrayWithElements(fixed_array); |
| } |
| |
| // #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); |
| DateTimeRecordCommon 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 instant be ! CreateTemporalInstant(epochNanoseconds − |
| // timeZone.[[OffsetNanoseconds]]). |
| Handle<JSTemporalInstant> instant = |
| temporal::CreateTemporalInstant( |
| isolate, |
| BigInt::Subtract( |
| isolate, epoch_nanoseconds, |
| BigInt::FromInt64(isolate, time_zone->offset_nanoseconds())) |
| .ToHandleChecked()) |
| .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 (new_target->IsUndefined()) { |
| 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 (temporal_time_obj->IsUndefined()) { |
| // 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, |
| ShowOverflow::kConstrain, |
| "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 (object->IsJSTemporalPlainDate() || object->IsJSTemporalPlainDateTime() || |
| object->IsJSTemporalPlainMonthDay() || object->IsJSTemporalPlainTime() || |
| object->IsJSTemporalPlainYearMonth() || |
| object->IsJSTemporalZonedDateTime()) { |
| // 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 (!calendar_property->IsUndefined()) { |
| // 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 (!time_zone_property->IsUndefined()) { |
| // 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(calendar, isolate->factory()->mergeFields_string()), |
| JSReceiver); |
| // 2. If mergeFields is undefined, then |
| if (merge_fields->IsUndefined()) { |
| // 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 (!result->IsJSReceiver()) { |
| 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 (!temporal_like_obj->IsJSReceiver()) { |
| // 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, temporal, 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 (item_obj->IsJSReceiver()) { |
| 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 (time_zone_like->IsUndefined()) { |
| // 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 (temporal_time_obj->IsUndefined()) { |
| // 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, |
| ShowOverflow::kConstrain, 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, |
| isolate->factory()->undefined_value()); |
| } |
| |
| // #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, |
| isolate->factory()->undefined_value()); |
| } |
| |
| // #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 (item->IsJSTemporalPlainDate()) { |
| // a. Perform ? ToTemporalOverflow(options). |
| MAYBE_RETURN( |
| ToTemporalOverflowForSideEffects(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) { |
| // TODO(ftang) Implement #sup-temporal.plaindate.prototype.tolocalestring |
| return TemporalDateToString(isolate, temporal_date, ShowCalendar::kAuto); |
| } |
| |
| // #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 (new_target->IsUndefined()) { |
| 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<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). |
| TimeRecordCommon time_result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, time_result, ToTemporalTimeRecord(isolate, fields, method_name), |
| Nothing<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<DateTimeRecord>()); |
| |
| // 3. Let overflow be ? ToTemporalOverflow(options). |
| ShowOverflow overflow; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, overflow, ToTemporalOverflow(isolate, options, method_name), |
| Nothing<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<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]] }. |
| |
| DateTimeRecord result = { |
| {temporal_date->iso_year(), temporal_date->iso_month(), |
| temporal_date->iso_day()}, |
| time_result, |
| Handle<String>()}; |
| return Just(result); |
| } |
| |
| // #sec-temporal-parsetemporaldatetimestring |
| Maybe<DateTimeRecord> 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<DateTimeRecord>()); |
| } |
| |
| // 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<DateTimeRecord>()); |
| } |
| |
| // 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(options->IsJSReceiver() || options->IsUndefined()); |
| |
| Factory* factory = isolate->factory(); |
| Handle<JSReceiver> calendar; |
| DateTimeRecord result; |
| // 2. If Type(item) is Object, then |
| if (item_obj->IsJSReceiver()) { |
| Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); |
| // a. If item has an [[InitializedTemporalDateTime]] internal slot, then |
| // i. Return item. |
| if (item->IsJSTemporalPlainDateTime()) { |
| return Handle<JSTemporalPlainDateTime>::cast(item); |
| } |
| // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, |
| // then |
| if (item->IsJSTemporalZonedDateTime()) { |
| // i. 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(); |
| // ii. 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 (item->IsJSTemporalPlainDate()) { |
| // i. 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( |
| ToTemporalOverflowForSideEffects(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). |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result, ParseTemporalDateTimeString(isolate, string), |
| Handle<JSTemporalPlainDateTime>()); |
| // 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]]). |
| Handle<Object> calendar_string; |
| if (result.calendar->length() == 0) { |
| calendar_string = factory->undefined_value(); |
| } else { |
| calendar_string = result.calendar; |
| } |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar, |
| ToTemporalCalendarWithISODefault(isolate, calendar_string, 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 (item->IsJSTemporalPlainDateTime()) { |
| // a. Perform ? ToTemporalOverflow(options). |
| MAYBE_RETURN( |
| ToTemporalOverflowForSideEffects(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 (!temporal_date_time_like_obj->IsJSReceiver()) { |
| // 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). |
| 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 (plain_time_like->IsUndefined()) { |
| // 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, ShowOverflow::kConstrain, |
| "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 DateTimeRecordCommon& 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 calendarID be ? ToString(calendar). |
| Handle<String> calendar_id; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, calendar_id, |
| Object::ToString(isolate, calendar), String); |
| |
| // 9. Let calendarString be ! FormatCalendarAnnotation(calendarID, |
| // showCalendar). |
| Handle<String> calendar_string = |
| FormatCalendarAnnotation(isolate, calendar_id, show_calendar); |
| |
| // 10. Return the string-concatenation of year, the code unit 0x002D |
| // (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, 0x0054 |
| // (LATIN CAPITAL LETTER T), hour, the code unit 0x003A (COLON), minute, |
| builder.AppendString(calendar_string); |
| return builder.Finish().ToHandleChecked(); |
| } |
| } // namespace |
| |
| // #sec-temporal.plaindatetime.prototype.tojson |
| MaybeHandle<String> JSTemporalPlainDateTime::ToJSON( |
| Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time) { |
| return TemporalDateTimeToString( |
| isolate, |
| {{date_time->iso_year(), date_time->iso_month(), date_time->iso_day()}, |
| {date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), |
| date_time->iso_millisecond(), date_time->iso_microsecond(), |
| date_time->iso_nanosecond()}}, |
| Handle<JSReceiver>(date_time->calendar(), isolate), Precision::kAuto, |
| ShowCalendar::kAuto); |
| } |
| |
| // #sec-temporal.plaindatetime.prototype.tolocalestring |
| MaybeHandle<String> JSTemporalPlainDateTime::ToLocaleString( |
| Isolate* isolate, Handle<JSTemporalPlainDateTime> date_time, |
| Handle<Object> locales, Handle<Object> options) { |
| // TODO(ftang) Implement #sup-temporal.plaindatetime.prototype.tolocalestring |
| 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); |
| } |
| |
| namespace { |
| |
| DateTimeRecordCommon RoundTime(Isolate* isolate, const TimeRecordCommon& time, |
| double increment, Unit unit, |
| RoundingMode rounding_mode, |
| double day_length_ns); |
| |
| // #sec-temporal-roundisodatetime |
| DateTimeRecordCommon RoundISODateTime(Isolate* isolate, |
| const DateTimeRecordCommon& date_time, |
| double increment, Unit unit, |
| RoundingMode rounding_mode, |
| double day_length_ns) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| // 3. Let roundedTime be ! RoundTime(hour, minute, second, millisecond, |
| // microsecond, nanosecond, increment, unit, roundingMode, dayLength). |
| DateTimeRecordCommon rounded_time = RoundTime( |
| isolate, date_time.time, increment, unit, rounding_mode, day_length_ns); |
| // 4. 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; |
| DateRecordCommon balance_result = BalanceISODate(isolate, rounded_time.date); |
| |
| // 5. 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}; |
| } |
| |
| DateTimeRecordCommon RoundISODateTime(Isolate* isolate, |
| const DateTimeRecordCommon& date_time, |
| double increment, Unit unit, |
| RoundingMode rounding_mode) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| // 2. If dayLength is not present, set dayLength to 8.64 × 10^13. |
| return RoundISODateTime(isolate, date_time, increment, unit, rounding_mode, |
| 86400000000000LLU); |
| } |
| |
| // #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.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). |
| DateTimeRecordCommon 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 { |
| |
| enum class Arithmetic { kAdd, kSubtract }; |
| |
| 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). |
| DateTimeRecordCommon 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"); |
| } |
| |
| // #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 (new_target->IsUndefined()) { |
| 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 (!reference_iso_year_obj->IsUndefined()) { |
| 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<DateRecord> 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<DateRecord>()); |
| } |
| // 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<DateRecord>()); |
| } |
| |
| // 3. Let result be ? ParseISODateTime(isoString). |
| DateTimeRecord result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result, ParseISODateTime(isolate, iso_string, *parsed), |
| Nothing<DateRecord>()); |
| // 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]] }. |
| DateRecord 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(options->IsJSReceiver() || options->IsUndefined()); |
| |
| // 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 (item_obj->IsJSReceiver()) { |
| Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); |
| // a. If item has an [[InitializedTemporalMonthDay]] internal slot, then |
| // i. Return item. |
| if (item_obj->IsJSTemporalPlainMonthDay()) { |
| 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 (item_obj->IsJSTemporalPlainDate()) { |
| calendar = handle(Handle<JSTemporalPlainDate>::cast(item_obj)->calendar(), |
| isolate); |
| } else if (item_obj->IsJSTemporalPlainDateTime()) { |
| calendar = handle( |
| Handle<JSTemporalPlainDateTime>::cast(item_obj)->calendar(), isolate); |
| } else if (item_obj->IsJSTemporalPlainTime()) { |
| calendar = handle(Handle<JSTemporalPlainTime>::cast(item_obj)->calendar(), |
| isolate); |
| } else if (item_obj->IsJSTemporalPlainYearMonth()) { |
| calendar = |
| handle(Handle<JSTemporalPlainYearMonth>::cast(item_obj)->calendar(), |
| isolate); |
| } else if (item_obj->IsJSTemporalZonedDateTime()) { |
| 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 (calendar_obj->IsUndefined()) { |
| // 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<JSObject> 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 && !month->IsUndefined() && month_code->IsUndefined() && |
| year->IsUndefined()) { |
| // 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); |
| } |
| // 4. Perform ? ToTemporalOverflow(options). |
| MAYBE_RETURN_ON_EXCEPTION_VALUE( |
| isolate, ToTemporalOverflow(isolate, options, method_name), |
| Handle<JSTemporalPlainMonthDay>()); |
| |
| // 5. Let string be ? ToString(item). |
| Handle<String> string; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, string, |
| Object::ToString(isolate, item_obj), |
| JSTemporalPlainMonthDay); |
| |
| // 6. Let result be ? ParseTemporalMonthDayString(string). |
| DateRecord result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result, ParseTemporalMonthDayString(isolate, string), |
| Handle<JSTemporalPlainMonthDay>()); |
| |
| // 7. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]). |
| Handle<Object> calendar_string; |
| if (result.calendar->length() == 0) { |
| calendar_string = factory->undefined_value(); |
| } else { |
| calendar_string = result.calendar; |
| } |
| Handle<JSReceiver> calendar; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar, |
| ToTemporalCalendarWithISODefault(isolate, calendar_string, method_name), |
| JSTemporalPlainMonthDay); |
| |
| // 8. 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; |
| // 9. 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); |
| // 10. Let canonicalMonthDayOptions be ! OrdinaryObjectCreate(null). |
| Handle<JSReceiver> canonical_month_day_options = |
| factory->NewJSObjectWithNullProto(); |
| |
| // 11. Return ? MonthDayFromFields(calendar, result, |
| // canonicalMonthDayOptions). |
| return MonthDayFromFields(isolate, calendar, created_result, |
| canonical_month_day_options); |
| } |
| |
| 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 (item->IsJSTemporalPlainMonthDay()) { |
| // 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 (!item_obj->IsJSReceiver()) { |
| // 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(item->IsString()); |
| 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(item->IsString()); |
| Handle<String> string = Handle<String>::cast(item); |
| if (!added->Has(isolate, string)) { |
| merged_field_names->set(added->NumberOfElements(), *item); |
| added = StringSet::Add(isolate, added, string); |
| } |
| } |
| merged_field_names = FixedArray::ShrinkOrEmpty(isolate, merged_field_names, |
| added->NumberOfElements()); |
| |
| // 11. Set mergedFields to ? PrepareTemporalFields(mergedFields, |
| // mergedFieldNames, «»). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, merged_fields, |
| PrepareTemporalFields(isolate, merged_fields, merged_field_names, |
| RequiredFields::kNone), |
| JSTemporalPlainDate); |
| // 12. Let options be ! OrdinaryObjectCreate(null). |
| Handle<JSObject> options = factory->NewJSObjectWithNullProto(); |
| // 13. Perform ! CreateDataPropertyOrThrow(options, "overflow", "reject"). |
| CHECK(JSReceiver::CreateDataProperty( |
| isolate, options, factory->overflow_string(), |
| factory->reject_string(), Just(kThrowOnError)) |
| .FromJust()); |
| // 14. Return ? DateFromFields(calendar, mergedFields, options). |
| return DateFromFields(isolate, calendar, merged_fields, options); |
| } |
| |
| } // namespace |
| |
| // #sec-temporal.plainmonthday.prototype.toplaindate |
| MaybeHandle<JSTemporalPlainDate> JSTemporalPlainMonthDay::ToPlainDate( |
| Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day, |
| Handle<Object> item_obj) { |
| Factory* factory = isolate->factory(); |
| // 5. Let receiverFieldNames be ? CalendarFields(calendar, « "day", |
| // "monthCode" »). |
| // 7. Let inputFieldNames be ? CalendarFields(calendar, « "year" »). |
| return PlainMonthDayOrYearMonthToPlainDate<JSTemporalPlainMonthDay>( |
| isolate, month_day, item_obj, factory->day_string(), |
| factory->monthCode_string(), factory->year_string()); |
| } |
| |
| // #sec-temporal.plainmonthday.prototype.getisofields |
| MaybeHandle<JSReceiver> JSTemporalPlainMonthDay::GetISOFields( |
| Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day) { |
| Factory* factory = isolate->factory(); |
| // 1. Let monthDay be the this value. |
| // 2. Perform ? RequireInternalSlot(monthDay, |
| // [[InitializedTemporalMonthDay]]). |
| // 3. Let fields be ! OrdinaryObjectCreate(%Object.prototype%). |
| Handle<JSObject> fields = factory->NewJSObject(isolate->object_function()); |
| // 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", |
| // montyDay.[[Calendar]]). |
| CHECK(JSReceiver::CreateDataProperty( |
| isolate, fields, factory->calendar_string(), |
| Handle<JSReceiver>(month_day->calendar(), isolate), |
| Just(kThrowOnError)) |
| .FromJust()); |
| |
| // 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", |
| // 𝔽(montyDay.[[ISODay]])). |
| // 6. Perform ! CreateDataPropertyOrThrow(fields, "isoMonth", |
| // 𝔽(montyDay.[[ISOMonth]])). |
| // 7. Perform ! CreateDataPropertyOrThrow(fields, "isoYear", |
| // 𝔽(montyDay.[[ISOYear]])). |
| DEFINE_INT_FIELD(fields, isoDay, iso_day, month_day) |
| DEFINE_INT_FIELD(fields, isoMonth, iso_month, month_day) |
| DEFINE_INT_FIELD(fields, isoYear, iso_year, month_day) |
| // 8. Return fields. |
| return fields; |
| } |
| |
| // #sec-temporal.plainmonthday.prototype.tojson |
| MaybeHandle<String> JSTemporalPlainMonthDay::ToJSON( |
| Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day) { |
| return TemporalMonthDayToString(isolate, month_day, ShowCalendar::kAuto); |
| } |
| |
| // #sec-temporal.plainmonthday.prototype.tostring |
| MaybeHandle<String> JSTemporalPlainMonthDay::ToString( |
| Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day, |
| Handle<Object> options) { |
| return TemporalToString<JSTemporalPlainMonthDay, TemporalMonthDayToString>( |
| isolate, month_day, options, "Temporal.PlainMonthDay.prototype.toString"); |
| } |
| |
| // #sec-temporal.plainmonthday.prototype.tolocalestring |
| MaybeHandle<String> JSTemporalPlainMonthDay::ToLocaleString( |
| Isolate* isolate, Handle<JSTemporalPlainMonthDay> month_day, |
| Handle<Object> locales, Handle<Object> options) { |
| // TODO(ftang) Implement #sup-temporal.plainmonthday.prototype.tolocalestring |
| return TemporalMonthDayToString(isolate, month_day, ShowCalendar::kAuto); |
| } |
| |
| 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 (new_target->IsUndefined()) { |
| 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 (!reference_iso_day_obj->IsUndefined()) { |
| 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<DateRecord> 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<DateRecord>()); |
| } |
| |
| // 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<DateRecord>()); |
| } |
| |
| // 3. Let result be ? ParseISODateTime(isoString). |
| DateTimeRecord result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result, ParseISODateTime(isolate, iso_string, *parsed), |
| Nothing<DateRecord>()); |
| |
| // 4. Return the Record { [[Year]]: result.[[Year]], [[Month]]: |
| // result.[[Month]], [[Day]]: result.[[Day]], [[Calendar]]: |
| // result.[[Calendar]] }. |
| DateRecord 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(); |
| |
| Factory* factory = isolate->factory(); |
| // 2. Assert: Type(options) is Object or Undefined. |
| DCHECK(options->IsJSReceiver() || options->IsUndefined()); |
| // 3. If Type(item) is Object, then |
| if (item_obj->IsJSReceiver()) { |
| Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); |
| // a. If item has an [[InitializedTemporalYearMonth]] internal slot, then |
| // i. Return item. |
| if (item_obj->IsJSTemporalPlainYearMonth()) { |
| 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(ToTemporalOverflowForSideEffects(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). |
| DateRecord result; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result, ParseTemporalYearMonthString(isolate, string), |
| Handle<JSTemporalPlainYearMonth>()); |
| // 7. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]). |
| Handle<Object> calendar_string; |
| if (result.calendar->length() == 0) { |
| calendar_string = factory->undefined_value(); |
| } else { |
| calendar_string = result.calendar; |
| } |
| Handle<JSReceiver> calendar; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar, |
| ToTemporalCalendarWithISODefault(isolate, calendar_string, 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. Let canonicalYearMonthOptions be ! OrdinaryObjectCreate(null). |
| Handle<JSReceiver> canonical_year_month_options = |
| factory->NewJSObjectWithNullProto(); |
| // 10. Return ? YearMonthFromFields(calendar, result, |
| // canonicalYearMonthOptions). |
| return YearMonthFromFields(isolate, calendar, created_result, |
| canonical_year_month_options); |
| } |
| |
| 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 (item->IsJSTemporalPlainYearMonth()) { |
| // a. Perform ? ToTemporalOverflow(options). |
| MAYBE_RETURN( |
| ToTemporalOverflowForSideEffects(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)); |
| } |
| |
| // #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) { |
| // TODO(ftang) Implement #sup-temporal.plainyearmonth.prototype.tolocalestring |
| return TemporalYearMonthToString(isolate, year_month, ShowCalendar::kAuto); |
| } |
| |
| // #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 (new_target->IsUndefined()) { |
| // 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 (!item_obj->IsJSReceiver()) { |
| // 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 (temporal_date_like->IsUndefined()) { |
| // 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 (temporal_time_zone_like->IsUndefined()) { |
| // 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 TimeRecordCommon& time1, |
| const TimeRecordCommon& 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, ShowOverflow::kConstrain, |
| method_name), |
| Smi); |
| // 2. Set two to ? ToTemporalTime(two). |
| Handle<JSTemporalPlainTime> two; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, two, |
| temporal::ToTemporalTime(isolate, two_obj, ShowOverflow::kConstrain, |
| 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, ShowOverflow::kConstrain, |
| "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(); |
| } |
| |
| // #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 (!temporal_time_like_obj->IsJSReceiver()) { |
| // 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). |
| TimeRecordCommon 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 (item_obj->IsJSTemporalPlainTime()) { |
| // 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, overflow, method_name); |
| } |
| |
| // #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]]). |
| DateTimeRecordCommon 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"); |
| } |
| |
| // #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) { |
| // TODO(ftang) Implement #sup-temporal.plaintime.prototype.tolocalestring |
| return TemporalTimeToString(isolate, temporal_time, Precision::kAuto); |
| } |
| |
| namespace { |
| |
| double RoundNumberToIncrement(Isolate* isolate, double x, double increment, |
| RoundingMode rounding_mode) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| // 3. Let quotient be x / increment. |
| double rounded; |
| switch (rounding_mode) { |
| // 4. If roundingMode is "ceil", then |
| case RoundingMode::kCeil: |
| // a. Let rounded be −floor(−quotient). |
| rounded = -std::floor(-x / increment); |
| break; |
| // 5. Else if roundingMode is "floor", then |
| case RoundingMode::kFloor: |
| // a. Let rounded be floor(quotient). |
| rounded = std::floor(x / increment); |
| break; |
| // 6. Else if roundingMode is "trunc", then |
| case RoundingMode::kTrunc: |
| // a. Let rounded be the integral part of quotient, removing any |
| // fractional digits. |
| rounded = |
| (x > 0) ? std::floor(x / increment) : -std::floor(-x / increment); |
| break; |
| // 7. Else, |
| default: |
| // a. Let rounded be ! RoundHalfAwayFromZero(quotient). |
| rounded = std::round(x / increment); |
| break; |
| } |
| // 8. Return rounded × increment. |
| return rounded * increment; |
| } |
| |
| DateTimeRecordCommon RoundTime(Isolate* isolate, const TimeRecordCommon& 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({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({time.hour, 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({time.hour, time.minute, result, 0, 0, 0}); |
| // 15. If unit is "millisecond", then |
| case Unit::kMillisecond: |
| // a. Return ! BalanceTime(hour, minute, second, result, 0, 0). |
| return BalanceTime({time.hour, time.minute, time.second, result, 0, 0}); |
| // 16. If unit is "microsecond", then |
| case Unit::kMicrosecond: |
| // a. Return ! BalanceTime(hour, minute, second, millisecond, result, 0). |
| return BalanceTime( |
| {time.hour, time.minute, time.second, time.millisecond, result, 0}); |
| default: |
| // 17. Assert: unit is "nanosecond". |
| DCHECK_EQ(unit, Unit::kNanosecond); |
| // 18. Return ! BalanceTime(hour, minute, second, millisecond, |
| // microsecond, result). |
| return BalanceTime({time.hour, time.minute, time.second, time.millisecond, |
| time.microsecond, result}); |
| } |
| } |
| |
| DateTimeRecordCommon RoundTime(Isolate* isolate, const TimeRecordCommon& time, |
| double increment, Unit unit, |
| RoundingMode rounding_mode) { |
| TEMPORAL_ENTER_FUNC(); |
| |
| // 3-a. If dayLengthNs is not present, set it to 8.64 × 10^13. |
| return RoundTime(isolate, time, increment, unit, rounding_mode, |
| 86400000000000LLU); |
| } |
| |
| // GetOption wihle the types is << Number, String >> and values is empty. |
| // #sec-getoption |
| MaybeHandle<Object> GetOption_NumberOrString(Isolate* isolate, |
| Handle<JSReceiver> options, |
| Handle<String> property, |
| Handle<Object> fallback, |
| const char* method_name) { |
| // 3. Let value be ? Get(options, property). |
| Handle<Object> value; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, value, JSReceiver::GetProperty(isolate, options, property), |
| Object); |
| // 4. If value is undefined, return fallback. |
| if (value->IsUndefined()) { |
| return fallback; |
| } |
| // 5. If types contains Type(value), then |
| // a. Let type be Type(value). |
| // 6. Else, |
| // a. Let type be the last element of types. |
| // 7. If type is Boolean, then |
| // a. Set value to ! ToBoolean(value). |
| // 8. Else if type is Number, then |
| if (value->IsNumber()) { |
| // a. Set value to ? ToNumber(value). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, value, Object::ToNumber(isolate, value), |
| Object); |
| // b. If value is NaN, throw a RangeError exception. |
| if (value->IsNaN()) { |
| THROW_NEW_ERROR( |
| isolate, |
| NewRangeError(MessageTemplate::kPropertyValueOutOfRange, property), |
| Object); |
| } |
| return value; |
| } |
| // 9. Else, |
| // a. Set value to ? ToString(value). |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, value, Object::ToString(isolate, value), |
| Object); |
| return value; |
| } |
| |
| // In #sec-temporal-tosecondsstringprecision |
| // ToSecondsStringPrecision ( normalizedOptions ) |
| // 8. Let digits be ? GetStringOrNumberOption(normalizedOptions, |
| // "fractionalSecondDigits", « "auto" », 0, 9, "auto"). |
| Maybe<Precision> GetFractionalSecondDigits(Isolate* isolate, |
| Handle<JSReceiver> options, |
| const char* method_name) { |
| Factory* factory = isolate->factory(); |
| // 2. Let value be ? GetOption(options, property, « Number, String », empty, |
| // fallback). |
| Handle<Object> value; |
| ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, value, |
| GetOption_NumberOrString(isolate, options, |
| factory->fractionalSecondDigits_string(), |
| factory->auto_string(), method_name), |
| Nothing<Precision>()); |
| |
| // 3. If Type(value) is Number, then |
| if (value->IsNumber()) { |
| // a. If value < minimum or value > maximum, throw a RangeError exception. |
| double value_num = value->Number(); |
| if (value_num < 0 || value_num > 9) { |
| THROW_NEW_ERROR_RETURN_VALUE( |
| isolate, |
| NewRangeError(MessageTemplate::kPropertyValueOutOfRange, |
| factory->fractionalSecondDigits_string()), |
| Nothing<Precision>()); |
| } |
| // b. Return floor(ℝ(value)). |
| int32_t v = std::floor(value_num); |
| return Just(static_cast<Precision>(v)); |
| } |
| // 4. Assert: Type(value) is String. |
| DCHECK(value->IsString()); |
| // 5. If stringValues does not contain value, throw a RangeError exception. |
| Handle<String> string_value = Handle<String>::cast(value); |
| if (!String::Equals(isolate, string_value, factory->auto_string())) { |
| THROW_NEW_ERROR_RETURN_VALUE( |
| isolate, |
| NewRangeError(MessageTemplate::kPropertyValueOutOfRange, |
| factory->fractionalSecondDigits_string()), |
| Nothing<Precision>()); |
| } |
| // 6. Return value. |
| return Just(Precision::kAuto); |
| } |
| |
| // #sec-temporal-tosecondsstringprecision |
| Maybe<StringPrecision> ToSecondsStringPrecision( |
| Isolate* isolate, Handle<JSReceiver> normalized_options, |
| const char* method_name) { |
| // 1. Let smallestUnit be ? ToSmallestTemporalUnit(normalizedOptions, « |
| // "year", "month", "week", "day", "hour" », undefined). |
| Unit smallest_unit; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, smallest_unit, |
| ToSmallestTemporalUnit( |
| isolate, normalized_options, |
| std::set<Unit>({Unit::kYear, Unit::kMonth, Unit::kWeek, Unit::kDay, |
| Unit::kHour}), |
| Unit::kNotPresent, method_name), |
| Nothing<StringPrecision>()); |
| |
| switch (smallest_unit) { |
| // 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; |
| } |
| // 7. Assert: smallestUnit is undefined. |
| DCHECK(smallest_unit == Unit::kNotPresent); |
| // 8. Let digits be ? GetStringOrNumberOption(normalizedOptions, |
| // "fractionalSecondDigits", « "auto" », 0, 9, "auto"). |
| Precision digits; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, digits, |
| GetFractionalSecondDigits(isolate, normalized_options, method_name), |
| Nothing<StringPrecision>()); |
| |
| switch (digits) { |
| // 9. If digits is "auto", then |
| case Precision::kAuto: |
| // a. Return the new Record { [[Precision]]: "auto", [[Unit]]: |
| // "nanosecond", [[Increment]]: 1 }. |
| return Just(StringPrecision({Precision::kAuto, Unit::kNanosecond, 1})); |
| // 10. If digits is 0, then |
| case Precision::k0: |
| // a. Return the new Record { [[Precision]]: 0, [[Unit]]: "second", |
| // [[Increment]]: 1 }. |
| return Just(StringPrecision({Precision::k0, Unit::kSecond, 1})); |
| // 11. If digits is 1, 2, or 3, then |
| // a. Return the new Record { [[Precision]]: digits, [[Unit]]: |
| // "millisecond", [[Increment]]: 10^3 − digits }. |
| case Precision::k1: |
| return Just(StringPrecision({Precision::k1, Unit::kMillisecond, 100})); |
| case Precision::k2: |
| return Just(StringPrecision({Precision::k2, Unit::kMillisecond, 10})); |
| case Precision::k3: |
| return Just(StringPrecision({Precision::k3, Unit::kMillisecond, 1})); |
| // 12. If digits is 4, 5, or 6, then |
| // a. Return the new Record { [[Precision]]: digits, [[Unit]]: |
| // "microsecond", [[Increment]]: 10^6 − digits }. |
| case Precision::k4: |
| return Just(StringPrecision({Precision::k4, Unit::kMicrosecond, 100})); |
| case Precision::k5: |
| return Just(StringPrecision({Precision::k5, Unit::kMicrosecond, 10})); |
| case Precision::k6: |
| return Just(StringPrecision({Precision::k6, Unit::kMicrosecond, 1})); |
| // 13. Assert: digits is 7, 8, or 9. |
| // 14. Return the new Record { [[Precision]]: digits, [[Unit]]: |
| // "nanosecond", [[Increment]]: 10^9 − digits }. |
| case Precision::k7: |
| return Just(StringPrecision({Precision::k7, Unit::kNanosecond, 100})); |
| case Precision::k8: |
| return Just(StringPrecision({Precision::k8, Unit::kNanosecond, 10})); |
| case Precision::k9: |
| 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). |
| |
| DateTimeRecordCommon 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 (new_target->IsUndefined()) { |
| // 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<Smi> 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 ? AddISODate(year, month, day, 0, 0, 0, 1, |
| // "reject"). |
| DateRecordCommon tomorrow_fields; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, tomorrow_fields, |
| AddISODate( |
| isolate, |
| {temporal_date_time->iso_year(), temporal_date_time->iso_month(), |
| temporal_date_time->iso_day()}, |
| {0, 0, 0, 1}, ShowOverflow::kReject), |
| Handle<Smi>()); |
| |
| // 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]]. |
| // 16. Return 𝔽(diffNs / (3.6 × 10^12)). |
| int64_t diff_ns = |
| BigInt::Subtract(isolate, |
| handle(tomorrow_instant->nanoseconds(), isolate), |
| handle(today_instant->nanoseconds(), isolate)) |
| .ToHandleChecked() |
| ->AsInt64(); |
| return handle(Smi::FromInt(static_cast<int32_t>(diff_ns / 3600000000000LL)), |
| isolate); |
| } |
| |
| namespace { |
| |
| 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-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(options->IsUndefined() || options->IsJSReceiver()); |
| // 3. Let offsetBehaviour be option. |
| OffsetBehaviour offset_behaviour = OffsetBehaviour::kOption; |
| // 4. Let matchBehaviour be match exactly. |
| MatchBehaviour match_behaviour = MatchBehaviour::kMatchExactly; |
| |
| Handle<String> offset_string; |
| Handle<JSReceiver> time_zone; |
| Handle<JSReceiver> calendar; |
| |
| ZonedDateTimeRecord result; |
| |
| // 5. If Type(item) is Object, then |
| if (item_obj->IsJSReceiver()) { |
| Handle<JSReceiver> item = Handle<JSReceiver>::cast(item_obj); |
| // a. If item has an [[InitializedTemporalZonedDateTime]] internal slot, |
| // then |
| if (item_obj->IsJSTemporalZonedDateTime()) { |
| // i. Return item. |
| return Handle<JSTemporalZonedDateTime>::cast(item_obj); |
| } |
| // b. Let calendar be ? GetTemporalCalendarWithISODefault(item). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, calendar, |
| GetTemporalCalendarWithISODefault(isolate, item, method_name), |
| JSTemporalZonedDateTime); |
| // c. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", |
| // "microsecond", "millisecond", "minute", "month", "monthCode", |
| // "nanosecond", "second", "year" »). |
| Handle<FixedArray> field_names = All10UnitsInFixedArray(isolate); |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, field_names, |
| CalendarFields(isolate, calendar, field_names), |
| JSTemporalZonedDateTime); |
| |
| // d. Append "timeZone" to fieldNames. |
| int32_t field_length = field_names->length(); |
| field_names = FixedArray::SetAndGrow(isolate, field_names, field_length++, |
| factory->timeZone_string()); |
| |
| // e. Append "offset" to fieldNames. |
| field_names = FixedArray::SetAndGrow(isolate, field_names, field_length++, |
| factory->offset_string()); |
| field_names->Shrink(isolate, field_length); |
| |
| // f. Let fields be ? PrepareTemporalFields(item, fieldNames, « "timeZone" |
| // »). |
| Handle<JSReceiver> fields; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, fields, |
| PrepareTemporalFields(isolate, item, field_names, |
| RequiredFields::kTimeZone), |
| JSTemporalZonedDateTime); |
| |
| // g. Let timeZone be ? Get(fields, "timeZone"). |
| Handle<Object> time_zone_obj; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, time_zone_obj, |
| JSReceiver::GetProperty(isolate, fields, factory->timeZone_string()), |
| JSTemporalZonedDateTime); |
| |
| // h. Set timeZone to ? ToTemporalTimeZone(timeZone). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, time_zone, |
| temporal::ToTemporalTimeZone(isolate, time_zone_obj, method_name), |
| JSTemporalZonedDateTime); |
| // i. Let offsetString be ? Get(fields, "offset"). |
| Handle<Object> offset_string_obj; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, offset_string_obj, |
| JSReceiver::GetProperty(isolate, fields, factory->offset_string()), |
| JSTemporalZonedDateTime); |
| |
| // j. If offsetString is undefined, then |
| if (offset_string_obj->IsUndefined()) { |
| // 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_obj), |
| JSTemporalZonedDateTime); |
| } |
| |
| // l. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, |
| // options). |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result.date_time, |
| InterpretTemporalDateTimeFields(isolate, calendar, fields, options, |
| method_name), |
| Handle<JSTemporalZonedDateTime>()); |
| // 5. Else, |
| } else { |
| // a. Perform ? ToTemporalOverflow(options). |
| MAYBE_RETURN( |
| ToTemporalOverflowForSideEffects(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). |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, result, ParseTemporalZonedDateTimeString(isolate, string), |
| Handle<JSTemporalZonedDateTime>()); |
| |
| // e. Assert: timeZoneName is not undefined. |
| Handle<String> time_zone_name = result.time_zone.name; |
| DCHECK(!time_zone_name.is_null()); |
| |
| // 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.[[TimeZoneOffsetString]]. |
| offset_string = result.time_zone.offset_string; |
| |
| // h. If result.[[TimeZoneZ]] is true, then |
| if (result.time_zone.z) { |
| // i. Set offsetBehaviour to exact. |
| offset_behaviour = OffsetBehaviour::kExact; |
| // i. Else if offsetString is undefined, then |
| } else if (offset_string.is_null()) { |
| // 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, result.date_time.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). |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, offset_nanoseconds, |
| ParseTimeZoneOffsetString(isolate, 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.date_time, 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); |
| } |
| |
| } // 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 (item->IsJSTemporalZonedDateTime()) { |
| // a. Perform ? ToTemporalOverflow(options). |
| MAYBE_RETURN( |
| ToTemporalOverflowForSideEffects(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); |
| } |
| |
| 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. Return epochNanoseconds − offsetNanoseconds. |
| return BigInt::Subtract(isolate, epoch_nanoseconds, |
| BigInt::FromInt64(isolate, offset_nanoseconds)) |
| .ToHandleChecked(); |
| } |
| // 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(possible_instants->get(i).IsJSTemporalInstant()); |
| 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"). |
| int64_t 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 (!temporal_zoned_date_time_like_obj->IsJSReceiver()) { |
| // a. Throw a TypeError exception. |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_TYPE_ERROR(), |
| JSTemporalZonedDateTime); |
| } |
| Handle<JSReceiver> temporal_zoned_date_time_like = |
| Handle<JSReceiver>::cast(temporal_zoned_date_time_like_obj); |
| // 4. Perform ? RejectObjectWithCalendarOrTimeZone(temporalZonedDateTimeLike). |
| MAYBE_RETURN(RejectObjectWithCalendarOrTimeZone( |
| isolate, temporal_zoned_date_time_like), |
| Handle<JSTemporalZonedDateTime>()); |
| |
| // 5. Let calendar be zonedDateTime.[[Calendar]]. |
| Handle<JSReceiver> calendar(zoned_date_time->calendar(), isolate); |
| |
| // 6. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", |
| // "microsecond", "millisecond", "minute", "month", "monthCode", "nanosecond", |
| // "second", "year" »). |
| Handle<FixedArray> field_names; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, field_names, |
| CalendarFields(isolate, calendar, All10UnitsInFixedArray(isolate)), |
| JSTemporalZonedDateTime); |
| |
| // 7. Append "offset" to fieldNames. |
| int32_t field_length = field_names->length(); |
| field_names = FixedArray::SetAndGrow(isolate, field_names, field_length++, |
| factory->offset_string()); |
| field_names->Shrink(isolate, field_length); |
| |
| // 8. Let partialZonedDateTime be ? |
| // PreparePartialTemporalFields(temporalZonedDateTimeLike, fieldNames). |
| Handle<JSObject> partial_zoned_date_time; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, partial_zoned_date_time, |
| PreparePartialTemporalFields(isolate, temporal_zoned_date_time_like, |
| field_names), |
| JSTemporalZonedDateTime); |
| // 9. Set options to ? GetOptionsObject(options). |
| Handle<JSReceiver> options; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, options, GetOptionsObject(isolate, options_obj, method_name), |
| JSTemporalZonedDateTime); |
| |
| // 10. Let disambiguation be ? ToTemporalDisambiguation(options). |
| Disambiguation disambiguation; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, disambiguation, |
| ToTemporalDisambiguation(isolate, options, method_name), |
| Handle<JSTemporalZonedDateTime>()); |
| |
| // 11. Let offset be ? ToTemporalOffset(options, "prefer"). |
| enum Offset offset; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, offset, |
| ToTemporalOffset(isolate, options, Offset::kPrefer, method_name), |
| Handle<JSTemporalZonedDateTime>()); |
| |
| // 12. Let timeZone be zonedDateTime.[[TimeZone]]. |
| Handle<JSReceiver> time_zone(zoned_date_time->time_zone(), isolate); |
| |
| // 13. Append "timeZone" to fieldNames. |
| field_length = field_names->length(); |
| field_names = FixedArray::SetAndGrow(isolate, field_names, field_length++, |
| factory->timeZone_string()); |
| field_names->Shrink(isolate, field_length); |
| |
| // 14. Let fields be ? PrepareTemporalFields(zonedDateTime, fieldNames, « |
| // "timeZone", "offset"»). |
| Handle<JSReceiver> fields; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, fields, |
| PrepareTemporalFields(isolate, zoned_date_time, field_names, |
| RequiredFields::kTimeZoneAndOffset), |
| JSTemporalZonedDateTime); |
| // 15. Set fields to ? CalendarMergeFields(calendar, fields, |
| // partialZonedDateTime). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, fields, |
| CalendarMergeFields(isolate, calendar, fields, partial_zoned_date_time), |
| JSTemporalZonedDateTime); |
| |
| // 16. Set fields to ? PrepareTemporalFields(fields, fieldNames, « "timeZone" |
| // , "offset"»). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, fields, |
| PrepareTemporalFields(isolate, fields, field_names, |
| RequiredFields::kTimeZoneAndOffset), |
| JSTemporalZonedDateTime); |
| |
| // 17. Let offsetString be ? Get(fields, "offset"). |
| Handle<Object> offset_string_obj; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, offset_string_obj, |
| JSReceiver::GetProperty(isolate, fields, factory->offset_string()), |
| JSTemporalZonedDateTime); |
| |
| // 18. Assert: Type(offsetString) is String. |
| DCHECK(offset_string_obj->IsString()); |
| |
| // 19. Let dateTimeResult be ? InterpretTemporalDateTimeFields(calendar, |
| // fields, options). |
| 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_obj)), |
| 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, |
| 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 (plain_time_like->IsUndefined()) { |
| // 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, |
| ShowOverflow::kConstrain, 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"); |
| } |
| |
| // #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); |
| } |
| |
| 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"); |
| } |
| |
| // #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 (new_target->IsUndefined()) { |
| // 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-roundhalfawayfromzero |
| Handle<BigInt> RoundHalfAwayFromZero(Isolate* isolate, Handle<BigInt> x, |
| Handle<BigInt> increment) { |
| DCHECK(!increment->IsNegative()); |
| bool negative = x->IsNegative(); |
| if (negative) { |
| x = BigInt::UnaryMinus(isolate, x); |
| } |
| Handle<BigInt> rounded = |
| BigInt::Divide(isolate, x, increment).ToHandleChecked(); |
| Handle<BigInt> remainder = |
| BigInt::Remainder(isolate, x, increment).ToHandleChecked(); |
| remainder = |
| BigInt::Multiply(isolate, remainder, BigInt::FromInt64(isolate, 2)) |
| .ToHandleChecked(); |
| switch (BigInt::CompareToBigInt(remainder, increment)) { |
| case ComparisonResult::kLessThan: |
| break; |
| case ComparisonResult::kEqual: |
| case ComparisonResult::kGreaterThan: |
| default: |
| rounded = BigInt::Increment(isolate, rounded).ToHandleChecked(); |
| break; |
| } |
| if (negative) { |
| rounded = BigInt::UnaryMinus(isolate, rounded); |
| } |
| return rounded; |
| } |
| |
| // #sec-temporal-roundnumbertoincrement |
| Handle<BigInt> RoundNumberToIncrement(Isolate* isolate, Handle<BigInt> x, |
| int64_t increment, |
| RoundingMode rounding_mode) { |
| TEMPORAL_ENTER_FUNC(); |
| CHECK_GE(increment, 0); |
| Handle<BigInt> increment_n = BigInt::FromInt64(isolate, increment); |
| Handle<BigInt> rounded; |
| // 3. Let quotient be x / increment. |
| switch (rounding_mode) { |
| // 4. If roundingMode is "ceil", then |
| case RoundingMode::kCeil: |
| // a. Let rounded be −floor(−quotient). |
| rounded = |
| BigInt::Divide(isolate, BigInt::UnaryMinus(isolate, x), increment_n) |
| .ToHandleChecked(); |
| rounded = BigInt::UnaryMinus(isolate, rounded); |
| break; |
| // 5. Else if roundingMode is "floor", then |
| case RoundingMode::kFloor: |
| // a. Let rounded be floor(quotient). |
| rounded = BigInt::Divide(isolate, x, increment_n).ToHandleChecked(); |
| break; |
| // 6. Else if roundingMode is "trunc", then |
| case RoundingMode::kTrunc: |
| // a. Let rounded be the integral part of quotient, removing any |
| // fractional digits. |
| if (x->IsNegative()) { |
| rounded = |
| BigInt::Divide(isolate, BigInt::UnaryMinus(isolate, x), increment_n) |
| .ToHandleChecked(); |
| rounded = BigInt::UnaryMinus(isolate, rounded); |
| } else { |
| rounded = BigInt::Divide(isolate, x, increment_n).ToHandleChecked(); |
| } |
| break; |
| // 7. Else, |
| default: |
| // a. Let rounded be ! RoundHalfAwayFromZero(quotient). |
| rounded = RoundHalfAwayFromZero(isolate, x, increment_n); |
| break; |
| } |
| // 8. Return rounded × increment. |
| return BigInt::Multiply(isolate, rounded, increment_n).ToHandleChecked(); |
| } |
| |
| // #sec-temporal-roundtemporalinstant |
| Handle<BigInt> RoundTemporalInstant(Isolate* isolate, Handle<BigInt> ns, |
| int64_t increment, Unit unit, |
| RoundingMode rounding_mode) { |
| TEMPORAL_ENTER_FUNC(); |
| // 1. Assert: Type(ns) is BigInt. |
| int64_t 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 ! RoundNumberToIncrement(ℝ(ns), incrementNs, roundingMode). |
| return RoundNumberToIncrement(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 (round_to_obj->IsUndefined()) { |
| // 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 (round_to_obj->IsString()) { |
| // 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); |
| } |
| |
| // 5. Let smallestUnit be ? ToSmallestTemporalUnit(roundTo, « "year", "month", |
| // "week", "day" », undefined). |
| Unit smallest_unit; |
| MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| isolate, smallest_unit, |
| ToSmallestTemporalUnit( |
| isolate, round_to, |
| std::set<Unit>({Unit::kYear, Unit::kMonth, Unit::kWeek, Unit::kDay}), |
| Unit::kNotPresent, method_name), |
| Handle<JSTemporalInstant>()); |
| // 6. If smallestUnit is undefined, throw a RangeError exception. |
| if (smallest_unit == Unit::kNotPresent) { |
| THROW_NEW_ERROR(isolate, NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(), |
| 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 8.64 × 10^13. |
| maximum = 8.64e13; |
| 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), |
| static_cast<int64_t>(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 (item->IsJSTemporalInstant()) { |
| // 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 (!item_obj->IsJSReceiver()) { |
| // 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 (calendar_like->IsUndefined()) { |
| // 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 (calendar_like->IsUndefined()) { |
| // 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); |
| } |
| |
| // #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 (item_obj->IsJSReceiver()) { |
| 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 (!time_zone_property->IsUndefined()) { |
| // 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); |
| } |
| |
| } // 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"); |
| } |
| |
| 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++) { |
| Object item = fields_name->get(i); |
| CHECK(item.IsString()); |
| 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(); |
| } |
| |
| } // namespace temporal |
| } // namespace internal |
| } // namespace v8 |