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 GitHub
fn 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 GitHub
fn 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 GitHub
fn 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.