use crate::calendar::Calendar;
use crate::utilities::unpack_date;
use time::{Date, Month, Weekday};
use RustQuant_iso::*;
pub struct UnitedStatesCalendar;
impl Calendar for UnitedStatesCalendar {
fn new() -> Self {
Self
}
fn name(&self) -> &'static str {
"United States of America"
}
fn country_code(&self) -> ISO_3166 {
UNITED_STATES_OF_AMERICA
}
fn market_identifier_code(&self) -> ISO_10383 {
XNYS
}
fn is_holiday(&self, date: Date) -> bool {
let (y, m, d, wd, _, _) = unpack_date(date, false);
if (
((d == 1 || (d == 2 && wd == Weekday::Monday)) && m == Month::January)
|| (d == 31 && wd == Weekday::Friday && m == Month::December)
|| ((15..=21).contains(&d) && wd == Weekday::Monday && m == Month::January && y >= 1983)
|| self.is_washington_birthday(date)
|| self.is_memorial_day(date)
|| self.is_juneteenth(date, true)
|| ((d == 4 || (d == 5 && wd == Weekday::Monday) || (d == 3 && wd == Weekday::Friday)) && m == Month::July)
|| self.is_labor_day(date)
|| self.is_columbus_day(date)
|| self.is_veterans_day(date)
|| ((22..=28).contains(&d) && wd == Weekday::Thursday && m == Month::November)
|| ((d == 25 || (d == 26 && wd == Weekday::Monday) || (d == 24 && wd == Weekday::Friday)) && m == Month::December)
) {
return true;
}
false
}
}
impl Default for UnitedStatesCalendar {
fn default() -> Self {
Self::new()
}
}
impl UnitedStatesCalendar {
pub fn new() -> Self {
UnitedStatesCalendar
}
fn is_washington_birthday(&self, date: Date) -> bool {
let (y, m, d, wd, _, _) = unpack_date(date, false);
if (y >= 1971) {
(15..=21).contains(&d) && wd == Weekday::Monday && m == Month::February
} else {
(d == 22 || (d == 23 && wd == Weekday::Monday) || (d == 21 && wd == Weekday::Friday))
&& m == Month::February
}
}
fn is_memorial_day(&self, date: Date) -> bool {
let (y, m, d, wd, _, _) = unpack_date(date, false);
if (y >= 1971) {
d >= 25 && wd == Weekday::Monday && m == Month::May
} else {
(d == 30 || (d == 31 && wd == Weekday::Monday) || (d == 29 && wd == Weekday::Friday))
&& m == Month::May
}
}
fn is_labor_day(&self, date: Date) -> bool {
let (_, m, d, wd, _, _) = unpack_date(date, false);
d <= 7 && wd == Weekday::Monday && m == Month::September
}
fn is_columbus_day(&self, date: Date) -> bool {
let (y, m, d, wd, _, _) = unpack_date(date, false);
(8..=14).contains(&d) && wd == Weekday::Monday && m == Month::October && y >= 1971
}
fn is_veterans_day(&self, date: Date) -> bool {
let (y, m, d, wd, _, _) = unpack_date(date, false);
if (y <= 1970 || y >= 1978) {
(d == 11 || (d == 12 && wd == Weekday::Monday) || (d == 10 && wd == Weekday::Friday))
&& m == Month::November
} else {
(22..=28).contains(&d) && wd == Weekday::Monday && m == Month::October
}
}
fn is_juneteenth(&self, date: Date, move_to_friday: bool) -> bool {
let (y, m, d, wd, _, _) = unpack_date(date, false);
(d == 19
|| (d == 20 && wd == Weekday::Monday)
|| ((d == 18 && wd == Weekday::Friday) && move_to_friday))
&& m == Month::June
&& y >= 2022
}
}
#[cfg(test)]
mod test_united_states {
use super::*;
use time::macros::date;
#[test]
fn test_name() {
let calendar = UnitedStatesCalendar;
assert_eq!(calendar.name(), "United States of America");
}
#[test]
fn test_is_weekend() {
let calendar = UnitedStatesCalendar;
let sat = date!(2023 - 08 - 26);
let sun = date!(2023 - 08 - 27);
assert!(!calendar.is_business_day(sat));
assert!(!calendar.is_business_day(sun));
}
#[test]
fn test_is_public_holiday() {
let calendar = UnitedStatesCalendar;
let new_years_day = date!(2023 - 01 - 01);
let independence_day = date!(2023 - 07 - 04);
let labor_day = date!(2023 - 09 - 04); let thanksgiving = date!(2023 - 11 - 23); let christmas = date!(2023 - 12 - 25);
let washington_birthday = date!(2023 - 02 - 20); let memorial_day = date!(2023 - 05 - 29); let juneteenth = date!(2023 - 06 - 19);
assert!(!calendar.is_business_day(new_years_day));
assert!(!calendar.is_business_day(independence_day));
assert!(!calendar.is_business_day(labor_day));
assert!(!calendar.is_business_day(thanksgiving));
assert!(!calendar.is_business_day(christmas));
assert!(!calendar.is_business_day(washington_birthday));
assert!(!calendar.is_business_day(memorial_day));
assert!(!calendar.is_business_day(juneteenth));
}
#[test]
fn test_is_regular_business_day() {
let calendar = UnitedStatesCalendar;
let regular_day1 = date!(2023 - 03 - 15);
let regular_day2 = date!(2023 - 08 - 15);
let regular_day3 = date!(2023 - 10 - 25);
assert!(calendar.is_business_day(regular_day1));
assert!(calendar.is_business_day(regular_day2));
assert!(calendar.is_business_day(regular_day3));
}
}