Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::dates_add_days::{days_in_month, format_iso_date, parse_iso_date, CivilDate}; ← from dates.add-days ^1.0.0 · built alongside by fune
/// Add whole months, keeping the day of the month and clamping it to the last
/// day of the target month when that month is shorter (31 Jan + 1 = 28/29 Feb).
///
/// The clamp is applied to the result only. Callers stepping through a schedule
/// must add k months to the original anchor, not 1 month k times, or the 31st
/// decays to the 28th after February.
///
/// # Panics
/// Panics on a malformed or impossible date, or a result outside 0001-9999.
pub fn add_months(iso: &str, months: i64) -> String {
let date = parse_iso_date(iso);
// Count months from year 0 so the year and month fall out of one division.
// div_euclid floors like Python's // and Math.floor for a negative total.
let total = date.year * 12 + (date.month - 1) + months;
let year = total.div_euclid(12);
let month = total.rem_euclid(12) + 1;
if !(1..=9999).contains(&year) {
panic!(
"\"{}\" plus {} months is outside the supported range 0001-01-01 to 9999-12-31",
iso, months
);
}
let day = date.day.min(days_in_month(year, month));
format_iso_date(&CivilDate { year, month, day })
}
pub fn fune_vector(args: &[Value]) -> Value {
// Refuse what the typed signature cannot hold, with the wording TypeScript
// and Python use, rather than let the conversion below quietly change it.
if let Value::Float(f) = args[1] {
if f.fract() != 0.0 {
panic!("months must be an integer, received {}", f);
}
}
Value::str(&add_months(args[0].as_str(), args[1].as_i64()))
}