feat: implement Temporal duration and plain date differences
This commit is contained in:
@@ -5,7 +5,7 @@ use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
/// 把 Temporal 命名空间及其 Now/Instant/PlainDate/PlainTime 子对象绑定到 global。
|
||||
/// 把 Temporal 命名空间及其子对象绑定到 global。
|
||||
pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
|
||||
let world = session.builtin_world();
|
||||
let temporal_ptr = world.temporal_object.as_ptr() as *mut JsObject;
|
||||
@@ -20,11 +20,7 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
||||
core,
|
||||
&[
|
||||
("instant", oxide_builtins::temporal::now_instant::<crate::vm::Vm> as *const (), 0),
|
||||
(
|
||||
"timeZoneId",
|
||||
oxide_builtins::temporal::now_time_zone_id::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
),
|
||||
("timeZoneId", oxide_builtins::temporal::now_time_zone_id::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -218,12 +214,18 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
||||
plain_date_proto,
|
||||
core,
|
||||
&[
|
||||
("toString", oxide_builtins::temporal::plain_date_to_string::<crate::vm::Vm> as *const (), 0),
|
||||
(
|
||||
"toString",
|
||||
oxide_builtins::temporal::plain_date_to_string::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
),
|
||||
("toJSON", oxide_builtins::temporal::plain_date_to_json::<crate::vm::Vm> as *const (), 0),
|
||||
("valueOf", oxide_builtins::temporal::plain_date_value_of::<crate::vm::Vm> as *const (), 0),
|
||||
("equals", oxide_builtins::temporal::plain_date_equals::<crate::vm::Vm> as *const (), 1),
|
||||
("add", oxide_builtins::temporal::plain_date_add::<crate::vm::Vm> as *const (), 1),
|
||||
("subtract", oxide_builtins::temporal::plain_date_subtract::<crate::vm::Vm> as *const (), 1),
|
||||
("until", oxide_builtins::temporal::plain_date_until::<crate::vm::Vm> as *const (), 1),
|
||||
("since", oxide_builtins::temporal::plain_date_since::<crate::vm::Vm> as *const (), 1),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -283,7 +285,62 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
||||
world,
|
||||
plain_time_proto,
|
||||
core,
|
||||
&[("toString", oxide_builtins::temporal::plain_time_to_string::<crate::vm::Vm> as *const (), 0)],
|
||||
&[(
|
||||
"toString",
|
||||
oxide_builtins::temporal::plain_time_to_string::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
)],
|
||||
);
|
||||
|
||||
// Temporal.Duration:构造器、from、分量 getter 与 ISO 字符串转换。
|
||||
let duration_ctor_ptr = world.duration_constructor.as_ptr() as *mut JsObject;
|
||||
let duration_ctor = unsafe { &mut *duration_ctor_ptr };
|
||||
configure_native_constructor(
|
||||
duration_ctor,
|
||||
oxide_builtins::temporal::duration_constructor::<crate::vm::Vm> as *const (),
|
||||
10,
|
||||
);
|
||||
apply_binding_table(
|
||||
world,
|
||||
duration_ctor,
|
||||
core,
|
||||
&[("from", oxide_builtins::temporal::duration_from::<crate::vm::Vm> as *const (), 1)],
|
||||
);
|
||||
let duration_proto_ptr = world.duration_proto.as_ptr() as *mut JsObject;
|
||||
let duration_proto = unsafe { &mut *duration_proto_ptr };
|
||||
for (name, getter) in [
|
||||
("years", oxide_builtins::temporal::duration_years::<crate::vm::Vm> as *const ()),
|
||||
("months", oxide_builtins::temporal::duration_months::<crate::vm::Vm> as *const ()),
|
||||
("weeks", oxide_builtins::temporal::duration_weeks::<crate::vm::Vm> as *const ()),
|
||||
("days", oxide_builtins::temporal::duration_days::<crate::vm::Vm> as *const ()),
|
||||
("hours", oxide_builtins::temporal::duration_hours::<crate::vm::Vm> as *const ()),
|
||||
("minutes", oxide_builtins::temporal::duration_minutes::<crate::vm::Vm> as *const ()),
|
||||
("seconds", oxide_builtins::temporal::duration_seconds::<crate::vm::Vm> as *const ()),
|
||||
(
|
||||
"milliseconds",
|
||||
oxide_builtins::temporal::duration_milliseconds::<crate::vm::Vm> as *const (),
|
||||
),
|
||||
(
|
||||
"microseconds",
|
||||
oxide_builtins::temporal::duration_microseconds::<crate::vm::Vm> as *const (),
|
||||
),
|
||||
(
|
||||
"nanoseconds",
|
||||
oxide_builtins::temporal::duration_nanoseconds::<crate::vm::Vm> as *const (),
|
||||
),
|
||||
] {
|
||||
bind_accessor_getter(core, session, duration_proto, name, getter);
|
||||
}
|
||||
apply_binding_table(
|
||||
world,
|
||||
duration_proto,
|
||||
core,
|
||||
&[
|
||||
("abs", oxide_builtins::temporal::duration_abs::<crate::vm::Vm> as *const (), 0),
|
||||
("negated", oxide_builtins::temporal::duration_negated::<crate::vm::Vm> as *const (), 0),
|
||||
("toString", oxide_builtins::temporal::duration_to_string::<crate::vm::Vm> as *const (), 0),
|
||||
("valueOf", oxide_builtins::temporal::duration_value_of::<crate::vm::Vm> as *const (), 0),
|
||||
],
|
||||
);
|
||||
|
||||
// 把子对象挂到 Temporal 命名空间对象上,再把 Temporal 挂到 global。
|
||||
@@ -291,5 +348,6 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
||||
bind_global_value(core, temporal, "Instant", JsValue::from_js_object(instant_ctor_ptr));
|
||||
bind_global_value(core, temporal, "PlainDate", JsValue::from_js_object(plain_date_ctor_ptr));
|
||||
bind_global_value(core, temporal, "PlainTime", JsValue::from_js_object(plain_time_ctor_ptr));
|
||||
bind_global_value(core, temporal, "Duration", JsValue::from_js_object(duration_ctor_ptr));
|
||||
bind_global_value(core, global, "Temporal", JsValue::from_js_object(temporal_ptr));
|
||||
}
|
||||
|
||||
@@ -112,8 +112,11 @@ fn instant_value_of_throws() {
|
||||
#[test]
|
||||
fn instant_method_on_non_instant_throws() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(&mut vm, "try { Temporal.Instant.prototype.toString.call({}) } catch (e) { e.constructor.name }")
|
||||
.unwrap();
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"try { Temporal.Instant.prototype.toString.call({}) } catch (e) { e.constructor.name }",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(str_val(&vm, r), "TypeError");
|
||||
}
|
||||
|
||||
@@ -188,6 +191,58 @@ fn plain_date_method_on_non_date_throws() {
|
||||
assert_eq!(str_val(&vm, r), "TypeError");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plain_date_add_and_subtract_support_negative_offsets() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(&mut vm, "new Temporal.PlainDate(2024, 1, 31).add({months:1}).toString()").unwrap();
|
||||
assert_eq!(str_val(&vm, r), "2024-02-29");
|
||||
let r = eval(&mut vm, "new Temporal.PlainDate(2024, 1, 5).subtract({days:10}).toString()").unwrap();
|
||||
assert_eq!(str_val(&vm, r), "2023-12-26");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plain_date_until_and_since_return_duration() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(&mut vm, "new Temporal.PlainDate(2024, 1, 1).until('2024-01-06').toString()").unwrap();
|
||||
assert_eq!(str_val(&vm, r), "P5D");
|
||||
assert_eq!(
|
||||
num(
|
||||
&mut vm,
|
||||
"new Temporal.PlainDate(2024, 1, 6).since(new Temporal.PlainDate(2024, 1, 11)).days",
|
||||
),
|
||||
-5.0
|
||||
);
|
||||
assert_eq!(
|
||||
num(
|
||||
&mut vm,
|
||||
"new Temporal.PlainDate(1997, 7, 16).since(new Temporal.PlainDate(2021, 7, 15), {largestUnit:'years'}).days",
|
||||
),
|
||||
-29.0
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duration_constructor_exposes_components() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(&mut vm, "new Temporal.Duration(1,2,3,4,5,6,7,8,9,10).toString()").unwrap();
|
||||
assert_eq!(str_val(&vm, r), "P1Y2M3W4DT5H6M7.00800901S");
|
||||
assert_eq!(num(&mut vm, "new Temporal.Duration(1,2,3,4,5,6,7,8,9,10).nanoseconds"), 10.0);
|
||||
let r = eval(&mut vm, "Temporal.Duration.from('-PT24.567890123H').toString()").unwrap();
|
||||
assert_eq!(str_val(&vm, r), "-PT24H34M4.4044428S");
|
||||
assert_eq!(num(&mut vm, "Temporal.Duration.from('P43Y').years"), 43.0);
|
||||
let r = eval(&mut vm, "new Temporal.Duration(1,2,3,4).negated().abs().toString()").unwrap();
|
||||
assert_eq!(str_val(&vm, r), "P1Y2M3W4D");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plain_date_balances_duration_time_units_into_days() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(&mut vm, "new Temporal.PlainDate(2000,5,2).add('P1DT24H1440M86400S').toString()").unwrap();
|
||||
assert_eq!(str_val(&vm, r), "2000-05-06");
|
||||
let r = eval(&mut vm, "new Temporal.PlainDate(2000,5,2).add('-PT24.567890123H').toString()").unwrap();
|
||||
assert_eq!(str_val(&vm, r), "2000-05-01");
|
||||
}
|
||||
|
||||
// -- Temporal.PlainTime --
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user