1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RustQuant: A Rust library for quantitative finance tools.
// Copyright (C) 2023 https://github.com/avhz
// Dual licensed under Apache 2.0 and MIT.
// See:
//      - LICENSE-APACHE.md
//      - LICENSE-MIT.md
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

use crate::time::{is_weekend, Calendar};
use time::{Month, OffsetDateTime, Weekday};

/// Canadian settlement calendar.
pub struct Canada;

impl Calendar for Canada {
    fn name(&self) -> &'static str {
        "Canada"
    }

    fn is_business_day(&self, date: OffsetDateTime) -> bool {
        let w = date.weekday();
        let d = date.day();
        let m = date.month();
        let y = date.year();
        let dd = date.ordinal(); // Day of the year

        let em = crate::time::easter_monday(y as usize, false); // assuming you have a similar easter_monday function

        if is_weekend(date)
            || ((d == 1 || ((d == 2 || d == 3) && w == Weekday::Monday)) && m == Month::January)
            || ((15..=21).contains(&d) && w == Weekday::Monday && m == Month::February && y >= 2008)
            || (dd == em - 3)
            || (d > 17 && d <= 24 && w == Weekday::Monday && m == Month::May)
            || ((d == 1 || ((d == 2 || d == 3) && w == Weekday::Monday)) && m == Month::July)
            || (d <= 7 && w == Weekday::Monday && m == Month::August)
            || (d <= 7 && w == Weekday::Monday && m == Month::September)
            || (((d == 30 && m == Month::September)
                || (d <= 2 && m == Month::October && w == Weekday::Monday))
                && y >= 2021)
            || (d > 7 && d <= 14 && w == Weekday::Monday && m == Month::October)
            || ((d == 11 || ((d == 12 || d == 13) && w == Weekday::Monday)) && m == Month::November)
            || ((d == 25 || (d == 27 && (w == Weekday::Monday || w == Weekday::Tuesday)))
                && m == Month::December)
            || ((d == 26 || (d == 28 && (w == Weekday::Monday || w == Weekday::Tuesday)))
                && m == Month::December)
        {
            return false;
        }

        true
    }
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// UNIT TESTS for Canada
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#[cfg(test)]
mod test_canada {
    use super::*;
    use time::macros::datetime;

    // Test to verify the name() method.
    #[test]
    fn test_name() {
        let calendar = Canada;
        assert_eq!(calendar.name(), "Canada");
    }

    // Test to verify if weekends are not considered business days.
    #[test]
    fn test_is_weekend() {
        let calendar = Canada;
        let sat = datetime!(2023-08-26 12:00:00 UTC);
        let sun = datetime!(2023-08-27 12:00:00 UTC);
        assert!(!calendar.is_business_day(sat));
        assert!(!calendar.is_business_day(sun));
    }

    // Test to verify if the is_business_day() method properly accounts for public holidays.
    #[test]
    fn test_is_public_holiday() {
        let calendar = Canada;
        let new_years_day = datetime!(2023-01-01 12:00:00 UTC);
        let family_day = datetime!(2023-02-20 12:00:00 UTC); // 3rd Monday of February
        let canada_day = datetime!(2023-07-01 12:00:00 UTC);
        let thanksgiving = datetime!(2023-10-09 12:00:00 UTC); // 2nd Monday in October
        let christmas = datetime!(2023-12-25 12:00:00 UTC);

        assert!(!calendar.is_business_day(new_years_day));
        assert!(!calendar.is_business_day(family_day));
        assert!(!calendar.is_business_day(canada_day));
        assert!(!calendar.is_business_day(thanksgiving));
        assert!(!calendar.is_business_day(christmas));
    }

    // Test to verify if the is_business_day() method properly accounts for regular business days.
    #[test]
    fn test_is_regular_business_day() {
        let calendar = Canada;
        let regular_day1 = datetime!(2023-03-01 12:00:00 UTC);
        let regular_day2 = datetime!(2023-07-12 12:00:00 UTC);
        let regular_day3 = datetime!(2023-11-17 12:00:00 UTC);

        assert!(calendar.is_business_day(regular_day1));
        assert!(calendar.is_business_day(regular_day2));
        assert!(calendar.is_business_day(regular_day3));
    }
}