use crate::{
Calendar, Date, EasterMethod, EasterOffset, FixedDate, LastWeekday, Month, NthWeekday, OneOff,
Ordinal, Rule, Weekday, Weekend, WeekendShift, Year, YearRange, easter_monday,
};
pub const GOVERNMENT_BOND: Calendar<'static> = Calendar {
name: "US government bond market",
weekend: Weekend::SAT_SUN,
rules: &[
Rule::Fixed(FixedDate::new(Month::Jan, 1).shift(WeekendShift::SunForward)),
Rule::NthWeekday(
NthWeekday::new(Ordinal::Third, Weekday::Mon, Month::Jan)
.from_year(Year::literal(1983)),
),
Rule::Fixed(
FixedDate::new(Month::Feb, 22)
.shift(WeekendShift::SatBackSunForward)
.years(YearRange::literal_through(1970)),
),
Rule::NthWeekday(
NthWeekday::new(Ordinal::Third, Weekday::Mon, Month::Feb)
.from_year(Year::literal(1971)),
),
Rule::Easter(EasterOffset::good_friday().years(YearRange::literal_through(1995))),
Rule::Custom(good_friday_post_1996),
Rule::Fixed(
FixedDate::new(Month::May, 30)
.shift(WeekendShift::SatBackSunForward)
.years(YearRange::literal_through(1970)),
),
Rule::LastWeekday(
LastWeekday::new(Weekday::Mon, Month::May).from_year(Year::literal(1971)),
),
Rule::Fixed(
FixedDate::new(Month::Jun, 19)
.shift(WeekendShift::SatBackSunForward)
.from_year(Year::literal(2022)),
),
Rule::Fixed(FixedDate::new(Month::Jul, 4).shift(WeekendShift::SatBackSunForward)),
Rule::NthWeekday(NthWeekday::new(Ordinal::First, Weekday::Mon, Month::Sep)),
Rule::NthWeekday(
NthWeekday::new(Ordinal::Second, Weekday::Mon, Month::Oct)
.from_year(Year::literal(1971)),
),
Rule::Fixed(
FixedDate::new(Month::Nov, 11)
.shift(WeekendShift::SunForward)
.years(YearRange::literal_through(1970)),
),
Rule::NthWeekday(
NthWeekday::new(Ordinal::Fourth, Weekday::Mon, Month::Oct)
.years(YearRange::literal_between(1971, 1977)),
),
Rule::Fixed(
FixedDate::new(Month::Nov, 11)
.shift(WeekendShift::SunForward)
.from_year(Year::literal(1978)),
),
Rule::NthWeekday(NthWeekday::new(Ordinal::Fourth, Weekday::Thu, Month::Nov)),
Rule::Fixed(FixedDate::new(Month::Dec, 25).shift(WeekendShift::SatBackSunForward)),
Rule::OneOff(OneOff::new(Date::literal(2004, Month::Jun, 11))), Rule::OneOff(OneOff::new(Date::literal(2012, Month::Oct, 30))), Rule::OneOff(OneOff::new(Date::literal(2018, Month::Dec, 5))), ],
};
fn good_friday_post_1996(d: Date) -> bool {
let y = d.year().get();
if y < 1996 || d.day() <= 7 {
return false;
}
let em_doy = easter_monday(d.year(), EasterMethod::Western);
em_doy >= 3 && d.day_of_year() + 3 == em_doy
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
fn ymd(y: u16, m: Month, d: u8) -> Date {
Date::from_ymd(y, m, d).unwrap()
}
#[test]
fn govbond_holidays_2024() {
assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::Jan, 1)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::Jan, 15)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::Feb, 19)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::Mar, 29))); assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::May, 27)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::Jun, 19)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::Jul, 4)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::Sep, 2)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::Oct, 14)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::Nov, 11)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::Nov, 28)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::Dec, 25)));
}
#[test]
fn good_friday_nfp_exception_since_1996() {
assert!(GOVERNMENT_BOND.is_holiday(ymd(1995, Month::Apr, 14)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(2024, Month::Mar, 29)));
assert!(!GOVERNMENT_BOND.is_holiday(ymd(2026, Month::Apr, 3)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(1988, Month::Apr, 1)));
}
#[test]
fn govbond_veterans_day_no_saturday_back() {
assert!(GOVERNMENT_BOND.is_holiday(ymd(1994, Month::Nov, 11)));
assert!(!GOVERNMENT_BOND.is_holiday(ymd(1995, Month::Nov, 10)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(1990, Month::Nov, 12)));
}
#[test]
fn govbond_new_year_sun_forward_only() {
assert!(!GOVERNMENT_BOND.is_holiday(ymd(2021, Month::Dec, 31)));
assert!(GOVERNMENT_BOND.is_holiday(ymd(2023, Month::Jan, 2)));
}
#[test]
fn govbond_special_closings() {
assert!(GOVERNMENT_BOND.is_holiday(ymd(2018, Month::Dec, 5))); assert!(GOVERNMENT_BOND.is_holiday(ymd(2012, Month::Oct, 30))); assert!(!GOVERNMENT_BOND.is_holiday(ymd(2012, Month::Oct, 29))); assert!(GOVERNMENT_BOND.is_holiday(ymd(2004, Month::Jun, 11))); }
}