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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Uzbekistan calendar.
//!
//! Port of `ql/time/calendars/uzbekistan.{hpp,cpp}`.
use crate::shared::shared;
use crate::time::calendar::{Calendar, CalendarImpl, is_weekend_sat_sun};
use crate::time::calendars::islamicholidays::moon_sighting::{is_eid_al_adha, is_eid_al_fitr};
use crate::time::date::{Date, Month, Year};
use crate::time::weekday::Weekday;
/// Last year for which Uzbekistan's Islamic (Eid) holidays are tabulated via
/// `moon_sighting` (matching QuantLib's data). Past this year the calendar
/// falls through to its weekend and recurring rules, exactly like QuantLib
/// (which never checks the year).
pub const HOLIDAY_HORIZON: Year = 2040;
/// Market handled by the Uzbekistan calendar.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Market {
/// Uzbekistan Stock Exchange.
Uzse,
}
/// The Uzbekistan calendar. The default market is [`Market::Uzse`].
///
/// # Accuracy
///
/// Uzbekistan's Islamic (Eid) holidays are tabulated (from QuantLib, via the
/// `moon_sighting` tables) only through 2040. After 2040 the tabulated holidays
/// are not observed and the calendar falls through to its weekend and recurring
/// rules, exactly like QuantLib (which never checks the year).
pub struct Uzbekistan;
impl Uzbekistan {
/// Builds an Uzbekistan calendar for the given `market`.
pub fn new(market: Market) -> Calendar {
let imp: crate::shared::Shared<dyn CalendarImpl> = match market {
Market::Uzse => shared(Impl),
};
Calendar::from_impl(imp)
}
}
struct Impl;
impl CalendarImpl for Impl {
fn holiday_horizon(&self) -> Option<Year> {
Some(HOLIDAY_HORIZON)
}
fn name(&self) -> String {
"Uzbekistan Stock Exchange".to_string()
}
fn is_weekend(&self, w: Weekday) -> bool {
is_weekend_sat_sun(w)
}
fn is_business_day(&self, date: Date) -> bool {
let w = date.weekday();
let d = date.day_of_month();
let m = date.month();
!(is_weekend_sat_sun(w)
|| is_eid_al_fitr(date)
|| is_eid_al_adha(date)
// New Year's Day
|| (d == 1 && m == Month::January)
// International Womens Day
|| (d == 8 && m == Month::March)
// Navruz(Persian New Year)
|| (d == 21 && m == Month::March)
// Day of Remembrance and Honors
|| (d == 9 && m == Month::May)
// Independence Day
|| (d == 1 && m == Month::September)
// Teachers Day
|| (d == 1 && m == Month::October)
// Constitution Day
|| (d == 8 && m == Month::December))
}
}
#[cfg(test)]
mod tests {
use super::*;
// Spot-checks, not a full transcription of test-suite/calendars.cpp.
#[test]
fn name_matches_quantlib() {
assert_eq!(
Uzbekistan::new(Market::Uzse).name(),
"Uzbekistan Stock Exchange"
);
}
#[test]
fn fixed_holidays() {
let c = Uzbekistan::new(Market::Uzse);
for (d, m) in [
(1, Month::January), // New Year's Day
(8, Month::March), // International Women's Day
(21, Month::March), // Navruz
(9, Month::May), // Day of Remembrance and Honors
(1, Month::September), // Independence Day
(1, Month::October), // Teachers' Day
(8, Month::December), // Constitution Day
] {
assert!(c.is_holiday(Date::new(d, m, 2020)), "{d} {m} 2020");
}
}
#[test]
fn weekend_rule() {
let c = Uzbekistan::new(Market::Uzse);
assert!(c.is_weekend(Weekday::Saturday));
assert!(c.is_weekend(Weekday::Sunday));
assert!(!c.is_weekend(Weekday::Friday));
}
#[test]
fn in_horizon_query_works() {
// A query at the last tabulated year must not panic. New Year's Day is
// unconditional.
let c = Uzbekistan::new(Market::Uzse);
assert!(c.is_holiday(Date::new(1, Month::January, HOLIDAY_HORIZON)));
}
#[test]
fn beyond_horizon_falls_through() {
// QuantLib never checks the year: past the tables only weekends and
// recurring rules apply, and tabulated dates do not recur.
let c = Uzbekistan::new(Market::Uzse);
assert!(c.is_holiday(Date::new(21, Month::March, 2041)));
assert!(c.is_business_day(Date::new(22, Month::March, 2041)));
}
}