use super::{next_business_day, previous_business_day};
use crate::calendar::Calendar;
use std::fmt;
use time::Date;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DateRollingConvention {
Actual,
Following,
ModifiedFollowing,
Preceding,
ModifiedPreceding,
ModifiedRolling,
}
pub trait DateRoller {
fn roll_date(&self, date: Date, convention: &DateRollingConvention) -> Date;
fn roll_dates(&self, dates: &[Date], convention: &DateRollingConvention) -> Vec<Date>;
}
impl<C> DateRoller for C
where
C: Calendar,
{
#[rustfmt::skip]
fn roll_date(&self, date: Date, convention: &DateRollingConvention) -> Date {
match convention {
DateRollingConvention::Actual => DateRollingConvention::roll_date_actual(date, self),
DateRollingConvention::Following => DateRollingConvention::roll_date_following(date, self),
DateRollingConvention::ModifiedFollowing => DateRollingConvention::roll_date_modified_following(date, self),
DateRollingConvention::Preceding => DateRollingConvention::roll_date_preceding(date, self),
DateRollingConvention::ModifiedPreceding => DateRollingConvention::roll_date_modified_preceding(date, self),
DateRollingConvention::ModifiedRolling => DateRollingConvention::roll_date_modified_rolling(date, self),
}
}
fn roll_dates(&self, dates: &[Date], convention: &DateRollingConvention) -> Vec<Date> {
dates
.iter()
.map(|&date| self.roll_date(date, convention))
.collect()
}
}
impl Default for DateRollingConvention {
fn default() -> Self {
DateRollingConvention::Actual
}
}
impl fmt::Display for DateRollingConvention {
#[rustfmt::skip]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Actual => write!(f, "Actual"),
Self::Following => write!(f, "Following"),
Self::ModifiedFollowing => write!(f, "Modified Following"),
Self::Preceding => write!(f, "Preceding"),
Self::ModifiedPreceding => write!(f, "Modified Preceding"),
Self::ModifiedRolling => write!(f, "Modified Rolling"),
}
}
}
impl DateRollingConvention {
pub(crate) fn roll_date_actual<C: Calendar>(date: Date, _calendar: &C) -> Date {
date
}
pub(crate) fn roll_date_following<C: Calendar>(date: Date, calendar: &C) -> Date {
next_business_day(date, calendar)
}
pub(crate) fn roll_date_modified_following<C: Calendar>(date: Date, calendar: &C) -> Date {
let mut new_date = next_business_day(date, calendar);
if new_date.month() != date.month() {
new_date = previous_business_day(date, calendar);
}
new_date
}
pub(crate) fn roll_date_modified_preceding<C: Calendar>(date: Date, calendar: &C) -> Date {
let mut new_date = previous_business_day(date, calendar);
if new_date.month() != date.month() {
new_date = next_business_day(date, calendar);
}
new_date
}
pub(crate) fn roll_date_modified_rolling<C: Calendar>(date: Date, calendar: &C) -> Date {
let mut new_date = date;
while !calendar.is_business_day(new_date) {
new_date = new_date.next_day().unwrap();
}
new_date
}
pub(crate) fn roll_date_preceding<C: Calendar>(date: Date, calendar: &C) -> Date {
previous_business_day(date, calendar)
}
}