Shared Rust kernels
catalogue/dates
All modules · quoted at commit 9e1884369a67 of DnaCalc/DnaCalc. Comments are the source's own.
environment · 4 lines · calc-func/src/catalogue/dates.rs on GitHub/// The call's environment, or the refusal of a call that has none.
fn environment<'a>(site: &'a Site<'_>) -> Step<&'a CalcEnvironment> {
site.environment().ok_or_else(|| site.not_modelled(None))
}
Used by 22 functions.
system · 3 lines · calc-func/src/catalogue/dates.rs on GitHubfn system(site: &Site<'_>) -> Step<DateSystem> {
environment(site).map(|environment| environment.date_system)
}
Used by 22 functions.
to_1900 · 7 lines · calc-func/src/catalogue/dates.rs on GitHub/// A serial of the call's date system as a 1900 serial; a negative serial stays negative.
fn to_1900(system: DateSystem, serial: f64) -> f64 {
match system {
DateSystem::System1904 if serial >= 0.0 => serial + OFFSET_1904,
_ => serial,
}
}
Used by 19 functions.
from_1900 · 10 lines · calc-func/src/catalogue/dates.rs on GitHub/// A 1900 serial as a serial of the call's date system: before 1 January 1904 is #NUM! there.
fn from_1900(system: DateSystem, serial: f64) -> Result<f64, W> {
match system {
DateSystem::System1900 => Ok(serial),
DateSystem::System1904 => {
let serial = serial - OFFSET_1904;
if serial < 0.0 { Err(W::Num) } else { Ok(serial) }
}
}
}
Used by 8 functions.
date_arg · 5 lines · calc-func/src/catalogue/dates.rs on GitHub/// Argument `index` as a date of the call's system, read as a 1900 serial.
fn date_arg(site: &Site<'_>, a: &[CalcValue], index: usize) -> Step<f64> {
let system = system(site)?;
Ok(to_1900(system, num(site, a, index)?))
}
Used by 15 functions.
strict_date_arg · 8 lines · calc-func/src/catalogue/dates.rs on GitHub/// A date argument under OxFunc's strict rule: a logical is #VALUE!, an omitted one #N/A.
fn strict_date_arg(site: &Site<'_>, a: &[CalcValue], index: usize) -> Step<f64> {
match a[index].core_ref() {
CoreRef::Logical(_) => fail(W::Value),
CoreRef::Missing => fail(W::NA),
_ => date_arg(site, a, index),
}
}
Used by 4 functions.
strict_num · 8 lines · calc-func/src/catalogue/dates.rs on GitHub/// A number argument under the strict rule.
fn strict_num(site: &Site<'_>, a: &[CalcValue], index: usize) -> Step<f64> {
match a[index].core_ref() {
CoreRef::Logical(_) => fail(W::Value),
CoreRef::Missing => fail(W::NA),
_ => num(site, a, index),
}
}
Used by 4 functions.
present_num · 7 lines · calc-func/src/catalogue/dates.rs on GitHub/// Fn7's required WORKDAY/NETWORKDAYS slots: omitted is #N/A; a blank reads as zero.
fn present_num(site: &Site<'_>, a: &[CalcValue], index: usize) -> Step<f64> {
match a[index].core_ref() {
CoreRef::Missing => fail(W::NA),
_ => num(site, a, index),
}
}
Used by 4 functions.
serial_out · 5 lines · calc-func/src/catalogue/dates.rs on GitHub/// A serial result of a 1900 kernel, in the call's system.
fn serial_out(site: &Site<'_>, result: Result<f64, W>) -> Step<CalcValue> {
let system = system(site)?;
out(site, result.and_then(|serial| from_1900(system, serial)))
}
Used by 8 functions.
of_date · 5 lines · calc-func/src/catalogue/dates.rs on GitHub/// A unary kernel of a date.
fn of_date(site: &Site<'_>, a: &[CalcValue], kernel: fn(f64) -> Result<f64, W>) -> Step<CalcValue> {
let serial = date_arg(site, a, 0)?;
out(site, kernel(serial))
}
Used by 6 functions.
weekend · 34 lines · calc-func/src/catalogue/dates.rs on GitHub/// The weekend of WORKDAY.INTL and NETWORKDAYS.INTL: Saturday and Sunday when the call leaves it
/// out, a code, or a mask of seven 0 and 1 characters from Monday.
fn weekend(site: &Site<'_>, a: &[CalcValue], index: usize, workday: bool) -> Step<WeekendMask> {
let Some(value) = a.get(index) else {
return Ok(WeekendMask::saturday_sunday());
};
match value.core_ref() {
CoreRef::Missing | CoreRef::Empty => Ok(WeekendMask::saturday_sunday()),
// An error value is no weekend: WORKDAY.INTL's #NUM!, NETWORKDAYS.INTL's #VALUE! (Excel
// 16.0.20430, 2026-10-25, dc-6au: `=WORKDAY.INTL(1,1,#REF!)` and `(1,1,#DIV/0!)` #NUM!,
// `=NETWORKDAYS.INTL(1,1,#REF!)` and `(1,1,#DIV/0!)` #VALUE!).
CoreRef::Error(_) if workday => fail(W::Num),
CoreRef::Error(_) => fail(W::Value),
CoreRef::Text(text) => {
let raw = text.to_string_lossy();
if raw.len() == 7 && raw.chars().all(|ch| ch == '0' || ch == '1') {
let mask = WeekendMask::from_mask_text(&raw).expect("a checked mask");
if mask.is_all_days() && workday {
return fail(W::Value);
}
return Ok(mask);
}
let code = num(site, a, index)?;
WeekendMask::from_number(code.trunc() as i64).ok_or(Exit::Error(W::Num))
}
_ => {
let code = num(site, a, index)?;
if !code.is_finite() {
return fail(W::Num);
}
WeekendMask::from_number(code.trunc() as i64).ok_or(Exit::Error(W::Num))
}
}
}
Used by 4 functions.
holidays · 35 lines · calc-func/src/catalogue/dates.rs on GitHub/// The holidays a list gives, as 1900 serials: each number it holds (text, logicals and blank
/// cells in a reference or an array are skipped; a direct value is read as a number).
fn holidays(
site: &Site<'_>,
a: &[CalcValue],
index: usize,
sv: &mut EvalServices<'_>,
) -> Step<BTreeSet<i64>> {
let mut found = BTreeSet::new();
if index >= a.len() || a[index].is_missing() {
return Ok(found);
}
let system = system(site)?;
let mut numbers = Vec::new();
values(site, a, index, index + 1, sv, &mut |held, value| {
let n = if held {
match value.core_ref() {
CoreRef::Number(n) => n,
CoreRef::Error(code) => return fail(code),
CoreRef::Array(_) | CoreRef::Reference(_) => {
return Err(site.not_modelled(Some(index)));
}
_ => return Ok(()),
}
} else {
site.number_argument(index, value)?
};
numbers.push(n);
Ok(())
})?;
for n in numbers {
found.insert(k::serial_from_number(to_1900(system, n)).map_err(Exit::Error)?);
}
Ok(found)
}
Used by 4 functions.
workday · 13 lines · calc-func/src/catalogue/dates.rs on GitHubfn workday(site: &Site<'_>, a: &[CalcValue], sv: &mut EvalServices<'_>, intl: bool) -> Step<CalcValue> {
reject_logical(&a[0])?;
let start = super::kit::checked_date_serial(to_1900(system(site)?, present_num(site, a, 0)?))?;
reject_logical(&a[1])?;
let days = present_num(site, a, 1)?;
let (mask, list) = if intl {
(weekend(site, a, 2, true)?, 3)
} else {
(WeekendMask::saturday_sunday(), 2)
};
let holidays = holidays(site, a, list, sv)?;
serial_out(site, k::workday(start, days, mask, &holidays))
}
Used by 4 functions.
networkdays · 20 lines · calc-func/src/catalogue/dates.rs on GitHubfn networkdays(
site: &Site<'_>,
a: &[CalcValue],
sv: &mut EvalServices<'_>,
intl: bool,
) -> Step<CalcValue> {
// NETWORKDAYS.INTL decodes its weekend before reading either date.
let (mask, list) = if intl {
(weekend(site, a, 2, false)?, 3)
} else {
(WeekendMask::saturday_sunday(), 2)
};
reject_logical(&a[0])?;
let system = system(site)?;
let start = super::kit::checked_date_serial(to_1900(system, present_num(site, a, 0)?))?;
reject_logical(&a[1])?;
let end = super::kit::checked_date_serial(to_1900(system, present_num(site, a, 1)?))?;
let holidays = holidays(site, a, list, sv)?;
out(site, k::networkdays(start, end, mask, &holidays))
}
Used by 2 functions.
return_type · 10 lines · calc-func/src/catalogue/dates.rs on GitHub/// The optional return type of WEEKDAY and WEEKNUM.
fn return_type(site: &Site<'_>, a: &[CalcValue], strict: bool) -> Step<Option<f64>> {
match a.get(1) {
None => Ok(None),
// WEEKDAY reads an explicit placeholder as zero; WEEKNUM keeps its default.
Some(value) if value.is_missing() && strict => Ok(None),
Some(_) if strict => strict_num(site, a, 1).map(Some),
Some(_) => num(site, a, 1).map(Some),
}
}
Used by 2 functions.
now · 7 lines · calc-func/src/catalogue/dates.rs on GitHub/// The calculation's clock as a 1900 serial, or the refusal of a call without one.
fn now(site: &Site<'_>) -> Step<f64> {
environment(site)?
.now
.filter(|now| now.is_finite() && *now >= 0.0)
.ok_or_else(|| site.not_modelled(None))
}
Used by 2 functions.