doomsday/doomsday.rs
1use std::fmt;
2use crate::*;
3
4/// Behold the 💥 doomsday of a year
5///
6/// Every year has a single **doomsday**, for example `Doomsday(2020)` is
7/// `Saturday`. Additionally, each month has fixed day number for which
8/// doomsday lies, for example, in `April` doomsday is always on the 4th. Given
9/// these two peices of information, it's possible to quickly determain the day
10/// of any date.
11///
12/// Years before `FIRST_LEAP_YEAR` aren't supported.
13#[derive(Debug)]
14pub struct Doomsday(pub usize);
15
16impl Doomsday {
17 /// Create a new doomsday for a given year.
18 /// TODO: Random or something?
19 #[inline]
20 pub fn new(year: usize) -> Doomsday {
21 Doomsday(year)
22 }
23
24 /// Return the **day number**, of the anchor doomsday in a given month.
25 ///
26 /// This is the main utility of this crate. Notice how there are really
27 /// only a few mnemonics to memorize here:
28 ///
29 /// - 4/4, 6/6, 8/8, 10/10, and 12/12
30 /// - 9 to 5 at 7-Eleven
31 /// - The last day of February (twice)
32 /// - 3 for three years, then 4 on the forth.
33 ///
34 /// These tricks are listed again below for each appropriate month.
35 //
36 // TODO: Formatting function. Would almost be trivial, if it wasn't for the 3rd.
37 pub fn anchor(&self, month: Month) -> usize {
38 match month {
39 // 3 for three years, then 4 on the forth.
40 January => if is_leap(self.0) { 4 } else { 3 },
41 // Always the last day of the month.
42 February => if is_leap(self.0) { 29 } else { 28 },
43 // Also the last day of February, interestingly.
44 March => 0,
45 // 4/4
46 April => 4,
47 // 9 to 5 (reversed)
48 May => 9,
49 // 6/6
50 June => 6,
51 // 7-Eleven
52 July => 11,
53 // 8/8
54 August => 8,
55 // 9 to 5
56 September => 5,
57 // 10/10
58 October => 10,
59 // 7-Eleven (reversed)
60 November => 7,
61 // 12/12
62 December => 12,
63 }
64 }
65
66 /// Return the **day of the week** which doomsday lies on in a given
67 /// year.
68 pub fn day(&self) -> Day {
69 Day::from_anchor(self.0 + leaps(self.0))
70 }
71}
72
73/// The display of doomsday is day, one for every week if you wish.
74impl fmt::Display for Doomsday {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 write!(f, "{}", self.day())
77 }
78}
79
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn test_day() {
87 assert_eq!(Day::Tuesday, Doomsday(1752).day());
88 assert_eq!(Day::Sunday, Doomsday(1993).day());
89 assert_eq!(Day::Saturday, Doomsday(2020).day());
90 }
91
92 #[test]
93 fn test_anchor() {
94 assert_eq!(3, Doomsday(1993).anchor(Month::January));
95 assert_eq!(4, Doomsday(2020).anchor(Month::January));
96 assert_eq!(28, Doomsday(1993).anchor(Month::February));
97 assert_eq!(29, Doomsday(2020).anchor(Month::February));
98 assert_eq!(0, Doomsday(2000).anchor(Month::March));
99 assert_eq!(4, Doomsday(2020).anchor(Month::April));
100 assert_eq!(9, Doomsday(2020).anchor(Month::May));
101 assert_eq!(6, Doomsday(2020).anchor(Month::June));
102 assert_eq!(11, Doomsday(2020).anchor(Month::July));
103 assert_eq!(8, Doomsday(2020).anchor(Month::August));
104 assert_eq!(5, Doomsday(2020).anchor(Month::September));
105 assert_eq!(10, Doomsday(2020).anchor(Month::October));
106 assert_eq!(7, Doomsday(2020).anchor(Month::November));
107 assert_eq!(12, Doomsday(2020).anchor(Month::December));
108 }
109}