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);