mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
fix: PlainMonthDay/YearMonth toString 非 iso8601 日历补年份/日分量与日历注解
auto/缺省形(含 toJSON/toLocaleString 同形)对非 iso8601 日历恒输出裸
MM-DD / ±YYYY-MM:按 TemporalMonthDayToString/TemporalYearMonthToString,
showCalendar ∈ {always,critical} 或 calendar ≠ iso8601 时补参考年(PMD)/
参考日(PYM),注解按 FormatCalendarAnnotation(auto 仅非 iso 出 [u-ca=…]、
never 无注解、critical 前置 !)。ShowCalendar 拆分 auto/never(原 Omitted
合并两者致注解表不可表达),注解表独立成 helper 供两站点复用。
This commit is contained in:
@@ -7891,11 +7891,24 @@ pub fn plain_year_month_era_year<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRe
|
|||||||
NativeResult::Ok(JsValue::undefined())
|
NativeResult::Ok(JsValue::undefined())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// toString 的 calendarName 选项取值(auto/never 同默认形)。
|
/// toString 的 calendarName 选项取值(缺省 = auto;auto 与 never 的差异仅在注解)。
|
||||||
enum ShowCalendar {
|
enum ShowCalendar {
|
||||||
Omitted,
|
Auto,
|
||||||
Always,
|
Always,
|
||||||
Critical,
|
Critical,
|
||||||
|
Never,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ISO 串尾部日历注解(FormatCalendarAnnotation):never 与 auto+iso8601 为空;
|
||||||
|
/// critical 前置 `!`;其余为 `[u-ca=…]`。
|
||||||
|
fn calendar_annotation(calendar: &str, show: ShowCalendar) -> String {
|
||||||
|
match (show, calendar) {
|
||||||
|
(ShowCalendar::Never, _) => String::new(),
|
||||||
|
(ShowCalendar::Auto, "iso8601") => String::new(),
|
||||||
|
(ShowCalendar::Always, _) => format!("[u-ca={calendar}]"),
|
||||||
|
(ShowCalendar::Critical, _) => format!("[!u-ca={calendar}]"),
|
||||||
|
(ShowCalendar::Auto, _) => format!("[u-ca={calendar}]"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 读并校验 toString 的 calendarName 选项:options 非对象 → TypeError;
|
/// 读并校验 toString 的 calendarName 选项:options 非对象 → TypeError;
|
||||||
@@ -7904,7 +7917,7 @@ enum ShowCalendar {
|
|||||||
fn temporal_to_show_calendar<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<ShowCalendar, JsValue> {
|
fn temporal_to_show_calendar<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<ShowCalendar, JsValue> {
|
||||||
let options = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
let options = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||||
if options.is_undefined() {
|
if options.is_undefined() {
|
||||||
return Ok(ShowCalendar::Omitted);
|
return Ok(ShowCalendar::Auto);
|
||||||
}
|
}
|
||||||
if !options.is_object() {
|
if !options.is_object() {
|
||||||
return Err(crate::error::create_type_error(vm, "options must be an object"));
|
return Err(crate::error::create_type_error(vm, "options must be an object"));
|
||||||
@@ -7915,48 +7928,50 @@ fn temporal_to_show_calendar<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<ShowC
|
|||||||
}
|
}
|
||||||
let raw = temporal_option_value(vm, unsafe { &*ptr }, options, "calendarName")?;
|
let raw = temporal_option_value(vm, unsafe { &*ptr }, options, "calendarName")?;
|
||||||
if raw.is_undefined() {
|
if raw.is_undefined() {
|
||||||
return Ok(ShowCalendar::Omitted);
|
return Ok(ShowCalendar::Auto);
|
||||||
}
|
}
|
||||||
let value = temporal_option_string(vm, raw)?;
|
let value = temporal_option_string(vm, raw)?;
|
||||||
match value.as_str() {
|
match value.as_str() {
|
||||||
"always" => Ok(ShowCalendar::Always),
|
"always" => Ok(ShowCalendar::Always),
|
||||||
"critical" => Ok(ShowCalendar::Critical),
|
"critical" => Ok(ShowCalendar::Critical),
|
||||||
"auto" | "never" => Ok(ShowCalendar::Omitted),
|
"never" => Ok(ShowCalendar::Never),
|
||||||
|
"auto" => Ok(ShowCalendar::Auto),
|
||||||
_ => Err(crate::error::create_range_error(vm, "invalid calendarName")),
|
_ => Err(crate::error::create_range_error(vm, "invalid calendarName")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PlainMonthDay 默认形串(`MM-DD`),含 receiver 校验,不读 options。
|
/// PlainMonthDay ISO 串:always/critical 或非 iso8601 日历补参考年与日历注解;
|
||||||
|
/// iso8601 日历的 auto/never 形保持裸 `MM-DD`。
|
||||||
|
fn plain_month_day_iso_string(obj: &JsObject, show: ShowCalendar) -> String {
|
||||||
|
let month = get_double_prop(obj, 0) as i32;
|
||||||
|
let day = get_double_prop(obj, 1) as i32;
|
||||||
|
let calendar = get_calendar_id(obj, 3);
|
||||||
|
let with_year = matches!(show, ShowCalendar::Always | ShowCalendar::Critical) || calendar != "iso8601";
|
||||||
|
if !with_year {
|
||||||
|
return format!("{month:02}-{day:02}");
|
||||||
|
}
|
||||||
|
let ref_year = get_double_prop(obj, 2) as i32;
|
||||||
|
let annotation = calendar_annotation(&calendar, show);
|
||||||
|
format!("{}-{month:02}-{day:02}{annotation}", format_iso_year(ref_year as i128))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PlainMonthDay 默认形串(auto 形),含 receiver 校验,不读 options。
|
||||||
fn plain_month_day_default_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
fn plain_month_day_default_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
let ptr = native_try!(receiver_obj(vm, args));
|
let ptr = native_try!(receiver_obj(vm, args));
|
||||||
let obj = unsafe { &*ptr };
|
let obj = unsafe { &*ptr };
|
||||||
native_try!(ensure_plain_month_day(vm, obj));
|
native_try!(ensure_plain_month_day(vm, obj));
|
||||||
let month = get_double_prop(obj, 0) as i32;
|
NativeResult::Ok(vm.new_string_owned(plain_month_day_iso_string(obj, ShowCalendar::Auto)))
|
||||||
let day = get_double_prop(obj, 1) as i32;
|
|
||||||
NativeResult::Ok(vm.new_string_owned(format!("{month:02}-{day:02}")))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Temporal.PlainMonthDay.prototype.toString([options])`:
|
/// `Temporal.PlainMonthDay.prototype.toString([options])`:
|
||||||
/// 默认 `MM-DD`;always/critical 补参考年与 `[u-ca=…]`/`[!u-ca=…]` 注解。
|
/// 默认 `MM-DD`;非 iso8601 日历补参考年与 `[u-ca=…]` 注解,
|
||||||
|
/// always/critical 另按注解表补 `[u-ca=…]`/`[!u-ca=…]`。
|
||||||
pub fn plain_month_day_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn plain_month_day_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
let ptr = native_try!(receiver_obj(vm, args));
|
let ptr = native_try!(receiver_obj(vm, args));
|
||||||
let obj = unsafe { &*ptr };
|
let obj = unsafe { &*ptr };
|
||||||
native_try!(ensure_plain_month_day(vm, obj));
|
native_try!(ensure_plain_month_day(vm, obj));
|
||||||
let show = native_try!(temporal_to_show_calendar(vm, args));
|
let show = native_try!(temporal_to_show_calendar(vm, args));
|
||||||
if matches!(show, ShowCalendar::Omitted) {
|
NativeResult::Ok(vm.new_string_owned(plain_month_day_iso_string(obj, show)))
|
||||||
return plain_month_day_default_string(vm, args);
|
|
||||||
}
|
|
||||||
let month = get_double_prop(obj, 0) as i32;
|
|
||||||
let day = get_double_prop(obj, 1) as i32;
|
|
||||||
let ref_year = get_double_prop(obj, 2) as i32;
|
|
||||||
let calendar = get_calendar_id(obj, 3);
|
|
||||||
let annotation = match show {
|
|
||||||
ShowCalendar::Always => format!("[u-ca={calendar}]"),
|
|
||||||
_ => format!("[!u-ca={calendar}]"),
|
|
||||||
};
|
|
||||||
NativeResult::Ok(
|
|
||||||
vm.new_string_owned(format!("{}-{month:02}-{day:02}{annotation}", format_iso_year(ref_year as i128))),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Temporal.PlainMonthDay.prototype.toJSON()`:默认形串,忽略参数。
|
/// `Temporal.PlainMonthDay.prototype.toJSON()`:默认形串,忽略参数。
|
||||||
@@ -7975,37 +7990,38 @@ pub fn plain_month_day_value_of<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeRe
|
|||||||
NativeResult::Err(crate::error::create_type_error(vm, "Temporal.PlainMonthDay has no valueOf"))
|
NativeResult::Err(crate::error::create_type_error(vm, "Temporal.PlainMonthDay has no valueOf"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PlainYearMonth 默认形串(`±YYYY-MM`),含 receiver 校验,不读 options。
|
/// PlainYearMonth ISO 串:always/critical 或非 iso8601 日历补参考日与日历注解;
|
||||||
|
/// iso8601 日历的 auto/never 形保持裸 `±YYYY-MM`。
|
||||||
|
fn plain_year_month_iso_string(obj: &JsObject, show: ShowCalendar) -> String {
|
||||||
|
let year = get_double_prop(obj, 0) as i32;
|
||||||
|
let month = get_double_prop(obj, 1) as i32;
|
||||||
|
let calendar = get_calendar_id(obj, 3);
|
||||||
|
let with_day = matches!(show, ShowCalendar::Always | ShowCalendar::Critical) || calendar != "iso8601";
|
||||||
|
if !with_day {
|
||||||
|
return format!("{}-{month:02}", format_iso_year(year as i128));
|
||||||
|
}
|
||||||
|
let ref_day = get_double_prop(obj, 2) as i32;
|
||||||
|
let annotation = calendar_annotation(&calendar, show);
|
||||||
|
format!("{}-{month:02}-{ref_day:02}{annotation}", format_iso_year(year as i128))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PlainYearMonth 默认形串(auto 形),含 receiver 校验,不读 options。
|
||||||
fn plain_year_month_default_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
fn plain_year_month_default_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
let ptr = native_try!(receiver_obj(vm, args));
|
let ptr = native_try!(receiver_obj(vm, args));
|
||||||
let obj = unsafe { &*ptr };
|
let obj = unsafe { &*ptr };
|
||||||
native_try!(ensure_plain_year_month(vm, obj));
|
native_try!(ensure_plain_year_month(vm, obj));
|
||||||
let year = get_double_prop(obj, 0) as i32;
|
NativeResult::Ok(vm.new_string_owned(plain_year_month_iso_string(obj, ShowCalendar::Auto)))
|
||||||
let month = get_double_prop(obj, 1) as i32;
|
|
||||||
NativeResult::Ok(vm.new_string_owned(format!("{}-{month:02}", format_iso_year(year as i128))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Temporal.PlainYearMonth.prototype.toString([options])`:
|
/// `Temporal.PlainYearMonth.prototype.toString([options])`:
|
||||||
/// 默认 `±YYYY-MM`;always/critical 补参考日与 `[u-ca=…]`/`[!u-ca=…]` 注解。
|
/// 默认 `±YYYY-MM`;非 iso8601 日历补参考日与 `[u-ca=…]` 注解,
|
||||||
|
/// always/critical 另按注解表补 `[u-ca=…]`/`[!u-ca=…]`。
|
||||||
pub fn plain_year_month_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
pub fn plain_year_month_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
let ptr = native_try!(receiver_obj(vm, args));
|
let ptr = native_try!(receiver_obj(vm, args));
|
||||||
let obj = unsafe { &*ptr };
|
let obj = unsafe { &*ptr };
|
||||||
native_try!(ensure_plain_year_month(vm, obj));
|
native_try!(ensure_plain_year_month(vm, obj));
|
||||||
let show = native_try!(temporal_to_show_calendar(vm, args));
|
let show = native_try!(temporal_to_show_calendar(vm, args));
|
||||||
if matches!(show, ShowCalendar::Omitted) {
|
NativeResult::Ok(vm.new_string_owned(plain_year_month_iso_string(obj, show)))
|
||||||
return plain_year_month_default_string(vm, args);
|
|
||||||
}
|
|
||||||
let year = get_double_prop(obj, 0) as i32;
|
|
||||||
let month = get_double_prop(obj, 1) as i32;
|
|
||||||
let ref_day = get_double_prop(obj, 2) as i32;
|
|
||||||
let calendar = get_calendar_id(obj, 3);
|
|
||||||
let annotation = match show {
|
|
||||||
ShowCalendar::Always => format!("[u-ca={calendar}]"),
|
|
||||||
_ => format!("[!u-ca={calendar}]"),
|
|
||||||
};
|
|
||||||
NativeResult::Ok(
|
|
||||||
vm.new_string_owned(format!("{}-{month:02}-{ref_day:02}{annotation}", format_iso_year(year as i128))),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Temporal.PlainYearMonth.prototype.toJSON()`:默认形串,忽略参数。
|
/// `Temporal.PlainYearMonth.prototype.toJSON()`:默认形串,忽略参数。
|
||||||
|
|||||||
@@ -2802,6 +2802,38 @@ fn plain_year_month_to_string_year_format() {
|
|||||||
assert_eq!(str_val(&vm, r), "-000001-08|-000001-08-01[u-ca=iso8601]|-000001-08");
|
assert_eq!(str_val(&vm, r), "-000001-08|-000001-08-01[u-ca=iso8601]|-000001-08");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plain_month_day_and_year_month_to_string_non_iso_calendar() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
// 非 iso8601 日历:auto/缺省形补参考年(PMD,默认 1972)/参考日(PYM,默认 1)
|
||||||
|
// 与 [u-ca=…] 注解;never 形仅分量无注解;toJSON/toLocaleString 走 auto 形。
|
||||||
|
let r = eval(
|
||||||
|
&mut vm,
|
||||||
|
"(() => {
|
||||||
|
const pmd = new Temporal.PlainMonthDay(5, 2, 'hebrew');
|
||||||
|
const pym = new Temporal.PlainYearMonth(2021, 2, 'hebrew');
|
||||||
|
return [
|
||||||
|
pmd.toString(),
|
||||||
|
pmd.toString({calendarName: 'never'}),
|
||||||
|
pmd.toString({calendarName: 'always'}),
|
||||||
|
pmd.toJSON(),
|
||||||
|
pmd.toLocaleString(),
|
||||||
|
pym.toString(),
|
||||||
|
pym.toString({calendarName: 'never'}),
|
||||||
|
pym.toJSON(),
|
||||||
|
pym.toLocaleString(),
|
||||||
|
].join('|');
|
||||||
|
})()",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
str_val(&vm, r),
|
||||||
|
"1972-05-02[u-ca=hebrew]|1972-05-02|1972-05-02[u-ca=hebrew]|1972-05-02[u-ca=hebrew]|\
|
||||||
|
1972-05-02[u-ca=hebrew]|2021-02-01[u-ca=hebrew]|2021-02-01|2021-02-01[u-ca=hebrew]|\
|
||||||
|
2021-02-01[u-ca=hebrew]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn plain_month_day_and_year_month_branding() {
|
fn plain_month_day_and_year_month_branding() {
|
||||||
let mut vm = Vm::new();
|
let mut vm = Vm::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user