Files
OxideJS-New/crates/oxide_builtins/src/date.rs
T

1127 lines
46 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use chrono::{DateTime, Datelike, Local, NaiveDate, NaiveDateTime, Timelike, Utc};
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use oxide_runtime_api::{NativeResult, ProtoKind, VmHost};
fn get_timestamp(obj: &JsObject) -> f64 {
let v = obj.get_prop_at(0);
if v.is_double() {
v.as_double()
} else {
f64::NAN
}
}
fn set_timestamp(obj: &mut JsObject, ms: f64) {
obj.set_prop_at(0, JsValue::float(ms));
}
fn ensure_date<H: VmHost>(vm: &mut H, obj: &JsObject) -> Result<(), JsValue> {
if !obj.is_date_obj() {
return Err(crate::error::create_type_error(vm, "called on incompatible receiver"));
}
Ok(())
}
macro_rules! native_try {
($expr:expr) => {
match $expr {
Ok(value) => value,
Err(err) => return NativeResult::Err(err),
}
};
}
fn date_this_mut<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<*mut JsObject, JsValue> {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() {
return Err(crate::error::create_type_error(vm, "called on incompatible receiver"));
}
let obj_ptr = raw.as_js_object_ptr();
if obj_ptr.is_null() {
return Err(crate::error::create_type_error(vm, "called on incompatible receiver"));
}
let obj = unsafe { &*obj_ptr };
ensure_date(vm, obj)?;
Ok(obj_ptr)
}
fn date_this<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<*const JsObject, JsValue> {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() {
return Err(crate::error::create_type_error(vm, "called on incompatible receiver"));
}
let obj_ptr = raw.as_js_object_ptr();
if obj_ptr.is_null() {
return Err(crate::error::create_type_error(vm, "called on incompatible receiver"));
}
let obj = unsafe { &*obj_ptr };
ensure_date(vm, obj)?;
Ok(obj_ptr)
}
fn dt_from_ms(ms: f64) -> Option<DateTime<Utc>> {
if !ms.is_finite() {
return None;
}
// f64 截断到 i64 可能溢出(超 ±9.22e18 ms),先做有界转换。
let ms_i64 = if ms >= i64::MIN as f64 && ms <= i64::MAX as f64 {
ms as i64
} else {
return None;
};
DateTime::from_timestamp_millis(ms_i64)
}
fn dt_from_ms_local(ms: f64) -> Option<DateTime<Local>> {
if !ms.is_finite() {
return None;
}
let ms_i64 = if ms >= i64::MIN as f64 && ms <= i64::MAX as f64 {
ms as i64
} else {
return None;
};
DateTime::from_timestamp_millis(ms_i64).map(|dt| dt.with_timezone(&Local))
}
fn naive_from_ms(ms: f64) -> Option<NaiveDateTime> {
if !ms.is_finite() {
return None;
}
dt_from_ms(ms).map(|dt| dt.naive_utc())
}
/// 日期值上界(TimeClip:|t| > 8.64e15 ms 为 Invalid Date)。
const MS_LIMIT: i64 = 8_640_000_000_000_000;
const MS_PER_DAY: i64 = 86_400_000;
/// 日数公式(自 1970-01-01 的天数,proleptic Gregorian):月 1-based、日
/// 1-based;越界分量直接吸收进线性日数(rollover 语义)。调用方须保证分量
/// 在 Date 可表示包络内(见 make_day 的上限判),否则乘积溢出。
fn civil_day_count(y: i64, m1: i64, d1: i64) -> i64 {
let y = if m1 <= 2 { y - 1 } else { y };
// era 取 400 年周期(负年向下取整),yoe 恒在 [0, 399]。
let era = (if y >= 0 { y } else { y - 399 }) / 400;
let yoe = y - era * 400;
let doy = (153 * (if m1 > 2 { m1 - 3 } else { m1 + 9 }) + 2) / 5 + d1 - 1;
let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
era * 146_097 + doe - 719_468
}
/// MakeDay(year, month, date):各分量先截断,返回日数(月 0-based、日 1-based);
/// 任一分量非有限或中间结果溢出返回 None(调用方按规范返回或置 NaN)。
fn make_day(y: f64, m: f64, d: f64) -> Option<i64> {
if !y.is_finite() || !m.is_finite() || !d.is_finite() {
return None;
}
let yr = y.trunc() as i64;
let mq = m.trunc() as i64;
let dq = d.trunc() as i64;
// Date 值域约 ±1e11 天;分量远超包络时直接越界,同时兜住后续乘积溢出。
if yr.abs() > 1_000_000_000 || mq.abs() > 12_000_000_000 || dq.abs() > 4_000_000_000 {
return None;
}
// 月溢出折入年份(欧几里得除法保持月在 [0, 12))。
let total = yr.checked_mul(12)?.checked_add(mq)?;
Some(civil_day_count(total.div_euclid(12), total.rem_euclid(12) + 1, dq))
}
/// MakeTime(hour, minute, second, millisecond):各分量截断后线性组合成日内毫秒;
/// 任一分量非有限或溢出返回 None。
fn make_time(h: f64, min: f64, s: f64, ms: f64) -> Option<i64> {
if !h.is_finite() || !min.is_finite() || !s.is_finite() || !ms.is_finite() {
return None;
}
(h.trunc() as i64)
.checked_mul(3_600_000)?
.checked_add((min.trunc() as i64).checked_mul(60_000)?)?
.checked_add((s.trunc() as i64).checked_mul(1_000)?)?
.checked_add(ms.trunc() as i64)
}
/// 日数与日内毫秒组合为最终时间戳;TimeClip 越界(|ts| > 8.64e15)返回 None。
fn make_date_ms(days: i64, time_ms: i64) -> Option<f64> {
let ts = days.checked_mul(MS_PER_DAY)?.checked_add(time_ms)?;
if !(-MS_LIMIT..=MS_LIMIT).contains(&ts) {
return None;
}
Some(ts as f64)
}
/// UTC 时间的日内毫秒(时/分/秒/毫秒分量组合)。
fn utc_time_ms(ndt: &NaiveDateTime) -> i64 {
ndt.time().hour() as i64 * 3_600_000
+ ndt.time().minute() as i64 * 60_000
+ ndt.time().second() as i64 * 1_000
+ ndt.time().nanosecond() as i64 / 1_000_000
}
/// 本地时间七分量(年、0-based 月、日、时、分、秒、毫秒);
/// 时间戳无效或超出可表示范围返回 None。
fn local_components(ms: f64) -> Option<(i64, i64, i64, i64, i64, i64, i64)> {
let dt = dt_from_ms_local(ms)?;
Some((
dt.year() as i64,
dt.month0() as i64,
dt.day() as i64,
dt.hour() as i64,
dt.minute() as i64,
dt.second() as i64,
dt.timestamp_subsec_millis() as i64,
))
}
/// TimeClip:|ts| > 8.64e15 或非有限的时刻值归 NaN(Invalid Date)。
fn time_clip(ts: f64) -> f64 {
if ts.is_finite() && (-MS_LIMIT as f64..=MS_LIMIT as f64).contains(&ts) {
ts
} else {
f64::NAN
}
}
/// 本地 setter 收尾:组合出的时刻值过 TimeClip 包络,越界(含非有限)为
/// Invalid Date(存 NaN 并返回 NaN);有效值写回并原样返回。
fn finish_local_setter(obj: &mut JsObject, ts: f64) -> NativeResult {
let ts = time_clip(ts);
set_timestamp(obj, ts);
NativeResult::Ok(JsValue::float(ts))
}
/// Date 数值参数:完整 ToNumber(对象路径经 engine_error 恢复原始异常值);
/// 缺参/undefined 得 NaN。
fn date_arg_number<H: VmHost>(vm: &mut H, val: JsValue) -> Result<f64, JsValue> {
match oxide_runtime_api::to_number_full(val, vm) {
Ok(n) => Ok(n),
Err(e) => Err(crate::iterator::engine_error(vm, &e)),
}
}
macro_rules! apply_date_result {
($obj:expr, $expr:expr) => {
match $expr {
Some(ts) => {
set_timestamp($obj, ts);
NativeResult::Ok(JsValue::float(ts))
}
None => {
// 非有限或越界:置 Invalid Date 并返回 NaN。
set_timestamp($obj, f64::NAN);
NativeResult::Ok(JsValue::float(f64::NAN))
}
}
};
}
/// MakeDay + MakeTime 语义组合本地时间戳:月/日/时/分/秒/毫秒分量越界时滚动进位,
/// 再按本地时区映射到真实 UTC 时刻。
///
/// # 边界与前提
/// - 任一分量非有限(±Infinity 或 NaN)直接返回 NaN。
/// - 分量按 ToIntegerOrInfinity 截断;年/月进位远超包络(日数公式乘积溢出带)
/// 直接返回 NaN。
/// - naive 时刻超 TimeClip 包络(|naive_ms| > 8.64e15)返回 NaN,同时兜住
/// i64 转换的饱和带;最终 UTC 时刻的 TimeClip 由调用方收尾(finish_local_setter
/// 或构造器 time_clip)。
fn make_local_timestamp(y: f64, m: f64, d: f64, h: f64, min: f64, sec: f64, ms: f64) -> f64 {
// 任一非有限分量按规范无效,同时封死后续整型饱和带。
if !y.is_finite()
|| !m.is_finite()
|| !d.is_finite()
|| !h.is_finite()
|| !min.is_finite()
|| !sec.is_finite()
|| !ms.is_finite()
{
return f64::NAN;
}
// 月溢出归一到 [0, 12),商进位到年份(负月同样成立)。
let m_norm = m.trunc().rem_euclid(12.0);
let y_carry = (m.trunc() / 12.0).floor();
// 年与月进位远超包络时直接拒绝,避免日数公式乘积溢出。
if y.abs() > 2e15 || y_carry.abs() > 2e15 {
return f64::NAN;
}
let y = (y.trunc() + y_carry) as i64;
// 基准取当月 1 号零点,全部分量统一折算为 i64 毫秒,
// 覆盖超出 chrono 可表示年份的区间。
let day_ms = civil_day_count(y, m_norm as i64 + 1, 1) as f64 * 86_400_000.0;
let time_ms = h.trunc() * 3_600_000.0 + min.trunc() * 60_000.0 + sec.trunc() * 1_000.0 + ms.trunc();
let naive_ms = day_ms + (d.trunc() - 1.0) * 86_400_000.0 + time_ms;
// 组合超 TimeClip 包络即无效,同时兜住 i64 转换的饱和带。
if !naive_ms.is_finite() || naive_ms.abs() > MS_LIMIT as f64 {
return f64::NAN;
}
let naive_ms = naive_ms as i64;
// naive 时刻按本地时区解释(DST 歧义取最早),得到真实 UTC 时间戳。
match DateTime::from_timestamp_millis(naive_ms) {
Some(dt) => dt
.naive_utc()
.and_local_timezone(Local)
.earliest()
.map(|dt| dt.timestamp_millis() as f64)
.unwrap_or(f64::NAN),
None => local_offset_sample(naive_ms).map(|ts| ts as f64).unwrap_or(f64::NAN),
}
}
/// 1970-01-01 起的日数转民用历法(年, 月, 日)。
fn civil_from_days(z0: i64) -> (i64, u32, u32) {
let z = z0 + 719_468;
let era = z.div_euclid(146_097);
let doe = z.rem_euclid(146_097);
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
let y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = (doy - (153 * mp + 2) / 5 + 1) as u32;
let m = if mp < 10 { mp + 3 } else { mp - 9 } as u32;
let y = if m <= 2 { y + 1 } else { y };
(y, m, d)
}
/// naive 时刻超出 chrono 可表示范围(约 262142-12-31 / -262143-01-01)时,
/// 用同一 400 年循环内的参考年采样本地偏移再映射回真实 UTC 时刻。
///
/// 规范将 LocalTime 映射定义为实现定义的近似,取同闰年周期的参考年满足该定义;
/// 参考时刻落在 DST 空档时返回 None(与范围内路径行为一致)。
fn local_offset_sample(naive_ms: i64) -> Option<i64> {
let days = naive_ms.div_euclid(86_400_000);
let rem = naive_ms.rem_euclid(86_400_000);
let (cy, cm, cd) = civil_from_days(days);
// 参考年与原始年位于同一 400 年周期的相同位置,闰年状态一致(2 月 29 日仅闰年可达)。
let (ry, rm, rd) = (2000 + cy.rem_euclid(400), cm, cd);
let ref_naive = NaiveDate::from_ymd_opt(ry as i32, rm, rd).and_then(|nd| {
nd.and_hms_milli_opt(
(rem / 3_600_000) as u32,
((rem / 60_000) % 60) as u32,
((rem / 1_000) % 60) as u32,
(rem % 1_000) as u32,
)
})?;
let ref_naive_ms = civil_day_count(ry, rm as i64, rd as i64) * 86_400_000 + rem;
let earliest = ref_naive.and_local_timezone(Local).earliest()?;
let offset_ms = ref_naive_ms - earliest.timestamp_millis();
Some(naive_ms - offset_ms)
}
/// 解析 ISO 8601 日期时间字符串(含时区偏移),返回 UTC 毫秒时间戳;无法解析返回 NaN。
///
/// # 边界与前提
/// - 偏移形态 `Z` / `+hh:mm` / `+hhmm` 均折算为 UTC;无偏移完整时间按 UTC;纯日期按 UTC 零点。
fn parse_iso_timestamp(s: &str) -> f64 {
// 带偏移(含 Z):DateTime 保留偏移直接折算 UTC。
if let Ok(dt) = chrono::DateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f%#z") {
return dt.timestamp_millis() as f64;
}
// 无偏移完整时间按 UTC。
if let Ok(ndt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f") {
return ndt.and_utc().timestamp_millis() as f64;
}
// 纯日期按 UTC 零点。
if let Ok(nd) = NaiveDate::parse_from_str(s, "%Y-%m-%d") {
if let Some(ndt) = nd.and_hms_opt(0, 0, 0) {
return ndt.and_utc().timestamp_millis() as f64;
}
}
f64::NAN
}
/// JS `Date()` 构造逻辑:构造调用无参取当前时间、单参支持时间戳/字符串/Date 对象、
/// 多参按本地时间字段(年/月/日/时/分/秒/毫秒)组合;普通调用忽略全部参数,
/// 返回当前时刻的日期字符串(与 `new Date().toString()` 同格式)。
pub fn date_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let is_ctor_call = this_val.is_object() && {
let ptr = this_val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
let date_proto = vm.builtin_proto(ProtoKind::DateProto);
if date_proto.is_null() {
false
} else {
let obj = unsafe { &*ptr };
let proto_ptr = obj.proto().as_js_object_ptr();
if proto_ptr.is_null() {
false
} else {
std::ptr::eq(proto_ptr, date_proto)
}
}
}
};
if !is_ctor_call {
// 普通调用:忽略全部参数(不做任何强制转换),返回当前时刻字符串。
let ms = Utc::now().timestamp_millis() as f64;
let s = match dt_from_ms(ms) {
Some(dt) => dt.format("%a %b %d %Y %H:%M:%S %Z %z").to_string(),
None => "Invalid Date".to_string(),
};
return NativeResult::Ok(vm.new_string_owned(s));
}
let timestamp = if args.len() < 2 {
Utc::now().timestamp_millis() as f64
} else if args.len() > 2 {
let y_val = oxide_runtime_api::to_number(vm.reg(args[1]));
// 年 0..99 按规范映射到 1900..1999(ToInteger 截断后判断)。
let y_val = if (0.0..=99.0).contains(&y_val.trunc()) {
y_val.trunc() + 1900.0
} else {
y_val
};
let m_val = oxide_runtime_api::to_number(vm.reg(args[2]));
let d_val = if args.len() > 3 { oxide_runtime_api::to_number(vm.reg(args[3])) } else { 1.0 };
let h_val = if args.len() > 4 { oxide_runtime_api::to_number(vm.reg(args[4])) } else { 0.0 };
let min_val = if args.len() > 5 { oxide_runtime_api::to_number(vm.reg(args[5])) } else { 0.0 };
let sec_val = if args.len() > 6 { oxide_runtime_api::to_number(vm.reg(args[6])) } else { 0.0 };
let ms_val = if args.len() > 7 { oxide_runtime_api::to_number(vm.reg(args[7])) } else { 0.0 };
if y_val.is_nan()
|| m_val.is_nan()
|| d_val.is_nan()
|| h_val.is_nan()
|| min_val.is_nan()
|| sec_val.is_nan()
|| ms_val.is_nan()
{
f64::NAN
} else {
// 组合结果与 setter 同收尾:TimeClip 包络外(含非有限)归 NaN。
time_clip(make_local_timestamp(y_val, m_val, d_val, h_val, min_val, sec_val, ms_val))
}
} else {
let val = vm.reg(args[1]);
if val.is_string() {
// SAFETY: val 已确认是字符串值。
let s = unsafe { (*val.as_string_ptr()).to_owned_string() };
parse_iso_timestamp(&s)
} else if val.is_int() || val.is_double() {
if val.is_int() {
val.as_int() as f64
} else {
val.as_double()
}
} else if val.is_object() && {
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
unsafe { &*ptr }.is_date_obj()
}
} {
let obj = unsafe { &*val.as_js_object_ptr() };
get_timestamp(obj)
} else {
match oxide_runtime_api::to_number_full(val, vm) {
Ok(n) => n,
Err(_) => {
// 对象经 ToNumber 转换时 toString/valueOf 可抛异常,须原样传播原始异常。
if let Some(exc) = vm.take_uncaught_value() {
return NativeResult::Err(exc);
}
return NativeResult::Err(crate::error::create_type_error(vm, "Cannot convert value to a number"));
}
}
}
};
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(vm.builtin_proto(ProtoKind::DateProto)));
obj.type_tag = JsObject::OBJ_TYPE_DATE;
obj.set_prop_at(0, JsValue::float(timestamp));
let ptr = vm.alloc_object(obj);
NativeResult::Ok(JsValue::from_js_object(ptr))
}
/// `Date.now()`:返回当前时间戳(毫秒)。
pub fn date_now<H: VmHost>(_vm: &mut H, _args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::float(Utc::now().timestamp_millis() as f64))
}
/// `Date.parse(string)`:解析日期字符串(RFC 2822、ISO 8601 及常用格式)为时间戳;
/// 无法解析返回 NaN。
pub fn date_parse<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let val = vm.reg(args[1]);
if !val.is_string() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
// SAFETY: val 已确认是字符串值。
let s = unsafe { (*val.as_string_ptr()).to_owned_string() };
let mut ts = f64::NAN;
if let Ok(dt) = chrono::DateTime::parse_from_rfc2822(&s) {
ts = dt.timestamp_millis() as f64;
}
if ts.is_nan() {
ts = parse_iso_timestamp(&s);
}
if ts.is_nan() {
if let Ok(nd) = NaiveDate::parse_from_str(&s, "%Y/%m/%d") {
if let Some(ndt) = nd.and_hms_opt(0, 0, 0).and_then(|n| n.and_local_timezone(Utc).earliest()) {
ts = ndt.timestamp_millis() as f64;
}
}
}
NativeResult::Ok(JsValue::float(ts))
}
/// `Date.UTC(y, m, d, h, min, s, ms)`:按 UTC 各字段组合成时间戳(m 为 0-based 月)。
pub fn date_utc<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let y = native_try!(date_arg_number(vm, vm.reg(args[1])));
let m = if args.len() > 2 {
native_try!(date_arg_number(vm, vm.reg(args[2])))
} else {
0.0
};
let d = if args.len() > 3 {
native_try!(date_arg_number(vm, vm.reg(args[3])))
} else {
1.0
};
let h = if args.len() > 4 {
native_try!(date_arg_number(vm, vm.reg(args[4])))
} else {
0.0
};
let min = if args.len() > 5 {
native_try!(date_arg_number(vm, vm.reg(args[5])))
} else {
0.0
};
let sec = if args.len() > 6 {
native_try!(date_arg_number(vm, vm.reg(args[6])))
} else {
0.0
};
let ms = if args.len() > 7 {
native_try!(date_arg_number(vm, vm.reg(args[7])))
} else {
0.0
};
if y.is_nan() || m.is_nan() || d.is_nan() || h.is_nan() || min.is_nan() || sec.is_nan() || ms.is_nan() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
// 年 0..99 按规范映射到 1900..1999(ToInteger 截断后判断)。
let y = if (0.0..=99.0).contains(&y.trunc()) { y + 1900.0 } else { y };
match make_day(y, m, d).and_then(|days| make_time(h, min, sec, ms).and_then(|t| make_date_ms(days, t))) {
Some(ts) => NativeResult::Ok(JsValue::float(ts)),
None => NativeResult::Ok(JsValue::float(f64::NAN)),
}
}
macro_rules! make_getter {
($name:ident, $f:expr, $df:expr) => {
/// UTC 视图 getter(如 `getTime`):返回 UTC 时间的指定分量;Invalid Date 返回 NaN。
pub fn $name<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float($df));
}
match dt_from_ms(ms) {
Some(dt) => NativeResult::Ok(JsValue::float(($f)(dt))),
None => NativeResult::Ok(JsValue::float($df)),
}
}
};
}
macro_rules! make_utc_getter {
($name:ident, $f:expr, $df:expr) => {
/// UTC getter(如 `getUTCFullYear`):返回 UTC 时间的指定分量;Invalid Date 返回 NaN。
pub fn $name<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float($df));
}
match naive_from_ms(ms) {
Some(ndt) => NativeResult::Ok(JsValue::float(($f)(ndt))),
None => NativeResult::Ok(JsValue::float($df)),
}
}
};
}
macro_rules! make_local_getter {
($name:ident, $f:expr, $df:expr) => {
/// 本地时区 getter(如 `getFullYear`):返回本地时间的指定分量;Invalid Date 返回 NaN。
pub fn $name<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float($df));
}
match dt_from_ms_local(ms) {
Some(dt) => NativeResult::Ok(JsValue::float(($f)(dt))),
None => NativeResult::Ok(JsValue::float($df)),
}
}
};
}
make_getter!(date_get_time, |dt: DateTime<Utc>| dt.timestamp_millis() as f64, f64::NAN);
make_local_getter!(date_get_full_year, |dt: DateTime<Local>| dt.year() as f64, f64::NAN);
make_local_getter!(date_get_month, |dt: DateTime<Local>| dt.month0() as f64, f64::NAN);
make_local_getter!(date_get_date, |dt: DateTime<Local>| dt.day() as f64, f64::NAN);
make_local_getter!(date_get_day, |dt: DateTime<Local>| dt.weekday().num_days_from_sunday() as f64, f64::NAN);
make_local_getter!(date_get_hours, |dt: DateTime<Local>| dt.hour() as f64, f64::NAN);
make_local_getter!(date_get_minutes, |dt: DateTime<Local>| dt.minute() as f64, f64::NAN);
make_local_getter!(date_get_seconds, |dt: DateTime<Local>| dt.second() as f64, f64::NAN);
make_local_getter!(date_get_milliseconds, |dt: DateTime<Local>| dt.timestamp_subsec_millis() as f64, f64::NAN);
make_utc_getter!(date_get_utc_full_year, |ndt: NaiveDateTime| ndt.date().year() as f64, f64::NAN);
make_utc_getter!(date_get_utc_month, |ndt: NaiveDateTime| ndt.date().month0() as f64, f64::NAN);
make_utc_getter!(date_get_utc_date, |ndt: NaiveDateTime| ndt.date().day() as f64, f64::NAN);
make_utc_getter!(
date_get_utc_day,
|ndt: NaiveDateTime| ndt.date().weekday().num_days_from_sunday() as f64,
f64::NAN
);
make_utc_getter!(date_get_utc_hours, |ndt: NaiveDateTime| ndt.time().hour() as f64, f64::NAN);
make_utc_getter!(date_get_utc_minutes, |ndt: NaiveDateTime| ndt.time().minute() as f64, f64::NAN);
make_utc_getter!(date_get_utc_seconds, |ndt: NaiveDateTime| ndt.time().second() as f64, f64::NAN);
make_utc_getter!(
date_get_utc_milliseconds,
|ndt: NaiveDateTime| ndt.time().nanosecond() as f64 / 1_000_000.0,
f64::NAN
);
/// `Date.prototype.getTimezoneOffset()`:返回本地时区相对 UTC 的分钟偏移。
pub fn date_get_timezone_offset<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
let offset_min = local_offset_minutes(ms);
NativeResult::Ok(JsValue::float(offset_min as f64))
}
fn local_offset_minutes(ms: f64) -> i32 {
if !ms.is_finite() {
return 0;
}
let offset = chrono::DateTime::from_timestamp_millis(ms as i64)
.map(|dt| dt.with_timezone(&chrono::Local))
.map(|dt| dt.offset().local_minus_utc())
.unwrap_or(0);
-offset / 60
}
/// `Date.prototype.setTime(ms)`:直接设置时间戳,返回新时间戳。
pub fn date_set_time<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = native_try!(date_arg_number(vm, val));
set_timestamp(obj, v);
NativeResult::Ok(JsValue::float(v))
}
/// `Date.prototype.setFullYear(y, m, d)`:设置本地年份(可选月/日),返回新时间戳。
pub fn date_set_full_year<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
// 先读 this 值;无效时取 +0(按规范不早退)。
let ms = get_timestamp(obj);
let y = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
// 无效 this 值取 +0 基准分量(1970-01-01 时刻全零,+0 不经 LocalTime 换算)。
let (_dy, dm, dd, dh, dmin, dsec, dms) = if ms.is_finite() {
local_components(ms).unwrap_or((1970, 0, 1, 0, 0, 0, 0))
} else {
(1970, 0, 1, 0, 0, 0, 0)
};
let m = if args.len() > 2 {
native_try!(date_arg_number(vm, vm.reg(args[2])))
} else {
dm as f64
};
let d = if args.len() > 3 {
native_try!(date_arg_number(vm, vm.reg(args[3])))
} else {
dd as f64
};
if y.is_nan() || m.is_nan() || d.is_nan() {
set_timestamp(obj, f64::NAN);
return NativeResult::Ok(JsValue::float(f64::NAN));
}
finish_local_setter(obj, make_local_timestamp(y, m, d, dh as f64, dmin as f64, dsec as f64, dms as f64))
}
/// `Date.prototype.setMonth(m, d)`:设置本地月份(可选日),返回新时间戳。
pub fn date_set_month<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
// 参数强转先于 NaN 判定(按规范序)。
let m = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
let d = if args.len() > 2 {
native_try!(date_arg_number(vm, vm.reg(args[2])))
} else {
0.0
};
let has_date = args.len() > 2;
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let Some((dy, _dm, dd, dh, dmin, dsec, dms)) = local_components(ms) else {
return NativeResult::Ok(JsValue::float(f64::NAN));
};
let d = if has_date { d } else { dd as f64 };
if m.is_nan() || d.is_nan() {
set_timestamp(obj, f64::NAN);
return NativeResult::Ok(JsValue::float(f64::NAN));
}
finish_local_setter(
obj,
make_local_timestamp(dy as f64, m, d, dh as f64, dmin as f64, dsec as f64, dms as f64),
)
}
/// `Date.prototype.setDate(d)`:设置本地日,返回新时间戳。
pub fn date_set_date<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
let d = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let Some((dy, dm, _dd, dh, dmin, dsec, dms)) = local_components(ms) else {
return NativeResult::Ok(JsValue::float(f64::NAN));
};
if d.is_nan() {
set_timestamp(obj, f64::NAN);
return NativeResult::Ok(JsValue::float(f64::NAN));
}
finish_local_setter(
obj,
make_local_timestamp(dy as f64, dm as f64, d, dh as f64, dmin as f64, dsec as f64, dms as f64),
)
}
/// `Date.prototype.setHours(h, min, s, ms)`:设置本地小时(可选分/秒/毫秒),返回新时间戳。
pub fn date_set_hours<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
let h = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
let min = if args.len() > 2 {
native_try!(date_arg_number(vm, vm.reg(args[2])))
} else {
0.0
};
let sec = if args.len() > 3 {
native_try!(date_arg_number(vm, vm.reg(args[3])))
} else {
0.0
};
let ms_arg = if args.len() > 4 {
native_try!(date_arg_number(vm, vm.reg(args[4])))
} else {
0.0
};
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let Some((dy, dm, dd, _dh, dmin, dsec, dms)) = local_components(ms) else {
return NativeResult::Ok(JsValue::float(f64::NAN));
};
// 缺省分量取本地时刻的 TimeWithinDay 值,与 MakeTime 组合后整体进位。
let min_v = if args.len() > 2 { min } else { dmin as f64 };
let sec_v = if args.len() > 3 { sec } else { dsec as f64 };
let ms_v = if args.len() > 4 { ms_arg } else { dms as f64 };
if h.is_nan() || min_v.is_nan() || sec_v.is_nan() || ms_v.is_nan() {
set_timestamp(obj, f64::NAN);
return NativeResult::Ok(JsValue::float(f64::NAN));
}
finish_local_setter(obj, make_local_timestamp(dy as f64, dm as f64, dd as f64, h, min_v, sec_v, ms_v))
}
/// `Date.prototype.setMinutes(min, s, ms)`:设置本地分钟(可选秒/毫秒),返回新时间戳。
pub fn date_set_minutes<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
let min = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
let sec = if args.len() > 2 {
native_try!(date_arg_number(vm, vm.reg(args[2])))
} else {
0.0
};
let ms_arg = if args.len() > 3 {
native_try!(date_arg_number(vm, vm.reg(args[3])))
} else {
0.0
};
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let Some((dy, dm, dd, dh, _dmin, dsec, dms)) = local_components(ms) else {
return NativeResult::Ok(JsValue::float(f64::NAN));
};
let sec_v = if args.len() > 2 { sec } else { dsec as f64 };
let ms_v = if args.len() > 3 { ms_arg } else { dms as f64 };
if min.is_nan() || sec_v.is_nan() || ms_v.is_nan() {
set_timestamp(obj, f64::NAN);
return NativeResult::Ok(JsValue::float(f64::NAN));
}
finish_local_setter(obj, make_local_timestamp(dy as f64, dm as f64, dd as f64, dh as f64, min, sec_v, ms_v))
}
/// `Date.prototype.setSeconds(s, ms)`:设置本地秒(可选毫秒),返回新时间戳。
pub fn date_set_seconds<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
let sec = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
let ms_arg = if args.len() > 2 {
native_try!(date_arg_number(vm, vm.reg(args[2])))
} else {
0.0
};
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let Some((dy, dm, dd, dh, dmin, _dsec, dms)) = local_components(ms) else {
return NativeResult::Ok(JsValue::float(f64::NAN));
};
let ms_v = if args.len() > 2 { ms_arg } else { dms as f64 };
if sec.is_nan() || ms_v.is_nan() {
set_timestamp(obj, f64::NAN);
return NativeResult::Ok(JsValue::float(f64::NAN));
}
finish_local_setter(
obj,
make_local_timestamp(dy as f64, dm as f64, dd as f64, dh as f64, dmin as f64, sec, ms_v),
)
}
/// `Date.prototype.setMilliseconds(ms)`:设置本地毫秒,返回新时间戳。
pub fn date_set_milliseconds<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
let ms_arg = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let Some((dy, dm, dd, dh, dmin, dsec, _dms)) = local_components(ms) else {
return NativeResult::Ok(JsValue::float(f64::NAN));
};
if ms_arg.is_nan() {
set_timestamp(obj, f64::NAN);
return NativeResult::Ok(JsValue::float(f64::NAN));
}
finish_local_setter(
obj,
make_local_timestamp(dy as f64, dm as f64, dd as f64, dh as f64, dmin as f64, dsec as f64, ms_arg),
)
}
/// `Date.prototype.setUTCFullYear(y, m, d)`:设置 UTC 年份(可选月/日),返回新时间戳。
pub fn date_set_utc_full_year<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let ndt = match naive_from_ms(ms) {
Some(n) => n,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let y = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
let m = if args.len() > 2 {
native_try!(date_arg_number(vm, vm.reg(args[2])))
} else {
ndt.date().month0() as f64
};
let d = if args.len() > 3 {
native_try!(date_arg_number(vm, vm.reg(args[3])))
} else {
ndt.date().day() as f64
};
let time_ms = utc_time_ms(&ndt);
apply_date_result!(obj, make_day(y, m, d).and_then(|days| make_date_ms(days, time_ms)))
}
/// `Date.prototype.setUTCMonth(m, d)`:设置 UTC 月份(可选日),返回新时间戳。
pub fn date_set_utc_month<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let ndt = match naive_from_ms(ms) {
Some(n) => n,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let m = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
let d = if args.len() > 2 {
native_try!(date_arg_number(vm, vm.reg(args[2])))
} else {
ndt.date().day() as f64
};
let time_ms = utc_time_ms(&ndt);
apply_date_result!(obj, make_day(ndt.date().year() as f64, m, d).and_then(|days| make_date_ms(days, time_ms)))
}
/// `Date.prototype.setUTCDate(d)`:设置 UTC 日,返回新时间戳。
pub fn date_set_utc_date<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let ndt = match naive_from_ms(ms) {
Some(n) => n,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let d = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
let time_ms = utc_time_ms(&ndt);
apply_date_result!(
obj,
make_day(ndt.date().year() as f64, ndt.date().month0() as f64, d).and_then(|days| make_date_ms(days, time_ms))
)
}
/// `Date.prototype.setUTCHours(h, min, s, ms)`:设置 UTC 小时(可选分/秒/毫秒),返回新时间戳。
pub fn date_set_utc_hours<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let ndt = match naive_from_ms(ms) {
Some(n) => n,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let h = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
let min = if args.len() > 2 {
native_try!(date_arg_number(vm, vm.reg(args[2])))
} else {
0.0
};
let sec = if args.len() > 3 {
native_try!(date_arg_number(vm, vm.reg(args[3])))
} else {
0.0
};
let ms_arg = if args.len() > 4 {
native_try!(date_arg_number(vm, vm.reg(args[4])))
} else {
0.0
};
// 缺省分量取当前 UTC 时刻值,与 MakeTime 组合后整体进位。
let min_v = if args.len() > 2 { min } else { ndt.time().minute() as f64 };
let sec_v = if args.len() > 3 { sec } else { ndt.time().second() as f64 };
let ms_v = if args.len() > 4 {
ms_arg
} else {
ndt.time().nanosecond() as f64 / 1_000_000.0
};
let days = civil_day_count(ndt.date().year() as i64, ndt.date().month() as i64, ndt.date().day() as i64);
let result = make_time(h, min_v, sec_v, ms_v).and_then(|time_ms| make_date_ms(days, time_ms));
apply_date_result!(obj, result)
}
/// `Date.prototype.setUTCMinutes(min, s, ms)`:设置 UTC 分钟(可选秒/毫秒),返回新时间戳。
pub fn date_set_utc_minutes<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let ndt = match naive_from_ms(ms) {
Some(n) => n,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let min = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
let sec = if args.len() > 2 {
native_try!(date_arg_number(vm, vm.reg(args[2])))
} else {
0.0
};
let ms_arg = if args.len() > 3 {
native_try!(date_arg_number(vm, vm.reg(args[3])))
} else {
0.0
};
let sec_v = if args.len() > 2 { sec } else { ndt.time().second() as f64 };
let ms_v = if args.len() > 3 {
ms_arg
} else {
ndt.time().nanosecond() as f64 / 1_000_000.0
};
let days = civil_day_count(ndt.date().year() as i64, ndt.date().month() as i64, ndt.date().day() as i64);
let result = make_time(ndt.time().hour() as f64, min, sec_v, ms_v).and_then(|time_ms| make_date_ms(days, time_ms));
apply_date_result!(obj, result)
}
/// `Date.prototype.setUTCSeconds(s, ms)`:设置 UTC 秒(可选毫秒),返回新时间戳。
pub fn date_set_utc_seconds<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let ndt = match naive_from_ms(ms) {
Some(n) => n,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let sec = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
let ms_arg = if args.len() > 2 {
native_try!(date_arg_number(vm, vm.reg(args[2])))
} else {
0.0
};
let ms_v = if args.len() > 2 {
ms_arg
} else {
ndt.time().nanosecond() as f64 / 1_000_000.0
};
let days = civil_day_count(ndt.date().year() as i64, ndt.date().month() as i64, ndt.date().day() as i64);
let result = make_time(ndt.time().hour() as f64, ndt.time().minute() as f64, sec, ms_v)
.and_then(|time_ms| make_date_ms(days, time_ms));
apply_date_result!(obj, result)
}
/// `Date.prototype.setUTCMilliseconds(ms)`:设置 UTC 毫秒,返回新时间戳。
pub fn date_set_utc_milliseconds<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let ndt = match naive_from_ms(ms) {
Some(n) => n,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let ms_arg = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
let days = civil_day_count(ndt.date().year() as i64, ndt.date().month() as i64, ndt.date().day() as i64);
let result = make_time(ndt.time().hour() as f64, ndt.time().minute() as f64, ndt.time().second() as f64, ms_arg)
.and_then(|time_ms| make_date_ms(days, time_ms));
apply_date_result!(obj, result)
}
/// Annex B `Date.prototype.getYear()`:返回本地年减 1900。
pub fn date_get_year<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
match dt_from_ms_local(ms) {
Some(dt) => NativeResult::Ok(JsValue::float((dt.year() - 1900) as f64)),
None => NativeResult::Ok(JsValue::float(f64::NAN)),
}
}
/// Annex B `Date.prototype.setYear(y)`:设置年份,0..99 自动加 1900;
/// 缺参/NaN 置为 Invalid Date。
///
/// 年份参数按完整 ToNumber 强转(对象走 valueOf/toString,Symbol 抛
/// TypeError);this 值无效时取 +0 基准分量(1970-01-01 全零)继续,
/// 结果经 TimeClip 包络收尾。
pub fn date_set_year<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
// this 值先于 ToNumber(year) 读取(规范步序)。
let ms = get_timestamp(obj);
let y = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
// 无效 this 值取 +0 基准分量(1970-01-01 时刻全零,与 setFullYear 同口径)。
let (_dy, dm, dd, dh, dmin, dsec, dms) = if ms.is_finite() {
local_components(ms).unwrap_or((1970, 0, 1, 0, 0, 0, 0))
} else {
(1970, 0, 1, 0, 0, 0, 0)
};
if y.is_nan() {
set_timestamp(obj, f64::NAN);
return NativeResult::Ok(JsValue::float(f64::NAN));
}
// MakeFullYear:ToIntegerOrInfinity 截断,0..99 映射到 1900..1999。
let y_int = y.trunc() as i64;
let full_year = if (0..=99).contains(&y_int) { y_int + 1900 } else { y_int };
finish_local_setter(
obj,
make_local_timestamp(full_year as f64, dm as f64, dd as f64, dh as f64, dmin as f64, dsec as f64, dms as f64),
)
}
/// `Date.prototype.toGMTString()`:别名 `toUTCString`(GMT 格式)。
pub fn date_to_gmt_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
date_to_utc_string(vm, args)
}
/// `Date.prototype.toLocaleDateString()`:本地化日期字符串(当前等同于 `toDateString`)。
pub fn date_to_locale_date_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
date_to_date_string(vm, args)
}
/// `Date.prototype.toLocaleString()`:本地化日期时间字符串(当前等同于 `toString`)。
pub fn date_to_locale_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
date_to_string(vm, args)
}
/// `Date.prototype.toLocaleTimeString()`:本地化时间字符串(当前等同于 `toTimeString`)。
pub fn date_to_locale_time_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
date_to_time_string(vm, args)
}
fn date_to_string_inner<H: VmHost>(vm: &mut H, args: &[u8], format_str: &str, invalid: &str) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(vm.new_string(invalid));
}
let s = match dt_from_ms(ms) {
Some(dt) => dt.format(format_str).to_string(),
None => invalid.to_string(),
};
NativeResult::Ok(vm.new_string_owned(s))
}
/// `Date.prototype.toISOString()`:输出 ISO 8601 格式(`YYYY-MM-DDTHH:MM:SS.mmmZ`)。
pub fn date_to_iso_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%Y-%m-%dT%H:%M:%S%.3fZ", "Invalid Date")
}
/// `Date.prototype.toJSON()`:输出 ISO 8601 字符串;Invalid Date 返回 null。
pub fn date_to_json<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::null());
}
let s = match dt_from_ms(ms) {
Some(dt) => dt.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string(),
None => return NativeResult::Ok(JsValue::null()),
};
NativeResult::Ok(vm.new_string_owned(s))
}
/// `Date.prototype.toString()`:本地时间完整字符串(如 `Wed Aug 05 2026 12:00:00 GMT+0000`)。
pub fn date_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%a %b %d %Y %H:%M:%S %Z %z", "Invalid Date")
}
/// `Date.prototype.toDateString()`:日期部分字符串(如 `Wed Aug 05 2026`)。
pub fn date_to_date_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%a %b %d %Y", "Invalid Date")
}
/// `Date.prototype.toTimeString()`:时间部分字符串(如 `12:00:00 GMT+0000`)。
pub fn date_to_time_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%H:%M:%S %Z", "Invalid Date")
}
/// `Date.prototype.toUTCString()`:UTC 完整字符串(如 `Wed, 05 Aug 2026 12:00:00 GMT`)。
pub fn date_to_utc_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%a, %d %b %Y %H:%M:%S GMT", "Invalid Date")
}
/// `Date.prototype.valueOf()`:返回时间戳(毫秒)。
pub fn date_value_of<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
NativeResult::Ok(JsValue::float(ms))
}