diff options
author | Linus Groh <mail@linusgroh.de> | 2021-07-27 00:21:16 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-07-27 19:51:44 +0100 |
commit | c303bbde5430c8d889853ae112d50275b57f74c5 (patch) | |
tree | 384724cadb81da7e3c237404b3d073cd4e6e1ddd /Userland/Libraries/LibJS/Runtime/Temporal | |
parent | 5512ff79f0836912f19499bf5dc59c49cbb48482 (diff) | |
download | serenity-c303bbde5430c8d889853ae112d50275b57f74c5.zip |
LibJS: Implement Temporal.Now.plainDate()
...and ten required AOs we didn't have yet:
- BalanceISODate
- BalanceISODateTime
- BalanceISOYearMonth
- BalanceTime
- BuiltinTimeZoneGetPlainDateTimeFor
- GetISOPartsFromEpoch
- GetOffsetNanosecondsFor
- ParseTemporalTimeZone
- SystemDateTime
- ToTemporalTimeZone
Diffstat (limited to 'Userland/Libraries/LibJS/Runtime/Temporal')
12 files changed, 473 insertions, 1 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Now.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Now.cpp index aa2073edaf..f33e1c3529 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/Now.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/Now.cpp @@ -6,8 +6,11 @@ #include <LibCrypto/BigInt/SignedBigInteger.h> #include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/Temporal/Calendar.h> #include <LibJS/Runtime/Temporal/Instant.h> #include <LibJS/Runtime/Temporal/Now.h> +#include <LibJS/Runtime/Temporal/PlainDate.h> +#include <LibJS/Runtime/Temporal/PlainDateTime.h> #include <LibJS/Runtime/Temporal/TimeZone.h> #include <time.h> @@ -31,6 +34,7 @@ void Now::initialize(GlobalObject& global_object) u8 attr = Attribute::Writable | Attribute::Configurable; define_native_function(vm.names.timeZone, time_zone, 0, attr); define_native_function(vm.names.instant, instant, 0, attr); + define_native_function(vm.names.plainDate, plain_date, 1, attr); } // 2.1.1 Temporal.Now.timeZone ( ), https://tc39.es/proposal-temporal/#sec-temporal.now.timezone @@ -47,6 +51,21 @@ JS_DEFINE_NATIVE_FUNCTION(Now::instant) return system_instant(global_object); } +// 2.1.7 Temporal.Now.plainDate ( calendar [ , temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaindate +JS_DEFINE_NATIVE_FUNCTION(Now::plain_date) +{ + auto calendar = vm.argument(0); + auto temporal_time_zone_like = vm.argument(1); + + // 1. Let dateTime be ? SystemDateTime(temporalTimeZoneLike, calendar). + auto* date_time = system_date_time(global_object, temporal_time_zone_like, calendar); + if (vm.exception()) + return {}; + + // 2. Return ? CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]). + return create_temporal_date(global_object, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->calendar()); +} + // 2.2.1 SystemTimeZone ( ), https://tc39.es/proposal-temporal/#sec-temporal-systemtimezone TimeZone* system_time_zone(GlobalObject& global_object) { @@ -90,4 +109,33 @@ Instant* system_instant(GlobalObject& global_object) return create_temporal_instant(global_object, *ns); } +// 2.2.4 SystemDateTime ( temporalTimeZoneLike, calendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-systemdatetime +PlainDateTime* system_date_time(GlobalObject& global_object, Value temporal_time_zone_like, Value calendar_like) +{ + auto& vm = global_object.vm(); + Object* time_zone; + + // 1. If temporalTimeZoneLike is undefined, then + if (temporal_time_zone_like.is_undefined()) { + // a. Let timeZone be ! SystemTimeZone(). + time_zone = system_time_zone(global_object); + } else { + // a. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike). + time_zone = to_temporal_time_zone(global_object, temporal_time_zone_like); + if (vm.exception()) + return {}; + } + + // 3. Let calendar be ? ToTemporalCalendar(calendarLike). + auto* calendar = to_temporal_calendar(global_object, calendar_like); + if (vm.exception()) + return {}; + + // 4. Let instant be ! SystemInstant(). + auto* instant = system_instant(global_object); + + // 5. Return ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar). + return builtin_time_zone_get_plain_date_time_for(global_object, *time_zone, *instant, *calendar); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Now.h b/Userland/Libraries/LibJS/Runtime/Temporal/Now.h index b18652ee28..e22a44ee16 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/Now.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/Now.h @@ -21,10 +21,12 @@ public: private: JS_DECLARE_NATIVE_FUNCTION(time_zone); JS_DECLARE_NATIVE_FUNCTION(instant); + JS_DECLARE_NATIVE_FUNCTION(plain_date); }; TimeZone* system_time_zone(GlobalObject&); BigInt* system_utc_epoch_nanoseconds(GlobalObject&); Instant* system_instant(GlobalObject&); +PlainDateTime* system_date_time(GlobalObject&, Value temporal_time_zone_like, Value calendar_like); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp index 94e4e506c3..9259d168d5 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> + * Copyright (c) 2021, Linus Groh <linusg@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ @@ -10,6 +11,7 @@ #include <LibJS/Runtime/Temporal/PlainDate.h> #include <LibJS/Runtime/Temporal/PlainDateConstructor.h> #include <LibJS/Runtime/Temporal/PlainDateTime.h> +#include <LibJS/Runtime/Temporal/PlainYearMonth.h> namespace JS::Temporal { @@ -220,6 +222,102 @@ bool is_valid_iso_date(i32 year, u8 month, u8 day) return true; } +// 3.5.6 BalanceISODate ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisodate +ISODate balance_iso_date(i32 year, i32 month, i32 day) +{ + // 1. Assert: year, month, and day are integers. + + // 2. Let balancedYearMonth be ! BalanceISOYearMonth(year, month). + auto balanced_year_month = balance_iso_year_month(year, month); + + // 3. Set month to balancedYearMonth.[[Month]]. + month = balanced_year_month.month; + + // 4. Set year to balancedYearMonth.[[Year]]. + year = balanced_year_month.year; + + // 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. + + i32 test_year; + + // 6. If month > 2, then + if (month > 2) { + // a. Let testYear be year. + test_year = year; + } + // 7. Else, + else { + // a. Let testYear be year โ 1. + test_year = year - 1; + } + + // 8. Repeat, while day < โ1 ร ! ISODaysInYear(testYear), + while (day < -1 * iso_days_in_year(test_year)) { + // a.Set day to day + !ISODaysInYear(testYear). + day += iso_days_in_year(test_year); + + // b.Set year to year โ 1. + 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 = year + 1; + + // 11. Repeat, while day > ! ISODaysInYear(testYear), + while (day > iso_days_in_year(test_year)) { + // a. Set day to day โ ! ISODaysInYear(testYear). + day -= iso_days_in_year(test_year); + + // b. Set year to year + 1. + 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 (day < 1) { + // a. Set balancedYearMonth to ! BalanceISOYearMonth(year, month โ 1). + balanced_year_month = balance_iso_year_month(year, month - 1); + + // b. Set year to balancedYearMonth.[[Year]]. + year = balanced_year_month.year; + + // c. Set month to balancedYearMonth.[[Month]]. + month = balanced_year_month.month; + + // d. Set day to day + ! ISODaysInMonth(year, month). + day += iso_days_in_month(year, 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), + while (day > iso_days_in_month(year, month)) { + // a. Set day to day โ ! ISODaysInMonth(year, month). + day -= iso_days_in_month(year, month); + + // b. Set balancedYearMonth to ! BalanceISOYearMonth(year, month + 1). + balanced_year_month = balance_iso_year_month(year, month + 1); + + // c. Set year to balancedYearMonth.[[Year]]. + year = balanced_year_month.year; + + // d. Set month to balancedYearMonth.[[Month]]. + month = balanced_year_month.month; + } + + // 16. Return the new Record { [[Year]]: year, [[Month]]: month, [[Day]]: day }. + return ISODate { .year = year, .month = static_cast<u8>(month), .day = static_cast<u8>(day) }; +} + // 3.5.10 CompareISODate ( y1, m1, d1, y2, m2, d2 ), https://tc39.es/proposal-temporal/#sec-temporal-compareisodate i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2) { diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h index aca9a99fbe..6cab0c015d 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> + * Copyright (c) 2021, Linus Groh <linusg@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ @@ -34,10 +35,17 @@ private: Object& m_calendar; // [[Calendar]] }; +struct ISODate { + i32 year; + u8 month; + u8 day; +}; + PlainDate* create_temporal_date(GlobalObject&, i32 iso_year, u8 iso_month, u8 iso_day, Object& calendar, FunctionObject* new_target = nullptr); PlainDate* to_temporal_date(GlobalObject&, Value item, Object* options = nullptr); Optional<TemporalDate> regulate_iso_date(GlobalObject&, double year, double month, double day, String const& overflow); bool is_valid_iso_date(i32 year, u8 month, u8 day); +ISODate balance_iso_date(i32 year, i32 month, i32 day); i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp index 785edb4939..e4ba059cd5 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp @@ -92,6 +92,25 @@ bool iso_date_time_within_limits(GlobalObject& global_object, i32 year, u8 month return true; } +// 5.5.5 BalanceISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisodatetime +ISODateTime balance_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, i64 nanosecond) +{ + // NOTE: The only use of this AO is in BuiltinTimeZoneGetPlainDateTimeFor, where we know that all values + // but `nanosecond` are in their usual range, hence why that's the only outlier here. The range for that + // is -86400000000000 to 86400000000999, so an i32 is not enough. + + // 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). + auto balanced_time = balance_time(hour, minute, second, millisecond, microsecond, nanosecond); + + // 3. Let balancedDate be ! BalanceISODate(year, month, day + balancedTime.[[Days]]). + auto balanced_date = balance_iso_date(year, month, day + balanced_time.days); + + // 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 ISODateTime { .year = balanced_date.year, .month = balanced_date.month, .day = balanced_date.day, .hour = balanced_time.hour, .minute = balanced_time.minute, .second = balanced_time.second, .millisecond = balanced_time.millisecond, .microsecond = balanced_time.microsecond, .nanosecond = balanced_time.nanosecond }; +} + // 5.5.6 CreateTemporalDateTime ( isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaldatetime PlainDateTime* create_temporal_date_time(GlobalObject& global_object, i32 iso_year, u8 iso_month, u8 iso_day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, FunctionObject* new_target) { diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h index 89ec45a53e..9e062565d0 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h @@ -7,7 +7,8 @@ #pragma once -#include <LibJS/Runtime/BigInt.h> +#include <LibJS/Runtime/Object.h> +#include <LibJS/Runtime/Temporal/AbstractOperations.h> namespace JS::Temporal { @@ -48,6 +49,7 @@ private: BigInt* get_epoch_from_iso_parts(GlobalObject&, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond); bool iso_date_time_within_limits(GlobalObject&, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond); +ISODateTime balance_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, i64 nanosecond); PlainDateTime* create_temporal_date_time(GlobalObject&, i32 iso_year, u8 iso_month, u8 iso_day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, FunctionObject* new_target = nullptr); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp index da1487cc65..740256a6e1 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp @@ -54,4 +54,57 @@ bool is_valid_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microseco return true; } +// 4.5.6 BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-balancetime +DaysAndTime balance_time(i64 hour, i64 minute, i64 second, i64 millisecond, i64 microsecond, i64 nanosecond) +{ + // 1. Assert: hour, minute, second, millisecond, microsecond, and nanosecond are integers. + + // 2. Set microsecond to microsecond + floor(nanosecond / 1000). + microsecond += nanosecond / 1000; + + // 3. Set nanosecond to nanosecond modulo 1000. + nanosecond %= 1000; + + // 4. Set millisecond to millisecond + floor(microsecond / 1000). + millisecond += microsecond / 1000; + + // 5. Set microsecond to microsecond modulo 1000. + microsecond %= 1000; + + // 6. Set second to second + floor(millisecond / 1000). + second += millisecond / 1000; + + // 7. Set millisecond to millisecond modulo 1000. + millisecond %= 1000; + + // 8. Set minute to minute + floor(second / 60). + minute += second / 60; + + // 9. Set second to second modulo 60. + second %= 60; + + // 10. Set hour to hour + floor(minute / 60). + hour += minute / 60; + + // 11. Set minute to minute modulo 60. + minute %= 60; + + // 12. Let days be floor(hour / 24). + u8 days = hour / 24; + + // 13. Set hour to hour modulo 24. + hour %= 24; + + // 14. Return the new Record { [[Days]]: days, [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond }. + return DaysAndTime { + .days = static_cast<i32>(days), + .hour = static_cast<u8>(hour), + .minute = static_cast<u8>(minute), + .second = static_cast<u8>(second), + .millisecond = static_cast<u16>(millisecond), + .microsecond = static_cast<u16>(microsecond), + .nanosecond = static_cast<u16>(nanosecond), + }; +} + } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h index 748298071e..1c85fa946f 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h @@ -10,6 +10,17 @@ namespace JS::Temporal { +struct DaysAndTime { + i32 days; + u8 hour; + u8 minute; + u8 second; + u16 millisecond; + u16 microsecond; + u16 nanosecond; +}; + bool is_valid_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond); +DaysAndTime balance_time(i64 hour, i64 minute, i64 second, i64 millisecond, i64 microsecond, i64 nanosecond); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp new file mode 100644 index 0000000000..330e139eaa --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021, Linus Groh <linusg@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibJS/Runtime/Temporal/PlainYearMonth.h> + +namespace JS::Temporal { + +// 9.5.5 BalanceISOYearMonth ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisoyearmonth +ISOYearMonth balance_iso_year_month(i32 year, i32 month) +{ + // 1. Assert: year and month are integers. + + // 2. Set year to year + floor((month - 1) / 12). + year += (month - 1) / 12; + + // 3. Set month to (month โ 1) modulo 12 + 1. + month = (month - 1) % 12 + 1; + + // 4. Return the new Record { [[Year]]: year, [[Month]]: month }. + return ISOYearMonth { .year = year, .month = static_cast<u8>(month) }; +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.h new file mode 100644 index 0000000000..00f3df8756 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2021, Linus Groh <linusg@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibJS/Runtime/Temporal/AbstractOperations.h> +#include <LibJS/Runtime/Value.h> + +namespace JS::Temporal { + +struct ISOYearMonth { + i32 year; + u8 month; +}; + +ISOYearMonth balance_iso_year_month(i32 year, i32 month); + +} diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp index 2240f103e9..cb6b521c57 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp @@ -5,8 +5,13 @@ */ #include <AK/DateTimeLexer.h> +#include <LibCrypto/BigInt/UnsignedBigInteger.h> #include <LibJS/Runtime/AbstractOperations.h> +#include <LibJS/Runtime/Date.h> #include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/Temporal/AbstractOperations.h> +#include <LibJS/Runtime/Temporal/Instant.h> +#include <LibJS/Runtime/Temporal/PlainDateTime.h> #include <LibJS/Runtime/Temporal/TimeZone.h> #include <LibJS/Runtime/Temporal/TimeZoneConstructor.h> @@ -59,6 +64,26 @@ String default_time_zone() return "UTC"; } +// 11.6.1 ParseTemporalTimeZone ( string ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimezone +String parse_temporal_time_zone(GlobalObject& global_object, String const& string) +{ + auto& vm = global_object.vm(); + + // 1. Assert: Type(string) is String. + + // 2. Let result be ? ParseTemporalTimeZoneString(string). + auto result = parse_temporal_time_zone_string(global_object, string); + if (vm.exception()) + return {}; + + // 3. If result.[[Z]] is not undefined, return "UTC". + if (result->z) + return "UTC"; + + // 4. Return result.[[Name]]. + return *result->name; +} + // 11.6.2 CreateTemporalTimeZone ( identifier [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaltimezone TimeZone* create_temporal_time_zone(GlobalObject& global_object, String const& identifier, FunctionObject* new_target) { @@ -92,6 +117,48 @@ TimeZone* create_temporal_time_zone(GlobalObject& global_object, String const& i return object; } +// 11.6.3 GetISOPartsFromEpoch ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-getisopartsfromepoch +Optional<ISODateTime> get_iso_parts_from_epoch(BigInt const& epoch_nanoseconds) +{ + // 1. Let remainderNs be epochNanoseconds modulo 10^6. + auto remainder_ns_bigint = epoch_nanoseconds.big_integer().divided_by(Crypto::UnsignedBigInteger { 1'000'000 }).remainder; + auto remainder_ns = remainder_ns_bigint.to_base(10).to_int<i64>().value(); + + // 2. Let epochMilliseconds be (epochNanoseconds โ remainderNs) / 10^6. + auto epoch_milliseconds_bigint = epoch_nanoseconds.big_integer().minus(remainder_ns_bigint).divided_by(Crypto::UnsignedBigInteger { 1'000'000 }).quotient; + auto epoch_milliseconds = (double)epoch_milliseconds_bigint.to_base(10).to_int<i64>().value(); + + // 3. Let year be ! YearFromTime(epochMilliseconds). + auto year = year_from_time(epoch_milliseconds); + + // 4. Let month be ! MonthFromTime(epochMilliseconds) + 1. + auto month = static_cast<u8>(month_from_time(epoch_milliseconds) + 1); + + // 5. Let day be ! DateFromTime(epochMilliseconds). + auto day = date_from_time(epoch_milliseconds); + + // 6. Let hour be ! HourFromTime(epochMilliseconds). + auto hour = hour_from_time(epoch_milliseconds); + + // 7. Let minute be ! MinFromTime(epochMilliseconds). + auto minute = min_from_time(epoch_milliseconds); + + // 8. Let second be ! SecFromTime(epochMilliseconds). + auto second = sec_from_time(epoch_milliseconds); + + // 9. Let millisecond be ! msFromTime(epochMilliseconds). + auto millisecond = ms_from_time(epoch_milliseconds); + + // 10. Let microsecond be floor(remainderNs / 1000) modulo 1000. + auto microsecond = static_cast<u16>((remainder_ns / 1000) % 1000); + + // 11. Let nanosecond be remainderNs modulo 1000. + auto nanosecond = static_cast<u16>(remainder_ns % 1000); + + // 12. Return the new Record { [[Year]]: year, [[Month]]: month, [[Day]]: day, [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond }. + return ISODateTime { .year = year, .month = month, .day = day, .hour = hour, .minute = minute, .second = second, .millisecond = millisecond, .microsecond = microsecond, .nanosecond = nanosecond }; +} + // 11.6.5 GetIANATimeZoneOffsetNanoseconds ( epochNanoseconds, timeZoneIdentifier ), https://tc39.es/proposal-temporal/#sec-temporal-getianatimezoneoffsetnanoseconds i64 get_iana_time_zone_offset_nanoseconds([[maybe_unused]] BigInt const& epoch_nanoseconds, [[maybe_unused]] String const& time_zone_identifier) { @@ -252,4 +319,115 @@ String format_time_zone_offset_string(double offset_nanoseconds) return builder.to_string(); } +// 11.6.10 ToTemporalTimeZone ( temporalTimeZoneLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimezone +Object* to_temporal_time_zone(GlobalObject& global_object, Value temporal_time_zone_like) +{ + auto& vm = global_object.vm(); + + // 1. If Type(temporalTimeZoneLike) is Object, then + if (temporal_time_zone_like.is_object()) { + // TODO: + // a. If temporalTimeZoneLike has an [[InitializedTemporalZonedDateTime]] internal slot, then + // i. Return temporalTimeZoneLike.[[TimeZone]]. + + // b. If ? HasProperty(temporalTimeZoneLike, "timeZone") is false, return temporalTimeZoneLike. + auto has_property = temporal_time_zone_like.as_object().has_property(vm.names.timeZone); + if (vm.exception()) + return {}; + if (!has_property) + return &temporal_time_zone_like.as_object(); + + // c. Set temporalTimeZoneLike to ? Get(temporalTimeZoneLike, "timeZone"). + temporal_time_zone_like = temporal_time_zone_like.as_object().get(vm.names.timeZone); + if (vm.exception()) + return {}; + + // d. If Type(temporalTimeZoneLike) is Object and ? HasProperty(temporalTimeZoneLike, "timeZone") is false, return temporalTimeZoneLike. + if (temporal_time_zone_like.is_object()) { + has_property = temporal_time_zone_like.as_object().has_property(vm.names.timeZone); + if (vm.exception()) + return {}; + if (!has_property) + return &temporal_time_zone_like.as_object(); + } + } + + // 2. Let identifier be ? ToString(temporalTimeZoneLike). + auto identifier = temporal_time_zone_like.to_string(global_object); + if (vm.exception()) + return {}; + + // 3. Let result be ? ParseTemporalTimeZone(identifier). + auto result = parse_temporal_time_zone(global_object, identifier); + if (vm.exception()) + return {}; + + // 4. Return ? CreateTemporalTimeZone(result). + return create_temporal_time_zone(global_object, result); +} + +// 11.6.11 GetOffsetNanosecondsFor ( timeZone, instant ), https://tc39.es/proposal-temporal/#sec-temporal-getoffsetnanosecondsfor +double get_offset_nanoseconds_for(GlobalObject& global_object, Object& time_zone, Instant& instant) +{ + auto& vm = global_object.vm(); + + // 1. Let getOffsetNanosecondsFor be ? GetMethod(timeZone, "getOffsetNanosecondsFor"). + auto* get_offset_nanoseconds_for = Value(&time_zone).get_method(global_object, vm.names.getOffsetNanosecondsFor); + if (vm.exception()) + return {}; + + // 2. If getOffsetNanosecondsFor is undefined, set getOffsetNanosecondsFor to %Temporal.TimeZone.prototype.getOffsetNanosecondsFor%. + if (!get_offset_nanoseconds_for) + get_offset_nanoseconds_for = global_object.temporal_time_zone_prototype_get_offset_nanoseconds_for_function(); + + // 3. Let offsetNanoseconds be ? Call(getOffsetNanosecondsFor, timeZone, ยซ instant ยป). + auto offset_nanoseconds_value = vm.call(*get_offset_nanoseconds_for, &time_zone, &instant); + if (vm.exception()) + return {}; + + // 4. If Type(offsetNanoseconds) is not Number, throw a TypeError exception. + if (!offset_nanoseconds_value.is_number()) { + vm.throw_exception<TypeError>(global_object, ErrorType::IsNotA, "Offset nanoseconds value", "number"); + return {}; + } + + // 5. If ! IsIntegralNumber(offsetNanoseconds) is false, throw a RangeError exception. + if (!offset_nanoseconds_value.is_integral_number()) { + vm.throw_exception<TypeError>(global_object, ErrorType::IsNotA, "Offset nanoseconds value", "integral number"); + return {}; + } + + // 6. Set offsetNanoseconds to โ(offsetNanoseconds). + auto offset_nanoseconds = offset_nanoseconds_value.as_double(); + + // 7. If abs(offsetNanoseconds) > 86400 ร 10^9, throw a RangeError exception. + if (fabs(offset_nanoseconds) > 86400000000000.0) { + vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidOffsetNanosecondsValue); + return {}; + } + + // 8. Return offsetNanoseconds. + return offset_nanoseconds; +} + +// 11.6.13 BuiltinTimeZoneGetPlainDateTimeFor ( timeZone, instant, calendar ), https://tc39.es/proposal-temporal/#sec-temporal-builtintimezonegetplaindatetimefor +PlainDateTime* builtin_time_zone_get_plain_date_time_for(GlobalObject& global_object, Object& time_zone, Instant& instant, Object& calendar) +{ + auto& vm = global_object.vm(); + + // 1. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant). + auto offset_nanoseconds = get_offset_nanoseconds_for(global_object, time_zone, instant); + if (vm.exception()) + return {}; + + // 2. Let result be ! GetISOPartsFromEpoch(instant.[[Nanoseconds]]). + auto result = get_iso_parts_from_epoch(instant.nanoseconds()); + + // 3. Set result to ! BalanceISODateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]] + offsetNanoseconds). + result = balance_iso_date_time(result->year, result->month, result->day, result->hour, result->minute, result->second, result->millisecond, result->microsecond, result->nanosecond + offset_nanoseconds); + + // 4. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], calendar). + return create_temporal_date_time(global_object, result->year, result->month, result->day, result->hour, result->minute, result->second, result->millisecond, result->microsecond, result->nanosecond, calendar); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h index adf94b8f14..1821c11669 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.h @@ -8,6 +8,7 @@ #include <AK/Optional.h> #include <LibJS/Runtime/Object.h> +#include <LibJS/Runtime/Temporal/AbstractOperations.h> namespace JS::Temporal { @@ -38,10 +39,15 @@ private: bool is_valid_time_zone_name(String const& time_zone); String canonicalize_time_zone_name(String const& time_zone); String default_time_zone(); +String parse_temporal_time_zone(GlobalObject&, String const&); TimeZone* create_temporal_time_zone(GlobalObject&, String const& identifier, FunctionObject* new_target = nullptr); +Optional<ISODateTime> get_iso_parts_from_epoch(BigInt const& epoch_nanoseconds); i64 get_iana_time_zone_offset_nanoseconds(BigInt const& epoch_nanoseconds, String const& time_zone_identifier); double parse_time_zone_offset_string(GlobalObject&, String const&); String format_time_zone_offset_string(double offset_nanoseconds); +Object* to_temporal_time_zone(GlobalObject&, Value temporal_time_zone_like); +double get_offset_nanoseconds_for(GlobalObject&, Object& time_zone, Instant&); +PlainDateTime* builtin_time_zone_get_plain_date_time_for(GlobalObject&, Object& time_zone, Instant&, Object& calendar); bool is_valid_time_zone_numeric_utc_offset_syntax(String const&); |