Skip to content

Commit 94d96cd

Browse files
authored
Merge pull request #289 from nadav7679/israel-calander
Basic support for Israel (Jewish) Calander
2 parents e2ffef0 + c3e5f92 commit 94d96cd

File tree

3 files changed

+234
-1
lines changed

3 files changed

+234
-1
lines changed

crates/RustQuant_time/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ RustQuant = { path = "../RustQuant" }
2020
[dependencies]
2121
RustQuant_iso = { workspace = true }
2222
RustQuant_utils = { workspace = true }
23-
23+
icu = "1.5.0"
24+
serde.workspace = true
2425
time = { workspace = true }
2526

2627
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2+
// RustQuant: A Rust library for quantitative finance tools.
3+
// Copyright (C) 2022-2024 https://github.com/avhz
4+
// Dual licensed under Apache 2.0 and MIT.
5+
// See:
6+
// - LICENSE-APACHE.md
7+
// - LICENSE-MIT.md
8+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9+
10+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11+
// IMPORTS
12+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13+
14+
use time::{Date, Weekday};
15+
16+
use icu;
17+
18+
use crate::calendar::Calendar;
19+
use crate::utilities::unpack_date;
20+
use RustQuant_iso::*;
21+
22+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23+
// CONSTANTS
24+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25+
26+
const JEWISH_HOLIDAYS: [(u8, u8); 16] = [
27+
(12, 29), // Jewish new year (Rosh Hashana) I
28+
(1, 1), // Jewish new year (Rosh Hashana) II
29+
(1, 2), // Jewish new year (Rosh Hashana) II
30+
(1, 9), // Yom Kippur I
31+
(1, 10), // Yom Kippur II
32+
(1, 14), // Sukkot I
33+
(1, 15), // Sukkot II
34+
(1, 22), // Simchat Torah I
35+
(1, 23), // Simchat Torah II
36+
(6, 14), // Purim
37+
(7, 14), // Passover I
38+
(7, 15), // Passover II
39+
(7, 20), // Passover two I
40+
(7, 21), // Passover two II
41+
(9, 5), // Shavut I
42+
(9, 6), // Shavut I
43+
];
44+
45+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
46+
// STRUCTS, ENUMS, TRAITS
47+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
48+
49+
/// Israel a national holiday calendar.
50+
pub struct IsraelCalendar;
51+
52+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
53+
// IMPLEMENTATIONS, METHODS
54+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
55+
56+
impl IsraelCalendar {
57+
/// Hebrew weekend is Friday and Saturday,
58+
/// as opposed to Saturday and Sunday in the Gregorian calendar.
59+
fn is_weekend(&self, date: Date) -> bool {
60+
let wd = date.weekday();
61+
wd == Weekday::Friday || wd == Weekday::Saturday
62+
}
63+
}
64+
65+
impl Calendar for IsraelCalendar {
66+
fn name(&self) -> &'static str {
67+
"Israel"
68+
}
69+
70+
fn country_code(&self) -> ISO_3166 {
71+
ISRAEL
72+
}
73+
74+
fn market_identifier_code(&self) -> ISO_10383 {
75+
XTAE
76+
}
77+
78+
fn is_business_day(&self, date: Date) -> bool {
79+
!self.is_weekend(date) && !self.is_holiday(date)
80+
}
81+
82+
fn is_holiday(&self, date: Date) -> bool {
83+
let (y, m, d, wd, _, _) = unpack_date(date, false);
84+
let m = m as u8;
85+
let iso_date = icu::calendar::Date::try_new_iso_date(y, m, d)
86+
.expect("Failed to initialize ISO Date instance for constructing Hebrew date.");
87+
88+
let hebrew_date = iso_date.to_calendar(icu::calendar::hebrew::Hebrew);
89+
let mut hebrew_month = hebrew_date.month().ordinal as u8;
90+
let hebrew_day = hebrew_date.day_of_month().0 as u8;
91+
92+
if hebrew_date.is_in_leap_year() && hebrew_month > 7 {
93+
hebrew_month -= 1;
94+
}
95+
96+
let is_independence_or_memorial_day = match &(hebrew_month, hebrew_day, wd) {
97+
(8, 3..=4, Weekday::Thursday) => true,
98+
(8, 2..=3, Weekday::Wednesday) => true,
99+
(8, 5, Weekday::Monday) => true,
100+
(8, 6, Weekday::Tuesday) => true,
101+
(8, 5, Weekday::Wednesday) => true,
102+
(8, 4, Weekday::Tuesday) => true,
103+
_ => false,
104+
};
105+
106+
let is_tisha_beav = match &(hebrew_month, hebrew_day, wd) {
107+
(11, 10, Weekday::Sunday) => true,
108+
(11, 9, Weekday::Saturday) => false,
109+
(11, 9, _) => true,
110+
_ => false,
111+
};
112+
113+
JEWISH_HOLIDAYS.contains(&(hebrew_month, hebrew_day))
114+
|| is_independence_or_memorial_day
115+
|| is_tisha_beav
116+
}
117+
}
118+
119+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
120+
// UNIT TESTS
121+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
122+
123+
#[cfg(test)]
124+
mod test_israel {
125+
use super::*;
126+
use time::macros::date;
127+
use time::Month;
128+
129+
// Test to verify the name() method.
130+
#[test]
131+
fn test_name() {
132+
let calendar = IsraelCalendar;
133+
assert_eq!(calendar.name(), "Israel");
134+
}
135+
136+
// Test to verify if weekends are not considered business days.
137+
#[test]
138+
fn test_is_weekend() {
139+
let calendar = IsraelCalendar;
140+
let fri = date!(2023 - 01 - 27);
141+
let sat = date!(2023 - 01 - 28);
142+
assert!(!calendar.is_business_day(fri));
143+
assert!(!calendar.is_business_day(sat));
144+
}
145+
146+
// Test to verify if the is_business_day() method properly accounts for public holidays.
147+
#[test]
148+
fn test_is_public_holiday() {
149+
let calendar = IsraelCalendar;
150+
let holidays = vec![
151+
(2024, 3, 24), // Purim
152+
(2024, 4, 22), // Passover Eve
153+
(2024, 4, 23), // Passover
154+
(2024, 4, 28), // Passover II Eve
155+
(2024, 4, 29), // Passover II
156+
(2024, 5, 13), // Memorial Day
157+
(2024, 5, 14), // Independence Day
158+
(2024, 6, 11), // Pentecost (Shavuot) Eve
159+
(2024, 6, 12), // Pentecost (Shavuot)
160+
(2024, 8, 13), // Fast Day (Tisha B'Av)
161+
(2024, 10, 3), // Jewish New Year I
162+
(2024, 10, 4), // Jewish New Year II
163+
(2024, 10, 11), // Yom Kippur Eve
164+
(2024, 10, 17), // Feast of Tabernacles (Sukkoth)
165+
(2024, 10, 24), // Rejoicing of the Law (Simchat Tora)
166+
(2025, 3, 14), // Purim
167+
(2025, 4, 13), // Passover
168+
(2025, 6, 2), // Pentecost (Shavuot)
169+
(2025, 8, 3), // Fast Day (Tisha B'Av)
170+
(2025, 9, 23), // Jewish New Year I
171+
(2025, 9, 24), // Jewish New Year II
172+
(2025, 10, 2), // Yom Kippur
173+
(2025, 10, 7), // Feast of Tabernacles (Sukkoth)
174+
(2025, 10, 14), // Rejoicing of the Law (Simchat Tora)
175+
(2015, 3, 5), // Purim
176+
(2015, 4, 10), // Passover II
177+
(2015, 4, 23), // Independence Day
178+
(2015, 5, 24), // Pentecost (Shavuot)
179+
(2015, 7, 26), // Fast Day
180+
(2015, 9, 14), // Jewish New Year I
181+
(2015, 9, 15), // Jewish New Year II
182+
(2015, 9, 23), // Yom Kippur
183+
(2015, 9, 28), // Feast of Tabernacles (Sukkoth)
184+
(2015, 10, 5), // Rejoicing of the Law (Simchat Tora)
185+
(2018, 3, 1), // Purim
186+
(2018, 4, 6), // Passover II
187+
(2018, 4, 19), // Independence Day
188+
(2018, 5, 20), // Pentecost (Shavuot)
189+
(2018, 7, 22), // Fast Day
190+
(2018, 9, 10), // Jewish New Year I
191+
(2018, 9, 11), // Jewish New Year II
192+
(2018, 9, 18), // Yom Kippur Eve
193+
(2018, 9, 19), // Yom Kippur
194+
(2018, 9, 24), // Feast of Tabernacles (Sukkoth)
195+
(2018, 10, 1), // Rejoicing of the Law (Simchat Tora)
196+
(2017, 3, 12), // Purim
197+
(2017, 4, 11), // Passover 1
198+
(2017, 4, 17), // Passover II
199+
(2017, 5, 2), // Independence Day
200+
(2017, 5, 31), // Pentecost (Shavuot)
201+
(2017, 8, 1), // Fast Day
202+
(2017, 9, 21), // Jewish New Year I
203+
(2017, 9, 22), // Jewish New Year II
204+
(2017, 9, 29), // Yom Kippur Eve
205+
(2017, 10, 5), // Feast of Tabernacles (Sukkoth)
206+
(2017, 10, 12), // Rejoicing of the Law (Simchat Tora)
207+
];
208+
for (y, m, d) in holidays {
209+
let date = Date::from_calendar_date(y, Month::try_from(m).unwrap(), d).unwrap();
210+
assert!(!calendar.is_business_day(date));
211+
}
212+
}
213+
214+
// Test to verify if the is_business_day() method properly accounts for regular business days.
215+
#[test]
216+
fn test_is_regular_business_day() {
217+
let calendar = IsraelCalendar;
218+
let regular_day1 = date!(2021 - 08 - 04);
219+
let regular_day2 = date!(2024 - 04 - 09);
220+
let regular_day3 = date!(2023 - 11 - 27);
221+
222+
assert!(calendar.is_business_day(regular_day1));
223+
assert!(calendar.is_business_day(regular_day2));
224+
assert!(calendar.is_business_day(regular_day3));
225+
}
226+
}

crates/RustQuant_time/src/countries/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,9 @@ pub mod south_america {
8080
/// Chile holidays and calendars.
8181
pub mod chile;
8282
}
83+
84+
/// Calanders implemented for Middle Eastern countries.
85+
pub mod middle_east {
86+
/// Israeli (Jewish) holidays and calander, implemented with an external API.
87+
pub mod israel;
88+
}

0 commit comments

Comments
 (0)