2021-07-11 21:04:11 +03:00
|
|
|
|
/*
|
|
|
|
|
|
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
2023-01-26 12:07:38 +00:00
|
|
|
|
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
2024-11-16 13:14:14 -05:00
|
|
|
|
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
2021-07-11 21:04:11 +03:00
|
|
|
|
*
|
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
2021-08-31 00:15:36 +01:00
|
|
|
|
#include <AK/Variant.h>
|
2024-11-18 14:27:54 -05:00
|
|
|
|
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
|
|
|
|
|
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
2024-11-18 10:36:03 -05:00
|
|
|
|
#include <LibGC/Ptr.h>
|
2021-07-18 21:44:05 +01:00
|
|
|
|
#include <LibJS/Forward.h>
|
2021-09-15 23:03:38 +01:00
|
|
|
|
#include <LibJS/Runtime/Completion.h>
|
2024-11-20 12:59:15 -05:00
|
|
|
|
#include <LibJS/Runtime/Temporal/ISO8601.h>
|
|
|
|
|
|
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
2024-11-16 12:51:53 -05:00
|
|
|
|
#include <LibJS/Runtime/VM.h>
|
2023-10-06 17:54:21 +02:00
|
|
|
|
#include <LibJS/Runtime/ValueInlines.h>
|
2024-11-18 11:58:51 -05:00
|
|
|
|
#include <math.h>
|
2021-07-11 21:04:11 +03:00
|
|
|
|
|
|
|
|
|
|
namespace JS::Temporal {
|
|
|
|
|
|
|
2024-11-18 12:48:10 -05:00
|
|
|
|
enum class ArithmeticOperation {
|
|
|
|
|
|
Add,
|
|
|
|
|
|
Subtract,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-11-20 12:59:15 -05:00
|
|
|
|
enum class DateType {
|
|
|
|
|
|
Date,
|
|
|
|
|
|
MonthDay,
|
|
|
|
|
|
YearMonth,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
enum class Overflow {
|
|
|
|
|
|
Constrain,
|
|
|
|
|
|
Reject,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
enum class TimeStyle {
|
|
|
|
|
|
Separated,
|
|
|
|
|
|
Unseparated,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-11-18 11:58:51 -05:00
|
|
|
|
// https://tc39.es/proposal-temporal/#sec-temporal-units
|
|
|
|
|
|
enum class Unit {
|
|
|
|
|
|
Year,
|
|
|
|
|
|
Month,
|
|
|
|
|
|
Week,
|
|
|
|
|
|
Day,
|
|
|
|
|
|
Hour,
|
|
|
|
|
|
Minute,
|
|
|
|
|
|
Second,
|
|
|
|
|
|
Millisecond,
|
|
|
|
|
|
Microsecond,
|
|
|
|
|
|
Nanosecond,
|
|
|
|
|
|
};
|
|
|
|
|
|
StringView temporal_unit_to_string(Unit);
|
|
|
|
|
|
|
|
|
|
|
|
// https://tc39.es/proposal-temporal/#sec-temporal-units
|
|
|
|
|
|
enum class UnitCategory {
|
|
|
|
|
|
Date,
|
|
|
|
|
|
Time,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// https://tc39.es/proposal-temporal/#sec-temporal-units
|
|
|
|
|
|
enum class UnitGroup {
|
|
|
|
|
|
Date,
|
|
|
|
|
|
Time,
|
|
|
|
|
|
DateTime,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-11-18 14:27:54 -05:00
|
|
|
|
// https://tc39.es/proposal-temporal/#table-unsigned-rounding-modes
|
|
|
|
|
|
enum class RoundingMode {
|
|
|
|
|
|
Ceil,
|
|
|
|
|
|
Floor,
|
|
|
|
|
|
Expand,
|
|
|
|
|
|
Trunc,
|
|
|
|
|
|
HalfCeil,
|
|
|
|
|
|
HalfFloor,
|
|
|
|
|
|
HalfExpand,
|
|
|
|
|
|
HalfTrunc,
|
|
|
|
|
|
HalfEven,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// https://tc39.es/proposal-temporal/#table-unsigned-rounding-modes
|
|
|
|
|
|
enum class UnsignedRoundingMode {
|
|
|
|
|
|
HalfEven,
|
|
|
|
|
|
HalfInfinity,
|
|
|
|
|
|
HalfZero,
|
|
|
|
|
|
Infinity,
|
|
|
|
|
|
Zero,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// https://tc39.es/proposal-temporal/#table-unsigned-rounding-modes
|
|
|
|
|
|
enum class Sign {
|
|
|
|
|
|
Negative,
|
|
|
|
|
|
Positive,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct Auto { };
|
|
|
|
|
|
struct Required { };
|
2024-11-18 11:58:51 -05:00
|
|
|
|
struct Unset { };
|
2024-11-18 14:27:54 -05:00
|
|
|
|
using Precision = Variant<Auto, u8>;
|
2024-11-18 11:58:51 -05:00
|
|
|
|
using RoundingIncrement = Variant<Unset, u64>;
|
2024-11-18 14:27:54 -05:00
|
|
|
|
using UnitDefault = Variant<Required, Unset, Auto, Unit>;
|
|
|
|
|
|
using UnitValue = Variant<Unset, Auto, Unit>;
|
|
|
|
|
|
|
|
|
|
|
|
struct SecondsStringPrecision {
|
|
|
|
|
|
struct Minute { };
|
2024-11-20 12:59:15 -05:00
|
|
|
|
using Precision = Variant<Minute, Auto, u8>;
|
2024-11-18 14:27:54 -05:00
|
|
|
|
|
2024-11-20 12:59:15 -05:00
|
|
|
|
Precision precision;
|
2024-11-18 14:27:54 -05:00
|
|
|
|
Unit unit;
|
|
|
|
|
|
u8 increment { 0 };
|
|
|
|
|
|
};
|
2024-11-18 11:58:51 -05:00
|
|
|
|
|
|
|
|
|
|
struct RelativeTo {
|
|
|
|
|
|
// FIXME: Make these objects represent their actual types when we re-implement them.
|
|
|
|
|
|
GC::Ptr<JS::Object> plain_relative_to; // [[PlainRelativeTo]]
|
|
|
|
|
|
GC::Ptr<JS::Object> zoned_relative_to; // [[ZonedRelativeTo]]
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-11-20 12:59:15 -05:00
|
|
|
|
// 13.31 ISO String Time Zone Parse Records, https://tc39.es/proposal-temporal/#sec-temporal-iso-string-time-zone-parse-records
|
|
|
|
|
|
struct ParsedISOTimeZone {
|
|
|
|
|
|
bool z_designator { false };
|
|
|
|
|
|
Optional<String> offset_string;
|
|
|
|
|
|
Optional<String> time_zone_annotation;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 13.32 ISO Date-Time Parse Records, https://tc39.es/proposal-temporal/#sec-temporal-iso-date-time-parse-records
|
|
|
|
|
|
struct ParsedISODateTime {
|
|
|
|
|
|
struct StartOfDay { };
|
|
|
|
|
|
|
|
|
|
|
|
Optional<i32> year { 0 };
|
|
|
|
|
|
u8 month { 0 };
|
|
|
|
|
|
u8 day { 0 };
|
|
|
|
|
|
Variant<StartOfDay, Time> time;
|
|
|
|
|
|
ParsedISOTimeZone time_zone;
|
|
|
|
|
|
Optional<String> calendar;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
double iso_date_to_epoch_days(double year, double month, double date);
|
|
|
|
|
|
double epoch_days_to_epoch_ms(double day, double time);
|
|
|
|
|
|
ThrowCompletionOr<Overflow> get_temporal_overflow_option(VM&, Object const& options);
|
2024-11-18 14:41:29 -05:00
|
|
|
|
ThrowCompletionOr<void> validate_temporal_rounding_increment(VM&, u64 increment, u64 dividend, bool inclusive);
|
2024-11-18 14:27:54 -05:00
|
|
|
|
ThrowCompletionOr<Precision> get_temporal_fractional_second_digits_option(VM&, Object const& options);
|
|
|
|
|
|
SecondsStringPrecision to_seconds_string_precision_record(UnitValue, Precision);
|
|
|
|
|
|
ThrowCompletionOr<UnitValue> get_temporal_unit_valued_option(VM&, Object const& options, PropertyKey const&, UnitGroup, UnitDefault const&, ReadonlySpan<UnitValue> extra_values = {});
|
2024-11-18 11:58:51 -05:00
|
|
|
|
ThrowCompletionOr<RelativeTo> get_temporal_relative_to_option(VM&, Object const& options);
|
2024-11-18 12:48:10 -05:00
|
|
|
|
Unit larger_of_two_temporal_units(Unit, Unit);
|
2024-11-18 11:58:51 -05:00
|
|
|
|
bool is_calendar_unit(Unit);
|
|
|
|
|
|
UnitCategory temporal_unit_category(Unit);
|
2024-11-18 14:41:29 -05:00
|
|
|
|
RoundingIncrement maximum_temporal_duration_rounding_increment(Unit);
|
2024-11-18 14:27:54 -05:00
|
|
|
|
Crypto::UnsignedBigInteger const& temporal_unit_length_in_nanoseconds(Unit);
|
|
|
|
|
|
String format_fractional_seconds(u64, Precision);
|
2024-11-20 12:59:15 -05:00
|
|
|
|
String format_time_string(u8 hour, u8 minute, u8 second, u16 sub_second_nanoseconds, SecondsStringPrecision::Precision, Optional<TimeStyle> = {});
|
2024-11-18 14:27:54 -05:00
|
|
|
|
UnsignedRoundingMode get_unsigned_rounding_mode(RoundingMode, Sign);
|
|
|
|
|
|
double apply_unsigned_rounding_mode(double, double r1, double r2, UnsignedRoundingMode);
|
|
|
|
|
|
Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedDivisionResult const&, Crypto::SignedBigInteger const& r1, Crypto::SignedBigInteger const& r2, UnsignedRoundingMode, Crypto::UnsignedBigInteger const& increment);
|
|
|
|
|
|
double round_number_to_increment(double, u64 increment, RoundingMode);
|
|
|
|
|
|
Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger const&, Crypto::UnsignedBigInteger const& increment, RoundingMode);
|
2024-11-20 12:59:15 -05:00
|
|
|
|
ThrowCompletionOr<ParsedISODateTime> parse_iso_date_time(VM&, StringView iso_string, ReadonlySpan<Production> allowed_formats);
|
|
|
|
|
|
ThrowCompletionOr<String> parse_temporal_calendar_string(VM&, String const&);
|
2024-11-18 11:58:51 -05:00
|
|
|
|
ThrowCompletionOr<GC::Ref<Duration>> parse_temporal_duration_string(VM&, StringView iso_string);
|
2024-11-20 12:59:15 -05:00
|
|
|
|
ThrowCompletionOr<TimeZone> parse_temporal_time_zone_string(VM& vm, StringView time_zone_string);
|
|
|
|
|
|
ThrowCompletionOr<String> to_month_code(VM&, Value argument);
|
|
|
|
|
|
ThrowCompletionOr<String> to_offset_string(VM&, Value argument);
|
|
|
|
|
|
CalendarFields iso_date_to_fields(StringView calendar, ISODate const&, DateType);
|
2024-11-18 11:58:51 -05:00
|
|
|
|
|
|
|
|
|
|
// 13.38 ToIntegerWithTruncation ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerwithtruncation
|
|
|
|
|
|
template<typename... Args>
|
|
|
|
|
|
ThrowCompletionOr<double> to_integer_with_truncation(VM& vm, Value argument, ErrorType error_type, Args&&... args)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1. Let number be ? ToNumber(argument).
|
|
|
|
|
|
auto number = TRY(argument.to_number(vm));
|
|
|
|
|
|
|
|
|
|
|
|
// 2. If number is NaN, +∞𝔽 or -∞𝔽, throw a RangeError exception.
|
|
|
|
|
|
if (number.is_nan() || number.is_infinity())
|
|
|
|
|
|
return vm.throw_completion<RangeError>(error_type, forward<Args>(args)...);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. Return truncate(ℝ(number)).
|
|
|
|
|
|
return trunc(number.as_double());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 13.38 ToIntegerWithTruncation ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerwithtruncation
|
|
|
|
|
|
// AD-HOC: We often need to use this AO when we have a parsed StringView. This overload allows callers to avoid creating
|
|
|
|
|
|
// a PrimitiveString for the primary definition.
|
|
|
|
|
|
template<typename... Args>
|
|
|
|
|
|
ThrowCompletionOr<double> to_integer_with_truncation(VM& vm, StringView argument, ErrorType error_type, Args&&... args)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1. Let number be ? ToNumber(argument).
|
|
|
|
|
|
auto number = string_to_number(argument);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. If number is NaN, +∞𝔽 or -∞𝔽, throw a RangeError exception.
|
|
|
|
|
|
if (isnan(number) || isinf(number))
|
|
|
|
|
|
return vm.throw_completion<RangeError>(error_type, forward<Args>(args)...);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. Return truncate(ℝ(number)).
|
|
|
|
|
|
return trunc(number);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-20 12:59:15 -05:00
|
|
|
|
// 13.37 ToPositiveIntegerWithTruncation ( argument ), https://tc39.es/proposal-temporal/#sec-topositiveintegerwithtruncation
|
|
|
|
|
|
template<typename... Args>
|
|
|
|
|
|
ThrowCompletionOr<double> to_positive_integer_with_truncation(VM& vm, Value argument, ErrorType error_type, Args&&... args)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1. Let integer be ? ToIntegerWithTruncation(argument).
|
|
|
|
|
|
auto integer = TRY(to_integer_with_truncation(vm, argument, error_type, args...));
|
|
|
|
|
|
|
|
|
|
|
|
// 2. If integer ≤ 0, throw a RangeError exception.
|
|
|
|
|
|
if (integer <= 0)
|
|
|
|
|
|
return vm.throw_completion<RangeError>(error_type, args...);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. Return integer.
|
|
|
|
|
|
return integer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-16 13:14:14 -05:00
|
|
|
|
// 13.39 ToIntegerIfIntegral ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerifintegral
|
|
|
|
|
|
template<typename... Args>
|
|
|
|
|
|
ThrowCompletionOr<double> to_integer_if_integral(VM& vm, Value argument, ErrorType error_type, Args&&... args)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1. Let number be ? ToNumber(argument).
|
|
|
|
|
|
auto number = TRY(argument.to_number(vm));
|
|
|
|
|
|
|
|
|
|
|
|
// 2. If number is not an integral Number, throw a RangeError exception.
|
|
|
|
|
|
if (!number.is_integral_number())
|
|
|
|
|
|
return vm.throw_completion<RangeError>(error_type, forward<Args>(args)...);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. Return ℝ(number).
|
|
|
|
|
|
return number.as_double();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-20 12:59:15 -05:00
|
|
|
|
// 14.2 The Year-Week Record Specification Type, https://tc39.es/proposal-temporal/#sec-year-week-record-specification-type
|
|
|
|
|
|
struct YearWeek {
|
|
|
|
|
|
Optional<u8> week;
|
|
|
|
|
|
Optional<i32> year;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2021-07-11 22:28:49 +03:00
|
|
|
|
enum class OptionType {
|
|
|
|
|
|
Boolean,
|
|
|
|
|
|
String,
|
|
|
|
|
|
};
|
2021-07-18 21:41:29 +01:00
|
|
|
|
|
2024-11-18 14:27:54 -05:00
|
|
|
|
using OptionDefault = Variant<Required, Empty, bool, StringView, double>;
|
2022-06-09 22:42:42 +01:00
|
|
|
|
|
2024-11-18 10:36:03 -05:00
|
|
|
|
ThrowCompletionOr<GC::Ref<Object>> get_options_object(VM&, Value options);
|
2023-02-05 19:02:54 +00:00
|
|
|
|
ThrowCompletionOr<Value> get_option(VM&, Object const& options, PropertyKey const& property, OptionType type, ReadonlySpan<StringView> values, OptionDefault const&);
|
2021-07-11 21:04:11 +03:00
|
|
|
|
|
2022-06-14 23:03:25 +01:00
|
|
|
|
template<size_t Size>
|
2022-08-20 08:52:42 +01:00
|
|
|
|
ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey const& property, OptionType type, StringView const (&values)[Size], OptionDefault const& default_)
|
2022-06-14 23:03:25 +01:00
|
|
|
|
{
|
2023-02-05 19:02:54 +00:00
|
|
|
|
return get_option(vm, options, property, type, ReadonlySpan<StringView> { values }, default_);
|
2022-06-14 23:03:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-18 14:27:54 -05:00
|
|
|
|
ThrowCompletionOr<RoundingMode> get_rounding_mode_option(VM&, Object const& options, RoundingMode fallback);
|
2024-11-18 14:41:29 -05:00
|
|
|
|
ThrowCompletionOr<u64> get_rounding_increment_option(VM&, Object const& options);
|
2024-11-20 12:59:15 -05:00
|
|
|
|
Crypto::SignedBigInteger get_utc_epoch_nanoseconds(ISODateTime const&);
|
2024-11-18 14:27:54 -05:00
|
|
|
|
|
2021-07-11 21:04:11 +03:00
|
|
|
|
}
|