Shared Rust kernels
ported/day_count_common
All modules · quoted at commit 9e1884369a67 of DnaCalc/DnaCalc. Comments are the source's own.
days_in_month · 11 lines · calc-func/src/ported/day_count_common.rs on GitHubfn days_in_month(year: i64, month: i64) -> i64 {
match month {
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
4 | 6 | 9 | 11 => 30,
2 => {
let leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
if leap { 29 } else { 28 }
}
_ => 30,
}
}
Used by 13 functions.
us_30_360 · 34 lines · calc-func/src/ported/day_count_common.rs on GitHub/// NASD US (30/360) day count between two Excel serial dates.
///
/// Excel's basis-0 rule rolls the end date to the 1st of the following month
/// when the end day is 31 and the (already adjusted) start day is below 30,
/// rather than collapsing the end day to 30 in place. The month roll keeps the
/// count non-negative for forward ranges; the in-place form produces short and
/// even negative counts for same-month spans.
pub fn us_30_360(start: i64, end: i64) -> Result<f64, WorksheetErrorCode> {
let (sy, sm, mut sd) = ymd_from_excel_serial(WorkbookDateSystem::System1900, start as f64)
.ok_or(WorksheetErrorCode::Value)?;
let (ey, em, mut ed) = ymd_from_excel_serial(WorkbookDateSystem::System1900, end as f64)
.ok_or(WorksheetErrorCode::Value)?;
// Excel's 1900 calendar has a Feb 29 (serial 60), so in 1900 the 29th is the month end.
let feb_end = |y: i64, m: i64| if y == 1900 { 29 } else { days_in_month(y, m) };
let start_last_feb = sm == 2 && sd == feb_end(sy, sm);
let end_last_feb = em == 2 && ed == feb_end(ey, em);
if sd == 31 || start_last_feb {
sd = 30;
}
if ed == 31 {
if sd < 30 {
let (ny, nm) = if em == 12 { (ey + 1, 1) } else { (ey, em + 1) };
return Ok((ny - sy) as f64 * 360.0 + (nm - sm) as f64 * 30.0 + (1 - sd) as f64);
}
ed = 30;
}
if end_last_feb && start_last_feb {
ed = 30;
}
Ok(((ey - sy) * 360 + (em - sm) * 30 + (ed - sd)) as f64)
}
Used by 13 functions.
real_days_in_year · 7 lines · calc-func/src/ported/day_count_common.rs on GitHubfn real_days_in_year(year: i64) -> f64 {
if (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 {
366.0
} else {
365.0
}
}
Used by 6 functions.
excel_actual_actual_year · 44 lines · calc-func/src/ported/day_count_common.rs on GitHub/// Excel's actual/actual day count (YEARFRAC basis 1, and ACCRINTM basis 1) for `start <= end`
/// serials, as the pair (days, year length) whose quotient is the year fraction. Not the ISDA
/// per-year split:
///
/// * if the dates are at most a year apart (same year, or the next year on an earlier or
/// equal month/day), the year length is 366 when both dates are in one leap year or a Feb 29
/// lies in the span (start before 1 March of a leap start year, or end on/after 1 March of a
/// leap end year, or the end is a real Feb 29), else 365, and the fraction is days / year length;
/// * otherwise days / (days in the whole years start..=end / number of years), the year total
/// on the real calendar while `days` is the serial difference (so it includes Excel's
/// fictitious 1900-02-29).
///
/// The rule is David Wheeler's YEARFRAC analysis (used by Gnumeric and LibreOffice);
/// reproduced on live Excel 20430 on two fresh corpora (W111-5 G8-05, G8-06).
pub fn excel_actual_actual_year(start: i64, end: i64) -> Result<(f64, f64), WorksheetErrorCode> {
let ymd = |serial: i64| {
ymd_from_excel_serial(WorkbookDateSystem::System1900, serial as f64)
.ok_or(WorksheetErrorCode::Value)
};
let serial_of = |y: i64, m: i64, d: i64| {
excel_serial_from_ymd(WorkbookDateSystem::System1900, y, m, d)
.map(|v| v as i64)
.ok_or(WorksheetErrorCode::Value)
};
let is_leap = |y: i64| real_days_in_year(y) == 366.0;
let (sy, sm, sd) = ymd(start)?;
let (ey, em, ed) = ymd(end)?;
let days = (end - start) as f64;
let within_a_year = sy == ey || (ey == sy + 1 && (sm > em || (sm == em && sd >= ed)));
if within_a_year {
let feb29_in_span = (is_leap(sy) && start < serial_of(sy, 3, 1)?)
|| (is_leap(ey) && end >= serial_of(ey, 3, 1)?)
|| (em == 2 && ed == 29 && is_leap(ey));
let year_length = if (sy == ey && is_leap(sy)) || feb29_in_span {
366.0
} else {
365.0
};
return Ok((days, year_length));
}
let years = (ey - sy + 1) as f64;
let days_in_years: f64 = (sy..=ey).map(real_days_in_year).sum();
Ok((days, days_in_years / years))
}
Used by 6 functions.
excel_actual_actual_fraction · 5 lines · calc-func/src/ported/day_count_common.rs on GitHub/// [`excel_actual_actual_year`]'s days divided by its year length.
pub fn excel_actual_actual_fraction(start: i64, end: i64) -> Result<f64, WorksheetErrorCode> {
let (days, year_length) = excel_actual_actual_year(start, end)?;
Ok(days / year_length)
}
Used by 2 functions.