rateslib/scheduling/calendars/
calendar.rs

1use chrono::prelude::*;
2use pyo3::{FromPyObject, IntoPyObject};
3use serde::{Deserialize, Serialize};
4use std::convert::From;
5
6use crate::scheduling::{Cal, CalendarAdjustment, DateRoll, NamedCal, UnionCal};
7
8/// Create a `NaiveDateTime` with default null time.
9///
10/// Panics if date values are invalid.
11pub fn ndt(year: i32, month: u32, day: u32) -> NaiveDateTime {
12    NaiveDate::from_ymd_opt(year, month, day)
13        .expect("`year`, `month` `day` are invalid.")
14        .and_hms_opt(0, 0, 0)
15        .unwrap()
16}
17
18/// Container for calendar types.
19#[derive(Debug, Clone, PartialEq, FromPyObject, Serialize, Deserialize, IntoPyObject)]
20pub enum Calendar {
21    Cal(Cal),
22    UnionCal(UnionCal),
23    NamedCal(NamedCal),
24}
25
26impl From<Cal> for Calendar {
27    fn from(item: Cal) -> Self {
28        Calendar::Cal(item)
29    }
30}
31
32impl From<UnionCal> for Calendar {
33    fn from(item: UnionCal) -> Self {
34        Calendar::UnionCal(item)
35    }
36}
37
38impl From<NamedCal> for Calendar {
39    fn from(item: NamedCal) -> Self {
40        Calendar::NamedCal(item)
41    }
42}
43
44impl DateRoll for Calendar {
45    fn is_weekday(&self, date: &NaiveDateTime) -> bool {
46        match self {
47            Calendar::Cal(c) => c.is_weekday(date),
48            Calendar::UnionCal(c) => c.is_weekday(date),
49            Calendar::NamedCal(c) => c.is_weekday(date),
50        }
51    }
52
53    fn is_holiday(&self, date: &NaiveDateTime) -> bool {
54        match self {
55            Calendar::Cal(c) => c.is_holiday(date),
56            Calendar::UnionCal(c) => c.is_holiday(date),
57            Calendar::NamedCal(c) => c.is_holiday(date),
58        }
59    }
60
61    fn is_settlement(&self, date: &NaiveDateTime) -> bool {
62        match self {
63            Calendar::Cal(c) => c.is_settlement(date),
64            Calendar::UnionCal(c) => c.is_settlement(date),
65            Calendar::NamedCal(c) => c.is_settlement(date),
66        }
67    }
68}
69
70impl CalendarAdjustment for Calendar {}
71
72// UNIT TESTS
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_docstring() {
79        let ldn = Cal::new(vec![ndt(2017, 5, 1)], vec![5, 6]); // UK Monday 1st May Bank Holiday
80        let tky = Cal::new(
81            vec![ndt(2017, 5, 3), ndt(2017, 5, 4), ndt(2017, 5, 5)],
82            vec![5, 6],
83        );
84
85        let date = ndt(2017, 4, 28); // Friday 28th April 2017
86        let spot = ldn.add_bus_days(&date, 2, true).unwrap();
87        assert_eq!(spot, ndt(2017, 5, 3));
88
89        let ldn_tky = UnionCal::new(vec![ldn, tky], None);
90        let spot = ldn_tky.add_bus_days(&date, 2, true).unwrap();
91        assert_eq!(spot, ndt(2017, 5, 8));
92
93        let tgt = Cal::new(vec![], vec![5, 6]);
94        let nyc = Cal::new(vec![ndt(2023, 6, 19)], vec![5, 6]); // Juneteenth Holiday
95        let tgt_nyc = UnionCal::new(vec![tgt], vec![nyc].into());
96
97        let date = ndt(2023, 6, 16);
98        let spot = tgt_nyc.add_bus_days(&date, 2, true).unwrap();
99        assert_eq!(spot, ndt(2023, 6, 20));
100
101        let date = ndt(2023, 6, 15);
102        let spot = tgt_nyc.add_bus_days(&date, 2, true).unwrap();
103        assert_eq!(spot, ndt(2023, 6, 20));
104
105        let spot = tgt_nyc.add_bus_days(&date, 2, false).unwrap();
106        assert_eq!(spot, ndt(2023, 6, 19));
107    }
108}