feat: Temporal.PlainTime equals/compare 比较语义
This commit is contained in:
@@ -4550,6 +4550,48 @@ pub fn plain_time_from<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Temporal.PlainTime.prototype.equals(other)`:各分量全等返回 true,否则 false。
|
||||||
|
///
|
||||||
|
/// # 边界与前提
|
||||||
|
/// - other 经 `plain_time_like_ns` 归一(实例/字符串/property bag),无法转换抛 TypeError/RangeError。
|
||||||
|
pub fn plain_time_equals<H: VmHost>(vm: &mut H, args: &[u8]) -> 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),
|
||||||
|
};
|
||||||
|
NativeResult::Ok(JsValue::bool(receiver_ns as i128 == other_ns as i128))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Temporal.PlainTime.compare(one, two)`:静态比较,按午夜后纳秒返回 -1、0 或 1。
|
||||||
|
pub fn plain_time_compare<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let one = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||||
|
let two = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||||
|
let one_ns = match plain_time_like_ns(vm, one) {
|
||||||
|
Ok(ns) => ns,
|
||||||
|
Err(error) => return NativeResult::Err(error),
|
||||||
|
};
|
||||||
|
let two_ns = match plain_time_like_ns(vm, two) {
|
||||||
|
Ok(ns) => ns,
|
||||||
|
Err(error) => return NativeResult::Err(error),
|
||||||
|
};
|
||||||
|
let result = match (one_ns as i128).cmp(&(two_ns as i128)) {
|
||||||
|
std::cmp::Ordering::Less => -1,
|
||||||
|
std::cmp::Ordering::Equal => 0,
|
||||||
|
std::cmp::Ordering::Greater => 1,
|
||||||
|
};
|
||||||
|
NativeResult::Ok(JsValue::int(result))
|
||||||
|
}
|
||||||
|
|
||||||
// ───────────────────── PlainDateTime 基础方法 ─────────────────────
|
// ───────────────────── PlainDateTime 基础方法 ─────────────────────
|
||||||
|
|
||||||
/// `Temporal.PlainDateTime` 构造器:保存 ISO 日期与午夜后纳秒。
|
/// `Temporal.PlainDateTime` 构造器:保存 ISO 日期与午夜后纳秒。
|
||||||
|
|||||||
@@ -624,7 +624,10 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
|||||||
world,
|
world,
|
||||||
plain_time_ctor,
|
plain_time_ctor,
|
||||||
core,
|
core,
|
||||||
&[("from", oxide_builtins::temporal::plain_time_from::<crate::vm::Vm> as *const (), 1)],
|
&[
|
||||||
|
("from", oxide_builtins::temporal::plain_time_from::<crate::vm::Vm> as *const (), 1),
|
||||||
|
("compare", oxide_builtins::temporal::plain_time_compare::<crate::vm::Vm> as *const (), 2),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
let plain_time_proto_ptr = world.plain_time_proto.as_ptr() as *mut JsObject;
|
let plain_time_proto_ptr = world.plain_time_proto.as_ptr() as *mut JsObject;
|
||||||
@@ -688,6 +691,7 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
|||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
("valueOf", oxide_builtins::temporal::plain_time_value_of::<crate::vm::Vm> as *const (), 0),
|
("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),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
bind_well_known_data_property(
|
bind_well_known_data_property(
|
||||||
|
|||||||
@@ -617,6 +617,57 @@ fn plain_time_range_validation() {
|
|||||||
assert_eq!(str_val(&vm, r), "RangeError");
|
assert_eq!(str_val(&vm, r), "RangeError");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plain_time_equals_same_and_different_fields() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let r = eval(&mut vm, "new Temporal.PlainTime(12, 30, 5).equals(new Temporal.PlainTime(12, 30, 5))").unwrap();
|
||||||
|
assert!(r.as_bool());
|
||||||
|
let r = eval(&mut vm, "new Temporal.PlainTime(12, 30, 5).equals(new Temporal.PlainTime(12, 30, 6))").unwrap();
|
||||||
|
assert!(!r.as_bool());
|
||||||
|
let r = eval(&mut vm, "new Temporal.PlainTime(12, 30, 5).equals('12:30:05')").unwrap();
|
||||||
|
assert!(r.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plain_time_equals_invalid_argument_throws() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let r = eval(&mut vm, "try { new Temporal.PlainTime(12).equals(42) } catch (e) { e.constructor.name }").unwrap();
|
||||||
|
assert_eq!(str_val(&vm, r), "TypeError");
|
||||||
|
let r = eval(
|
||||||
|
&mut vm,
|
||||||
|
"try { Temporal.PlainTime.prototype.equals.call({}) } catch (e) { e.constructor.name }",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(str_val(&vm, r), "TypeError");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plain_time_compare_epoch_order() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
assert_eq!(
|
||||||
|
num(
|
||||||
|
&mut vm,
|
||||||
|
"Temporal.PlainTime.compare(new Temporal.PlainTime(12, 30), new Temporal.PlainTime(13, 30))"
|
||||||
|
),
|
||||||
|
-1.0
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
num(
|
||||||
|
&mut vm,
|
||||||
|
"Temporal.PlainTime.compare(new Temporal.PlainTime(12, 30), new Temporal.PlainTime(12, 30))"
|
||||||
|
),
|
||||||
|
0.0
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
num(
|
||||||
|
&mut vm,
|
||||||
|
"Temporal.PlainTime.compare(new Temporal.PlainTime(13, 30), new Temporal.PlainTime(12, 30))"
|
||||||
|
),
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
assert_eq!(num(&mut vm, "Temporal.PlainTime.compare('23:59', '00:00')"), 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
// -- Temporal.PlainDateTime --
|
// -- Temporal.PlainDateTime --
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user