feat: Temporal.PlainTime until/since 差值计算

This commit is contained in:
2026-08-17 02:40:21 +08:00
parent bed1abd3f3
commit 0037f0e5ad
3 changed files with 84 additions and 0 deletions
+44
View File
@@ -4592,6 +4592,50 @@ pub fn plain_time_compare<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::int(result))
}
// ───────────────────── PlainTime until / since ─────────────────────
/// until/since 核心:receiver 与 other 归一为午夜后纳秒,经 `difference_core` 计算差值。
///
/// # 步骤
/// 1. branding receiver 取槽 0 ns,other 经 `plain_time_like_ns` 归一。
/// 2. `parse_difference_settings` 解析 options(default_largest=4=hour)。
/// 3. 日期固定为 epoch(0,0,0),调 `difference_core` 输出 Duration。
///
/// # 边界与前提
/// - since 复用 same 语义并整体取反(difference_core 内部处理),不调换两端。
fn plain_time_difference<H: VmHost>(vm: &mut H, args: &[u8], since: bool) -> NativeResult {
let ptr = match receiver_obj(vm, args) {
Ok(p) => p,
Err(error) => return NativeResult::Err(error),
};
let obj = unsafe { &*ptr };
if let Err(error) = ensure_plain_time(vm, obj) {
return NativeResult::Err(error);
}
let receiver_ns = get_double_prop(obj, 0);
let other_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let other_ns = match plain_time_like_ns(vm, other_val) {
Ok(ns) => ns,
Err(error) => return NativeResult::Err(error),
};
let options_value = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
let settings = match parse_difference_settings(vm, options_value, false, 4) {
Ok(settings) => settings,
Err(error) => return NativeResult::Err(error),
};
difference_core(vm, (0, 0, 0), receiver_ns as i128, (0, 0, 0), other_ns as i128, settings, since)
}
/// `Temporal.PlainTime.prototype.until(other, options)`。
pub fn plain_time_until<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
plain_time_difference(vm, args, false)
}
/// `Temporal.PlainTime.prototype.since(other, options)`。
pub fn plain_time_since<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
plain_time_difference(vm, args, true)
}
// ───────────────────── PlainDateTime 基础方法 ─────────────────────
/// `Temporal.PlainDateTime` 构造器:保存 ISO 日期与午夜后纳秒。
@@ -692,6 +692,8 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
),
("valueOf", oxide_builtins::temporal::plain_time_value_of::<crate::vm::Vm> as *const (), 0),
("equals", oxide_builtins::temporal::plain_time_equals::<crate::vm::Vm> as *const (), 1),
("until", oxide_builtins::temporal::plain_time_until::<crate::vm::Vm> as *const (), 1),
("since", oxide_builtins::temporal::plain_time_since::<crate::vm::Vm> as *const (), 1),
],
);
bind_well_known_data_property(
@@ -668,6 +668,44 @@ fn plain_time_compare_epoch_order() {
assert_eq!(num(&mut vm, "Temporal.PlainTime.compare('23:59', '00:00')"), 1.0);
}
#[test]
fn plain_time_until_since_normal() {
let mut vm = Vm::new();
let r = eval(&mut vm, "new Temporal.PlainTime(12, 30).until(new Temporal.PlainTime(13, 30)).toString()").unwrap();
assert_eq!(str_val(&vm, r), "PT1H");
let r = eval(&mut vm, "new Temporal.PlainTime(13, 30).since(new Temporal.PlainTime(12, 30)).toString()").unwrap();
assert_eq!(str_val(&vm, r), "PT1H");
let r = eval(
&mut vm,
"new Temporal.PlainTime(12, 30).until(new Temporal.PlainTime(13, 30, 5, 500)).toString()",
)
.unwrap();
assert_eq!(str_val(&vm, r), "PT1H5.5S");
}
#[test]
fn plain_time_until_since_options_and_direction() {
let mut vm = Vm::new();
let r = eval(
&mut vm,
"new Temporal.PlainTime(12, 30).until(new Temporal.PlainTime(13, 35), { largestUnit: 'minute' }).toString()",
)
.unwrap();
assert_eq!(str_val(&vm, r), "PT65M");
let r = eval(
&mut vm,
"new Temporal.PlainTime(12, 30).since(new Temporal.PlainTime(13, 35), { largestUnit: 'minute' }).toString()",
)
.unwrap();
assert_eq!(str_val(&vm, r), "-PT65M");
let r = eval(
&mut vm,
"new Temporal.PlainTime(12, 30).until(new Temporal.PlainTime(13, 30), { smallestUnit: 'hour' }).toString()",
)
.unwrap();
assert_eq!(str_val(&vm, r), "PT1H");
}
// -- Temporal.PlainDateTime --
#[test]