mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: ZonedDateTime.compare 跨实例按 epoch 比较(复用 from 解析器,忽略时区/日历)
This commit is contained in:
@@ -1734,6 +1734,28 @@ pub fn zoned_date_time_from<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
make_zoned_date_time(vm, epoch_ns, &time_zone_id, &calendar_id)
|
||||
}
|
||||
|
||||
/// `Temporal.ZonedDateTime.compare(one, two)`:按纪元纳秒比较两 ZDT,忽略时区 ID 与日历。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 两参数各经 zoned_date_time_like_epoch_ns 解析(默认 offset=reject + disambiguation=compatible)。
|
||||
/// 2. 仅比较 epoch 纳秒,返回 -1/0/1。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 参数可为 ZDT 对象 / ISO 字符串 / property bag;解析失败抛错。
|
||||
/// - 同 epoch 不同时区或日历恒相等(比较不读时区/日历槽)。
|
||||
pub fn zoned_date_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 = native_try!(zoned_date_time_like_epoch_ns(vm, one, "reject", "compatible"));
|
||||
let two = native_try!(zoned_date_time_like_epoch_ns(vm, two, "reject", "compatible"));
|
||||
let ordering = one.0.cmp(&two.0);
|
||||
NativeResult::Ok(JsValue::int(match ordering {
|
||||
std::cmp::Ordering::Less => -1,
|
||||
std::cmp::Ordering::Equal => 0,
|
||||
std::cmp::Ordering::Greater => 1,
|
||||
}))
|
||||
}
|
||||
|
||||
/// 时区注解文本:命名区原样返回,偏移区规范化为 `±HH:MM` 带冒号;critical 时 `!` 置于括号内。
|
||||
fn format_time_zone_annotation(time_zone_id: &str, critical: bool) -> String {
|
||||
let inner = if matches!(time_zone_id.as_bytes().first(), Some(b'+') | Some(b'-')) {
|
||||
|
||||
@@ -152,7 +152,14 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
||||
world,
|
||||
zoned_date_time_ctor,
|
||||
core,
|
||||
&[("from", oxide_builtins::temporal::zoned_date_time_from::<crate::vm::Vm> as *const (), 1)],
|
||||
&[
|
||||
("from", oxide_builtins::temporal::zoned_date_time_from::<crate::vm::Vm> as *const (), 1),
|
||||
(
|
||||
"compare",
|
||||
oxide_builtins::temporal::zoned_date_time_compare::<crate::vm::Vm> as *const (),
|
||||
2,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
let zoned_date_time_proto_ptr = world.zoned_date_time_proto.as_ptr() as *mut JsObject;
|
||||
|
||||
@@ -1487,6 +1487,74 @@ fn zoned_date_time_from_options_validation() {
|
||||
assert_eq!(str_val(&vm, r), "true|true|true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zoned_date_time_compare_orders_by_epoch() {
|
||||
let mut vm = Vm::new();
|
||||
// 按 epoch 大小比较而非墙钟:epoch 大者胜,返回 -1/0/1。
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"const a = new Temporal.ZonedDateTime(217175010123456789n, 'UTC');
|
||||
const b = new Temporal.ZonedDateTime(217175010223456789n, 'UTC');
|
||||
Temporal.ZonedDateTime.compare(a, a) + '|' +
|
||||
Temporal.ZonedDateTime.compare(a, b) + '|' +
|
||||
Temporal.ZonedDateTime.compare(b, a)",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(str_val(&vm, r), "0|-1|1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zoned_date_time_compare_ignores_time_zone_and_calendar() {
|
||||
let mut vm = Vm::new();
|
||||
// 同 epoch 不同时区/日历 → 0(只按 epoch 比较)。
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"const a = new Temporal.ZonedDateTime(0n, '+01:00', 'hebrew');
|
||||
const b = new Temporal.ZonedDateTime(0n, 'UTC');
|
||||
const c = new Temporal.ZonedDateTime(1n, '-05:00');
|
||||
Temporal.ZonedDateTime.compare(a, b) + '|' +
|
||||
Temporal.ZonedDateTime.compare(b, c)",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(str_val(&vm, r), "0|-1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zoned_date_time_compare_accepts_string_and_bag() {
|
||||
let mut vm = Vm::new();
|
||||
// 字符串 / property bag 参数走 from 解析器归一为 epoch 后比较。
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"const zdt = new Temporal.ZonedDateTime(0n, 'UTC');
|
||||
Temporal.ZonedDateTime.compare('1970-01-01T00:00Z[UTC]', zdt) + '|' +
|
||||
Temporal.ZonedDateTime.compare(
|
||||
{ year: 1969, month: 12, day: 31, hour: 19, minute: 0, timeZone: '-05:00' },
|
||||
zdt) + '|' +
|
||||
Temporal.ZonedDateTime.compare(zdt, '1970-01-01T00:00:01Z[UTC]')",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(str_val(&vm, r), "0|0|-1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zoned_date_time_compare_error_paths() {
|
||||
let mut vm = Vm::new();
|
||||
// 非法字符串 / 缺 timeZone 的 bag / 非转换原始值均抛错(RangeError/TypeError)。
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"( () => { try { Temporal.ZonedDateTime.compare('garbage', '1970-01-01T00:00Z[UTC]'); return 'no-throw'; }
|
||||
catch (e) { return e instanceof RangeError; } })() + '|' +
|
||||
(() => { try { Temporal.ZonedDateTime.compare(
|
||||
{ year: 2000, month: 1, day: 1 },
|
||||
'1970-01-01T00:00Z[UTC]'); return 'no-throw'; }
|
||||
catch (e) { return e instanceof TypeError; } })() + '|' +
|
||||
(() => { try { Temporal.ZonedDateTime.compare(123, 456); return 'no-throw'; }
|
||||
catch (e) { return e instanceof TypeError; } })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(str_val(&vm, r), "true|true|true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zoned_date_time_until_defaults_to_hours() {
|
||||
let mut vm = Vm::new();
|
||||
|
||||
Reference in New Issue
Block a user