use super::civil::{day_of_week, days_to_ymd, ymd_to_days};
use super::config::CalendarConfig;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Weekday {
Monday = 0,
Tuesday = 1,
Wednesday = 2,
Thursday = 3,
Friday = 4,
Saturday = 5,
Sunday = 6,
}
impl Weekday {
pub fn from_u32(day: u32) -> Self {
match day % 7 {
0 => Weekday::Monday,
1 => Weekday::Tuesday,
2 => Weekday::Wednesday,
3 => Weekday::Thursday,
4 => Weekday::Friday,
5 => Weekday::Saturday,
6 => Weekday::Sunday,
_ => unreachable!(),
}
}
#[inline]
pub fn is_weekend(&self) -> bool {
matches!(self, Weekday::Saturday | Weekday::Sunday)
}
}
#[inline]
pub fn is_weekend(year: i32, month: u32, day: u32) -> bool {
let days = ymd_to_days(year, month, day);
let weekday = day_of_week(days);
Weekday::from_u32(weekday).is_weekend()
}
#[inline]
pub fn is_business_day(year: i32, month: u32, day: u32, config: &CalendarConfig) -> bool {
!is_weekend(year, month, day) && !config.is_holiday(year, month, day)
}
pub fn count_business_days(
from_year: i32,
from_month: u32,
from_day: u32,
to_year: i32,
to_month: u32,
to_day: u32,
config: &CalendarConfig,
) -> i64 {
let start_days = ymd_to_days(from_year, from_month, from_day);
let end_days = ymd_to_days(to_year, to_month, to_day);
if start_days > end_days {
return 0;
}
let mut count = 0i64;
for days in start_days..=end_days {
let (y, m, d) = days_to_ymd(days);
if is_business_day(y, m, d, config) {
count += 1;
}
}
count
}
pub fn add_business_days(
year: i32,
month: u32,
day: u32,
business_days: i64,
config: &CalendarConfig,
) -> (i32, u32, u32) {
if business_days == 0 {
return (year, month, day);
}
let mut current_days = ymd_to_days(year, month, day);
let mut remaining = business_days.abs();
let direction = if business_days >= 0 { 1 } else { -1 };
while remaining > 0 {
current_days += direction;
let (y, m, d) = days_to_ymd(current_days);
if is_business_day(y, m, d, config) {
remaining -= 1;
}
}
days_to_ymd(current_days)
}
pub fn next_business_day(
year: i32,
month: u32,
day: u32,
config: &CalendarConfig,
) -> (i32, u32, u32) {
let mut days = ymd_to_days(year, month, day) + 1;
loop {
let (y, m, d) = days_to_ymd(days);
if is_business_day(y, m, d, config) {
return (y, m, d);
}
days += 1;
}
}
pub fn prev_business_day(
year: i32,
month: u32,
day: u32,
config: &CalendarConfig,
) -> (i32, u32, u32) {
let mut days = ymd_to_days(year, month, day) - 1;
loop {
let (y, m, d) = days_to_ymd(days);
if is_business_day(y, m, d, config) {
return (y, m, d);
}
days -= 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_weekday_from_u32() {
assert_eq!(Weekday::from_u32(0), Weekday::Monday);
assert_eq!(Weekday::from_u32(6), Weekday::Sunday);
assert_eq!(Weekday::from_u32(7), Weekday::Monday); }
#[test]
fn test_weekday_is_weekend() {
assert!(!Weekday::Monday.is_weekend());
assert!(!Weekday::Friday.is_weekend());
assert!(Weekday::Saturday.is_weekend());
assert!(Weekday::Sunday.is_weekend());
}
#[test]
fn test_is_weekend() {
assert!(is_weekend(2024, 1, 6));
assert!(is_weekend(2024, 1, 7));
assert!(!is_weekend(2024, 1, 8));
}
#[test]
fn test_is_business_day() {
let config = CalendarConfig::new();
assert!(is_business_day(2024, 1, 8, &config));
assert!(!is_business_day(2024, 1, 6, &config));
let config = config.add_holiday(2024, 12, 25);
assert!(!is_business_day(2024, 12, 25, &config));
}
#[test]
fn test_count_business_days() {
let config = CalendarConfig::new();
let count = count_business_days(2024, 1, 8, 2024, 1, 12, &config);
assert_eq!(count, 5);
let count = count_business_days(2024, 1, 12, 2024, 1, 15, &config);
assert_eq!(count, 2);
}
#[test]
fn test_count_business_days_with_holiday() {
let config = CalendarConfig::new().add_holiday(2024, 12, 25);
let count = count_business_days(2024, 12, 23, 2024, 12, 27, &config);
assert_eq!(count, 4);
}
#[test]
fn test_add_business_days() {
let config = CalendarConfig::new();
let (y, m, d) = add_business_days(2024, 1, 8, 5, &config);
assert_eq!((y, m, d), (2024, 1, 15));
let (y, m, d) = add_business_days(2024, 1, 12, 1, &config);
assert_eq!((y, m, d), (2024, 1, 15));
}
#[test]
fn test_add_business_days_negative() {
let config = CalendarConfig::new();
let (y, m, d) = add_business_days(2024, 1, 15, -1, &config);
assert_eq!((y, m, d), (2024, 1, 12));
let (y, m, d) = add_business_days(2024, 1, 15, -5, &config);
assert_eq!((y, m, d), (2024, 1, 8));
}
#[test]
fn test_next_business_day() {
let config = CalendarConfig::new();
let (y, m, d) = next_business_day(2024, 1, 12, &config);
assert_eq!((y, m, d), (2024, 1, 15));
let (y, m, d) = next_business_day(2024, 1, 8, &config);
assert_eq!((y, m, d), (2024, 1, 9));
}
#[test]
fn test_prev_business_day() {
let config = CalendarConfig::new();
let (y, m, d) = prev_business_day(2024, 1, 15, &config);
assert_eq!((y, m, d), (2024, 1, 12));
let (y, m, d) = prev_business_day(2024, 1, 9, &config);
assert_eq!((y, m, d), (2024, 1, 8));
}
#[test]
fn test_next_business_day_with_holiday() {
let config = CalendarConfig::new().add_holiday(2024, 1, 15);
let (y, m, d) = next_business_day(2024, 1, 12, &config);
assert_eq!((y, m, d), (2024, 1, 16));
}
}