From f1a6c1eaac203ac78f903581069131728eaf6e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Mon, 5 Oct 2026 14:05:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D(=E5=85=AC=E5=8E=86=E6=8D=A2?= =?UTF-8?q?=E7=AE=97):=20days=5Ffrom=5Fcivil=20=E5=85=A8=E9=87=8F=E6=94=B9?= =?UTF-8?q?=E7=94=A8=20i64=20=E8=BF=90=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - i32 年份区间内 era * 146_097 会超过 i32::MAX,debug 下 panic、release 下静默回绕 - 年、月、日中间量提升到 i64,返回类型仍是 i64;补 i32::MIN/MAX 边界回归测试 --- .../module-runtime/src/civil_calendar.rs | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/server-rs/crates/module-runtime/src/civil_calendar.rs b/server-rs/crates/module-runtime/src/civil_calendar.rs index 5b43da801..e4a679e5a 100644 --- a/server-rs/crates/module-runtime/src/civil_calendar.rs +++ b/server-rs/crates/module-runtime/src/civil_calendar.rs @@ -20,17 +20,20 @@ pub fn is_leap_year(year: i32) -> bool { } /// 公历年月日 → 1970-01-01 起的日序号。 +/// +/// 全程用 `i64` 运算:`i32` 年份区间内 `era * 146_097` 会超过 `i32::MAX`,在 `i32` 下 +/// 会溢出(debug panic、release 静默回绕),因此这里的中间量一律提升到 `i64`。 pub fn days_from_civil(year: i32, month: u8, day: u8) -> i64 { // 中文注释:Howard Hinnant civil calendar 算法,返回 1970-01-01 起的日序号。 - let adjusted_year = year - if month <= 2 { 1 } else { 0 }; + let adjusted_year = i64::from(year) - if month <= 2 { 1 } else { 0 }; let era = adjusted_year.div_euclid(400); let year_of_era = adjusted_year - era * 400; - let month = i32::from(month); - let day = i32::from(day); + let month = i64::from(month); + let day = i64::from(day); let month_prime = month + if month > 2 { -3 } else { 9 }; let day_of_year = (153 * month_prime + 2) / 5 + day - 1; let day_of_era = year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year; - i64::from(era * 146_097 + day_of_era - 719_468) + era * 146_097 + day_of_era - 719_468 } /// 1970-01-01 起的日序号 → 公历年月日。 @@ -79,6 +82,21 @@ mod tests { } } + #[test] + fn days_from_civil_handles_full_i32_year_range_without_overflow() { + // 中文注释:i32 年份区间内的 era 乘法会超过 i32::MAX,必须提升到 i64 才不溢出。 + for (year, month, day) in [ + (i32::MIN, 1, 1), + (i32::MIN, 2, 29), + (i32::MIN, 12, 31), + (i32::MAX, 1, 1), + (i32::MAX, 12, 31), + ] { + let date_key = days_from_civil(year, month, day); + assert_eq!(civil_from_days(date_key), (year, month, day)); + } + } + #[test] fn days_in_month_applies_leap_year_rules() { assert_eq!(days_in_month(2024, 2), 29);