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
/// A month counted from January of year 0, back to its year and month.
fn month_from_index(index: i64) -> (i64, i64) {
(index.div_euclid(12), index.rem_euclid(12) + 1)
}
fn first_day(index: i64) -> String {
let (year, month) = month_from_index(index);
format_iso_date(&CivilDate { year, month, day: 1 })
}
fn last_day(index: i64) -> String {
let (year, month) = month_from_index(index);
format_iso_date(&CivilDate {
year,
month,
day: days_in_month(year, month),
})
}
/// The calendar and fiscal quarter of a date. The fiscal year is given as
/// dates, not a number, because "FY2027" means the year ending in 2027 to one
/// organisation and the year starting in it to another.
///
/// # Panics
/// Panics on a malformed date, a start month outside 1-12, or a fiscal year
/// outside 0001-9999.
pub fn quarter(iso: &str, fiscal_year_start_month: i64) -> Quarter {
if !(1..=12).contains(&fiscal_year_start_month) {
panic!(
"fiscalYearStartMonth must be 1-12, received {}",
fiscal_year_start_month
);
}
let date = parse_iso_date(iso);
let months_into_year = (date.month - fiscal_year_start_month).rem_euclid(12);
let fiscal_quarter = months_into_year / 3 + 1;
// Work in months counted from year 0 so year boundaries need no special case.
let year_start = date.year * 12 + (date.month - 1) - months_into_year;
let quarter_start = year_start + 3 * (fiscal_quarter - 1);
if month_from_index(year_start).0 < 1 || month_from_index(year_start + 11).0 > 9999 {
panic!(
"the fiscal year containing \"{}\" is outside the supported range 0001-01-01 to 9999-12-31",
iso
);
}
Quarter {
calendar_year: date.year,
calendar_quarter: (date.month - 1) / 3 + 1,
fiscal_quarter,
fiscal_year_start: first_day(year_start),
fiscal_year_end: last_day(year_start + 11),
fiscal_quarter_start: first_day(quarter_start),
fiscal_quarter_end: last_day(quarter_start + 2),
}
}
pub fn quarter_to_value(q: &Quarter) -> Value {
Value::obj(vec![
("calendarYear", Value::Int(q.calendar_year)),
("calendarQuarter", Value::Int(q.calendar_quarter)),
("fiscalQuarter", Value::Int(q.fiscal_quarter)),
("fiscalYearStart", Value::str(&q.fiscal_year_start)),
("fiscalYearEnd", Value::str(&q.fiscal_year_end)),
("fiscalQuarterStart", Value::str(&q.fiscal_quarter_start)),
("fiscalQuarterEnd", Value::str(&q.fiscal_quarter_end)),
])
}
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!("fiscalYearStartMonth must be an integer, received {}", f);
}
}
quarter_to_value(&quarter(args[0].as_str(), args[1].as_i64()))
}