Skip to content

Commit

Permalink
Add public holidays calendars for Ukraine, UK, and US
Browse files Browse the repository at this point in the history
Added implementation of public holidays for Ukraine, the UK, and the US in the third-party calendar module. Including various markets and special closures days. Calendar functionality was expanded to indicate business days specific to these countries.  This will aid in accurate business day calculations for these markets. Test cases were also added to validate the accuracy of the holidays and business days implemented.
  • Loading branch information
nakashima-hikaru committed Jan 6, 2024
1 parent d4617f0 commit e5950e6
Show file tree
Hide file tree
Showing 2 changed files with 259 additions and 0 deletions.
15 changes: 15 additions & 0 deletions third-parties/calendar/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "calendar"
version.workspace = true
authors.workspace = true
edition.workspace = true
repository.workspace = true
keywords.workspace = true
categories.workspace = true
readme = "../../README.md"
description = "Time related code for the qlab"
license-file = "LICENSE"

[dependencies]
chrono = "0.4.31"
num-traits = { workspace = true }
244 changes: 244 additions & 0 deletions third-parties/calendar/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
use chrono::{Datelike, Duration, NaiveDate, Weekday};
use std::fmt::Debug;

const EASTER_MONDAY: [u32; 299] = [
98, 90, 103, 95, 114, 106, 91, 111, 102, // 1901-1909
87, 107, 99, 83, 103, 95, 115, 99, 91, 111, // 1910-1919
96, 87, 107, 92, 112, 103, 95, 108, 100, 91, // 1920-1929
111, 96, 88, 107, 92, 112, 104, 88, 108, 100, // 1930-1939
85, 104, 96, 116, 101, 92, 112, 97, 89, 108, // 1940-1949
100, 85, 105, 96, 109, 101, 93, 112, 97, 89, // 1950-1959
109, 93, 113, 105, 90, 109, 101, 86, 106, 97, // 1960-1969
89, 102, 94, 113, 105, 90, 110, 101, 86, 106, // 1970-1979
98, 110, 102, 94, 114, 98, 90, 110, 95, 86, // 1980-1989
106, 91, 111, 102, 94, 107, 99, 90, 103, 95, // 1990-1999
115, 106, 91, 111, 103, 87, 107, 99, 84, 103, // 2000-2009
95, 115, 100, 91, 111, 96, 88, 107, 92, 112, // 2010-2019
104, 95, 108, 100, 92, 111, 96, 88, 108, 92, // 2020-2029
112, 104, 89, 108, 100, 85, 105, 96, 116, 101, // 2030-2039
93, 112, 97, 89, 109, 100, 85, 105, 97, 109, // 2040-2049
101, 93, 113, 97, 89, 109, 94, 113, 105, 90, // 2050-2059
110, 101, 86, 106, 98, 89, 102, 94, 114, 105, // 2060-2069
90, 110, 102, 86, 106, 98, 111, 102, 94, 114, // 2070-2079
99, 90, 110, 95, 87, 106, 91, 111, 103, 94, // 2080-2089
107, 99, 91, 103, 95, 115, 107, 91, 111, 103, // 2090-2099
88, 108, 100, 85, 105, 96, 109, 101, 93, 112, // 2100-2109
97, 89, 109, 93, 113, 105, 90, 109, 101, 86, // 2110-2119
106, 97, 89, 102, 94, 113, 105, 90, 110, 101, // 2120-2129
86, 106, 98, 110, 102, 94, 114, 98, 90, 110, // 2130-2139
95, 86, 106, 91, 111, 102, 94, 107, 99, 90, // 2140-2149
103, 95, 115, 106, 91, 111, 103, 87, 107, 99, // 2150-2159
84, 103, 95, 115, 100, 91, 111, 96, 88, 107, // 2160-2169
92, 112, 104, 95, 108, 100, 92, 111, 96, 88, // 2170-2179
108, 92, 112, 104, 89, 108, 100, 85, 105, 96, // 2180-2189
116, 101, 93, 112, 97, 89, 109, 100, 85, 105, // 2190-2199
];

const ORTHODOX_EASTER_MONDAY: [u32; 299] = [
105, 118, 110, 102, 121, 106, 126, 118, 102, // 1901-1909
122, 114, 99, 118, 110, 95, 115, 106, 126, 111, // 1910-1919
103, 122, 107, 99, 119, 110, 123, 115, 107, 126, // 1920-1929
111, 103, 123, 107, 99, 119, 104, 123, 115, 100, // 1930-1939
120, 111, 96, 116, 108, 127, 112, 104, 124, 115, // 1940-1949
100, 120, 112, 96, 116, 108, 128, 112, 104, 124, // 1950-1959
109, 100, 120, 105, 125, 116, 101, 121, 113, 104, // 1960-1969
117, 109, 101, 120, 105, 125, 117, 101, 121, 113, // 1970-1979
98, 117, 109, 129, 114, 105, 125, 110, 102, 121, // 1980-1989
106, 98, 118, 109, 122, 114, 106, 118, 110, 102, // 1990-1999
122, 106, 126, 118, 103, 122, 114, 99, 119, 110, // 2000-2009
95, 115, 107, 126, 111, 103, 123, 107, 99, 119, // 2010-2019
111, 123, 115, 107, 127, 111, 103, 123, 108, 99, // 2020-2029
119, 104, 124, 115, 100, 120, 112, 96, 116, 108, // 2030-2039
128, 112, 104, 124, 116, 100, 120, 112, 97, 116, // 2040-2049
108, 128, 113, 104, 124, 109, 101, 120, 105, 125, // 2050-2059
117, 101, 121, 113, 105, 117, 109, 101, 121, 105, // 2060-2069
125, 110, 102, 121, 113, 98, 118, 109, 129, 114, // 2070-2079
106, 125, 110, 102, 122, 106, 98, 118, 110, 122, // 2080-2089
114, 99, 119, 110, 102, 115, 107, 126, 118, 103, // 2090-2099
123, 115, 100, 120, 112, 96, 116, 108, 128, 112, // 2100-2109
104, 124, 109, 100, 120, 105, 125, 116, 108, 121, // 2110-2119
113, 104, 124, 109, 101, 120, 105, 125, 117, 101, // 2120-2129
121, 113, 98, 117, 109, 129, 114, 105, 125, 110, // 2130-2139
102, 121, 113, 98, 118, 109, 129, 114, 106, 125, // 2140-2149
110, 102, 122, 106, 126, 118, 103, 122, 114, 99, // 2150-2159
119, 110, 102, 115, 107, 126, 111, 103, 123, 114, // 2160-2169
99, 119, 111, 130, 115, 107, 127, 111, 103, 123, // 2170-2179
108, 99, 119, 104, 124, 115, 100, 120, 112, 103, // 2180-2189
116, 108, 128, 119, 104, 124, 116, 100, 120, 112, // 2190-2199
];

pub mod argentina;

pub mod austria;

pub mod australia;

pub mod botswana;

pub mod brazil;

pub mod canada;

pub mod chile;

pub mod china;

pub mod czechrepublic;

pub mod denmark;

pub mod finland;

pub mod france;

pub mod germany;

pub mod hongkong;

pub mod hungary;

pub mod iceland;

pub mod india;

pub mod indonesia;

pub mod israel;

pub mod italy;

pub mod japan;

pub mod jointcalendar;

pub mod mexico;

pub mod newzealand;

pub mod norway;

pub mod poland;

pub mod romania;

pub mod russia;

pub mod singapore;

pub mod slovakia;

pub mod southafrica;

pub mod southkorea;

pub mod sweden;

pub mod switzerland;

pub mod taiwan;

pub mod target;

pub mod thailand;

pub mod turkey;

pub mod ukraine;

pub mod unitedkingdom;

pub mod unitedstates;

pub mod weekendsonly;

pub trait Calendar: Debug {
fn naive_date_to_dkmy(&self, date: NaiveDate) -> (u32, Weekday, u32, i32, u32) {
(
date.day(),
date.weekday(),
date.month(),
date.year(),
date.ordinal(),
)
}

fn last_day_of_month(&self, date: NaiveDate) -> NaiveDate {
let year = date.year();
let month = date.month();
NaiveDate::from_ymd_opt(year, month + 1, 1)
.unwrap_or(NaiveDate::from_ymd_opt(year + 1, 1, 1).unwrap())
.pred_opt()
.unwrap()
}

fn end_of_month(&self, date: NaiveDate) -> NaiveDate {
let mut last_day_of_month = self.last_day_of_month(date);
while self.is_holiday(last_day_of_month) {
last_day_of_month -= Duration::days(1);
}
last_day_of_month
}

fn easter_monday(&self, year: i32) -> u32 {
EASTER_MONDAY[year as usize - 1901usize]
}

fn orthodox_easter_monday(&self, year: i32) -> u32 {
ORTHODOX_EASTER_MONDAY[year as usize - 1901usize]
}

fn is_weekend(&self, date: NaiveDate) -> bool {
let weekday = date.weekday();
matches!(weekday, Weekday::Sat | Weekday::Sun)
}

fn is_business_day(&self, date: NaiveDate) -> bool;

fn is_holiday(&self, date: NaiveDate) -> bool {
!self.is_business_day(date)
}

#[inline]
fn business_days_between(
&self,
from: NaiveDate,
to: NaiveDate,
include_first: Option<bool>,
include_last: Option<bool>,
) -> i64 {
if from > to {
return -self.business_days_between(to, from, include_first, include_last);
}
let include_first = include_first.unwrap_or(true);
let include_last = include_last.unwrap_or(false);
let mut day_count = 0;
let day_diff = ((to + Duration::days(i64::from(include_last)))
- (from + Duration::days(i64::from(include_first))))
.num_days();
for date in (from + Duration::days(i64::from(include_first)))
.iter_days()
.take(usize::try_from(day_diff).unwrap())
{
if self.is_business_day(date) {
day_count += 1;
}
}
day_count
}
}

#[cfg(test)]
mod tests {
use super::Calendar;
use crate::unitedkingdom::UnitedKingdom;
use chrono::NaiveDate;

#[test]
fn test_business_days_between() {
let calendar = UnitedKingdom::default();
let from = NaiveDate::from_ymd_opt(2023, 4, 1).unwrap();
let to = NaiveDate::from_ymd_opt(2023, 4, 30).unwrap();
assert_eq!(calendar.business_days_between(from, to, None, None), 18);
assert_eq!(calendar.business_days_between(to, from, None, None), -18);
}
}

0 comments on commit e5950e6

Please sign in to comment.