finquant/time/calendars/
botswana.rs

1// Holidays in Botswana.
2
3use crate::time::calendars::Calendar;
4
5use chrono::{NaiveDate, Weekday};
6use serde::{Deserialize, Serialize};
7
8#[derive(Deserialize, Serialize, Default, Debug)]
9pub struct Botswana;
10
11#[typetag::serde]
12impl Calendar for Botswana {
13    fn is_business_day(&self, date: NaiveDate) -> bool {
14        let (d, w, m, y, dd) = self.naive_date_to_dkmy(date);
15        let em = self.easter_monday(y);
16
17        if self.is_weekend(date)
18            // New Year's Day (possibly moved to Weekday::Mon or Weekday::Tue)
19            || ((d == 1 || (d == 2 && w == Weekday::Mon) || (d == 3 && w == Weekday::Tue))
20            && m == 1)
21            // Good Friday
22            || (dd == em - 3)
23            // Easter Weekday::Mon
24            || (dd == em)
25            // Labour Day, 5 1st (possibly moved to Weekday::Mon)
26            || ((d == 1 || (d == 2 && w == Weekday::Mon))
27            && m == 5)
28            // Ascension
29            || (dd == em + 38)
30            // Sir Seretse Khama Day, 7 1st (possibly moved to Weekday::Mon)
31            || ((d == 1 || (d == 2 && w == Weekday::Mon))
32            && m == 7)
33            // Presidents' Day (third Weekday::Mon of 7)
34            || ((15..=21).contains(&d) && w == Weekday::Mon && m == 7)
35            // Independence Day, 9 30th (possibly moved to Weekday::Mon)
36            || ((d == 30 && m == 9) ||
37            (d == 1  && w == Weekday::Mon && m == 10))
38            // Botswana Day, 10 1st (possibly moved to Weekday::Mon or Weekday::Tue)
39            || ((d == 1 || (d == 2 && w == Weekday::Mon) || (d == 3 && w == Weekday::Tue))
40            && m == 10)
41            // Christmas
42            || (d == 25 && m == 12)
43            // Boxing Day (possibly moved to Weekday::Mon)
44            || ((d == 26 || (d == 27 && w == Weekday::Mon))
45            && m == 12)
46        {
47            return false;
48        }
49        true
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::Botswana;
56    use crate::time::calendars::Calendar;
57    use chrono::{Duration, NaiveDate};
58
59    #[test]
60    fn test_botswana_holiday() {
61        // Test all results from 2023-01-01 to 2023-12-31
62        let expected_results_for_2023 = vec![
63            false, false, false, true, true, true, false, false, true, true, true, true, true,
64            false, false, true, true, true, true, true, false, false, true, true, true, true, true,
65            false, false, true, true, true, true, true, false, false, true, true, true, true, true,
66            false, false, true, true, true, true, true, false, false, true, true, true, true, true,
67            false, false, true, true, true, true, true, false, false, true, true, true, true, true,
68            false, false, true, true, true, true, true, false, false, true, true, true, true, true,
69            false, false, true, true, true, true, true, false, false, true, true, true, true,
70            false, false, false, false, true, true, true, true, false, false, true, true, true,
71            true, true, false, false, true, true, true, true, true, false, false, false, true,
72            true, true, true, false, false, true, true, true, true, true, false, false, true, true,
73            true, false, true, false, false, true, true, true, true, true, false, false, true,
74            true, true, true, true, false, false, true, true, true, true, true, false, false, true,
75            true, true, true, true, false, false, true, true, true, true, true, false, false, true,
76            true, true, true, true, false, false, true, true, true, true, true, false, false, true,
77            true, true, true, true, false, false, false, true, true, true, true, false, false,
78            true, true, true, true, true, false, false, true, true, true, true, true, false, false,
79            true, true, true, true, true, false, false, true, true, true, true, true, false, false,
80            true, true, true, true, true, false, false, true, true, true, true, true, false, false,
81            true, true, true, true, true, false, false, true, true, true, true, true, false, false,
82            true, true, true, true, true, false, false, true, true, true, true, true, false, false,
83            false, false, true, true, true, false, false, true, true, true, true, true, false,
84            false, true, true, true, true, true, false, false, true, true, true, true, true, false,
85            false, true, true, true, true, true, false, false, true, true, true, true, true, false,
86            false, true, true, true, true, true, false, false, true, true, true, true, true, false,
87            false, true, true, true, true, true, false, false, true, true, true, true, true, false,
88            false, true, true, true, true, true, false, false, true, true, true, true, true, false,
89            false, false, false, true, true, true, false, false,
90        ];
91        let first_date = NaiveDate::from_ymd_opt(2023, 1, 1).unwrap();
92        for n in 0i32..365 {
93            let target_date = first_date + Duration::try_days(n as i64).unwrap();
94            let expected = expected_results_for_2023[n as usize];
95            assert_eq!(Botswana.is_business_day(target_date), expected);
96        }
97    }
98}