calenda_rs/
frequency.rs

1// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2// calenda-rs: A Rust library for global calendars.
3// Copyright (C) 2024 https://github.com/avhz
4//
5// Dual licensed under Apache 2.0 and MIT.
6//
7// See:
8//      - LICENSE-APACHE.md
9//      - LICENSE-MIT.md
10// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11
12use time::{Date, Duration};
13
14use crate::constants::{
15    ANNUALLY, BI_WEEKLY, DAILY, MONTHLY, QUARTERLY, SEMI_ANNUALLY, SEMI_MONTHLY, SEMI_QUARTERLY,
16    TRI_ANNUALLY, WEEKLY,
17};
18
19/// Interest/coupon frequency per year.
20/// This is important in finance, as it determines the number of times
21/// a cash flow is paid in a year, and thus affects the present value
22/// of the cash flows.
23#[derive(Debug, Clone, Copy)]
24pub enum Frequency {
25    /// Daily (252 per year).
26    Daily = DAILY,
27
28    /// Weekly (52 per year).
29    Weekly = WEEKLY,
30
31    /// Bi-weekly (26 per year).
32    BiWeekly = BI_WEEKLY,
33
34    /// Semi-monthly (24 per year).
35    SemiMonthly = SEMI_MONTHLY,
36
37    /// Monthly (12 per year).
38    Monthly = MONTHLY,
39
40    /// Semi-quarterly (8 per year).
41    SemiQuarterly = SEMI_QUARTERLY,
42
43    /// Quarterly.
44    Quarterly = QUARTERLY,
45
46    /// Tri-annually.
47    TriAnnually = TRI_ANNUALLY,
48
49    /// Semi-annually.
50    SemiAnnually = SEMI_ANNUALLY,
51
52    /// Annually.
53    Annually = ANNUALLY,
54}
55
56impl Frequency {
57    /// Function to infer the frequency between two `Date`s.
58    ///
59    /// This is a very simple (fallible) way to infer the frequency between two dates.
60    ///
61    /// # Panics
62    ///
63    /// Panics if the difference between the two dates is not a recognized frequency.
64    pub fn infer_frequency(start: Date, end: Date) -> Frequency {
65        let diff = end - start;
66
67        if diff == Duration::days(1) {
68            Frequency::Daily
69        } else if diff == Duration::weeks(1) {
70            Frequency::Weekly
71        } else if diff == Duration::weeks(2) {
72            Frequency::BiWeekly
73        } else if diff > Duration::days(14) && diff < Duration::days(16) {
74            Frequency::SemiMonthly
75        } else if diff >= Duration::days(28) && diff <= Duration::days(31) {
76            Frequency::Monthly
77        } else if diff >= Duration::days(45) && diff <= Duration::days(46) {
78            Frequency::SemiQuarterly
79        } else if diff >= Duration::days(91) && diff <= Duration::days(92) {
80            Frequency::Quarterly
81        } else if diff >= Duration::days(121) && diff <= Duration::days(122) {
82            Frequency::TriAnnually
83        } else if diff >= Duration::days(182) && diff <= Duration::days(183) {
84            Frequency::SemiAnnually
85        } else if diff >= Duration::days(365) && diff <= Duration::days(366) {
86            Frequency::Annually
87        } else {
88            panic!("Unable to infer frequency between the two dates.")
89        }
90    }
91
92    /// Get the number of times the frequency occurs in a year.
93    pub fn times_in_year(&self) -> isize {
94        match self {
95            Frequency::Daily => DAILY,
96            Frequency::Weekly => WEEKLY,
97            Frequency::BiWeekly => BI_WEEKLY,
98            Frequency::SemiMonthly => SEMI_MONTHLY,
99            Frequency::Monthly => MONTHLY,
100            Frequency::SemiQuarterly => SEMI_QUARTERLY,
101            Frequency::Quarterly => QUARTERLY,
102            Frequency::TriAnnually => TRI_ANNUALLY,
103            Frequency::SemiAnnually => SEMI_ANNUALLY,
104            Frequency::Annually => ANNUALLY,
105        }
106    }
107}