1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 16:27:35 +00:00

LibJS: Implement Temporal.Now.plainDate()

...and ten required AOs we didn't have yet:

- BalanceISODate
- BalanceISODateTime
- BalanceISOYearMonth
- BalanceTime
- BuiltinTimeZoneGetPlainDateTimeFor
- GetISOPartsFromEpoch
- GetOffsetNanosecondsFor
- ParseTemporalTimeZone
- SystemDateTime
- ToTemporalTimeZone
This commit is contained in:
Linus Groh 2021-07-27 00:21:16 +01:00
parent 5512ff79f0
commit c303bbde54
18 changed files with 507 additions and 1 deletions

View file

@ -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);
}
}