1use std::ops::RangeInclusive;
2
3use chrono::{Datelike, NaiveDate};
4
5use crate::dialog_view_type::DialogViewType;
6
7pub const YEARS_IN_YEAR_SELECTION: i32 = 20;
8
9pub type YearNumber = i32;
10pub type MonthNumber = u32;
11pub type DayNumber = u32;
12
13pub trait ViewedDate {
15 fn previous_month(&self) -> NaiveDate;
17
18 fn next_month(&self) -> NaiveDate;
20
21 fn previous_year(&self) -> NaiveDate;
23
24 fn next_year(&self) -> NaiveDate;
26
27 fn previous_year_group(&self) -> NaiveDate;
29
30 fn next_year_group(&self) -> NaiveDate;
32
33 fn first_day_of_month(&self) -> NaiveDate;
35
36 fn contains(&self, dialog_view_type: &DialogViewType, date: &NaiveDate) -> bool;
38}
39
40impl ViewedDate for NaiveDate {
41 fn previous_month(&self) -> NaiveDate {
42 let mut year = self.year();
43 let mut month = self.month();
44 if month == 1 {
45 month = 12;
46 year -= 1;
47 } else {
48 month -= 1;
49 }
50 NaiveDate::from_ymd(year, month, 1)
51 }
52
53 fn next_month(&self) -> NaiveDate {
54 let mut year = self.year();
55 let mut month = self.month();
56 if month == 12 {
57 month = 1;
58 year += 1;
59 } else {
60 month += 1;
61 }
62 NaiveDate::from_ymd(year, month, 1)
63 }
64
65 fn previous_year(&self) -> NaiveDate {
66 NaiveDate::from_ymd(self.year() - 1, 1, 1)
67 }
68
69 fn next_year(&self) -> NaiveDate {
70 NaiveDate::from_ymd(self.year() + 1, 1, 1)
71 }
72
73 fn previous_year_group(&self) -> NaiveDate {
74 NaiveDate::from_ymd(year_group_start(self.year()) - 1, 1, 1)
75 }
76
77 fn next_year_group(&self) -> NaiveDate {
78 NaiveDate::from_ymd(year_group_end(self.year()) + 1, 1, 1)
79 }
80
81 fn first_day_of_month(&self) -> NaiveDate {
82 NaiveDate::from_ymd(self.year(), self.month(), 1)
83 }
84
85 fn contains(&self, dialog_view_type: &DialogViewType, date: &NaiveDate) -> bool {
86 match dialog_view_type {
87 DialogViewType::Years => self.year() == date.year(),
88 DialogViewType::Months => self.year() == date.year() && self.month() == date.month(),
89 DialogViewType::Days => self == date,
90 }
91 }
92}
93
94pub fn year_group_start(year: YearNumber) -> YearNumber {
95 year - (year % YEARS_IN_YEAR_SELECTION)
96}
97
98pub fn year_group_end(year: YearNumber) -> YearNumber {
99 year_group_start(year) + (YEARS_IN_YEAR_SELECTION - 1)
100}
101
102pub fn year_group_range(year: YearNumber) -> RangeInclusive<YearNumber> {
103 year_group_start(year)..=year_group_end(year)
104}
105
106#[cfg(test)]
107mod tests {
108 use crate::rstest_utils::create_date;
109 use rstest::*;
110
111 use super::*;
112
113 #[rstest(
114 expected, given, case::from_january(create_date(1989, 12, 1), create_date(1990, 1, 15)),
116 case::not_from_january(create_date(1990, 2, 1), create_date(1990, 3, 22)),
117 )]
118 fn previous_month(expected: NaiveDate, given: NaiveDate) {
119 assert_eq!(expected, given.previous_month());
120 }
121
122 #[rstest(
123 expected, given, case::from_december(create_date(1991, 1, 1), create_date(1990, 12, 22)),
125 case::not_from_december(create_date(1990, 4, 1), create_date(1990, 3, 15)),
126 )]
127 fn next_month(expected: NaiveDate, given: NaiveDate) {
128 assert_eq!(expected, given.next_month());
129 }
130
131 #[rstest(
132 expected, given, case(create_date(1989, 1, 1), create_date(1990, 12, 25)),
134 case(create_date(1990, 1, 1), create_date(1991, 3, 22)),
135 )]
136 fn previous_year(expected: NaiveDate, given: NaiveDate) {
137 assert_eq!(expected, given.previous_year());
138 }
139
140 #[rstest(
141 expected, given, case(create_date(1991, 1, 1), create_date(1990, 12, 25)),
143 case(create_date(1992, 1, 1), create_date(1991, 3, 22)),
144 )]
145 fn next_year(expected: NaiveDate, given: NaiveDate) {
146 assert_eq!(expected, given.next_year());
147 }
148
149 #[rstest(
150 expected, given, case::in_middle(create_date(1979, 1, 1), create_date(1990, 1, 1)),
152 case::at_start(create_date(1979, 1, 1), create_date(1980, 3, 20)),
153 case::at_end(create_date(1979, 1, 1), create_date(1999, 7, 24)),
154 case::next_group(create_date(1999, 1, 1), create_date(2000, 8, 22)),
155 )]
156 fn previous_year_group(expected: NaiveDate, given: NaiveDate) {
157 assert_eq!(expected, given.previous_year_group());
158 }
159
160 #[rstest(
161 expected, given, case::in_middle(create_date(2000, 1, 1), create_date(1990, 1, 1)),
163 case::at_start(create_date(2000, 1, 1), create_date(1980, 3, 20)),
164 case::at_end(create_date(2000, 1, 1), create_date(1999, 7, 24)),
165 case::next_group(create_date(2020, 1, 1), create_date(2000, 8, 22)),
166 )]
167 fn next_year_group(expected: NaiveDate, given: NaiveDate) {
168 assert_eq!(expected, given.next_year_group());
169 }
170
171 #[rstest(
172 expected, given, case(create_date(1990, 12, 1), create_date(1990, 12, 15)),
174 case(create_date(1991, 3, 1), create_date(1991, 3, 24)),
175 )]
176 fn first_day_of_month(expected: NaiveDate, given: NaiveDate) {
177 assert_eq!(expected, given.first_day_of_month());
178 }
179
180 #[rstest(
181 expected, viewed_date, dialog_view_type, tested_date, case::years_different(false, create_date(1990, 1, 1), DialogViewType::Years, create_date(1989, 1, 1)),
183 case::years_equal(true, create_date(1990, 1, 1), DialogViewType::Years, create_date(1990, 5, 15)),
184
185 case::months_different_year(false, create_date(1990, 3, 1), DialogViewType::Months, create_date(1989, 3, 1)),
186 case::months_different_month(false, create_date(1990, 3, 1), DialogViewType::Months, create_date(1990, 4, 1)),
187 case::months_equal(true, create_date(1990, 3, 1), DialogViewType::Months, create_date(1990, 3, 15)),
188
189 case::days_different_year(false, create_date(1990, 3, 1), DialogViewType::Days, create_date(1989, 3, 1)),
190 case::days_different_month(false, create_date(1990, 3, 1), DialogViewType::Days, create_date(1990, 4, 1)),
191 case::days_different_day(false, create_date(1990, 3, 1), DialogViewType::Days, create_date(1990, 3, 15)),
192 case::months_equal(true, create_date(1990, 3, 1), DialogViewType::Months, create_date(1990, 3, 15)),
193 )]
194 fn contains(
195 expected: bool,
196 viewed_date: NaiveDate,
197 dialog_view_type: DialogViewType,
198 tested_date: NaiveDate,
199 ) {
200 assert_eq!(
201 expected,
202 viewed_date.contains(&dialog_view_type, &tested_date)
203 );
204 }
205
206 #[rstest(
207 expected, input, case::at_zero(0, 0),
209 case::in_middle(1980, 1990),
210 case::at_start(1980, 1980),
211 case::at_end(1980, 1999),
212 case::after_end(2000, 2000)
213 )]
214 fn test_year_group_start(expected: YearNumber, input: YearNumber) {
215 assert_eq!(expected, year_group_start(input));
216 }
217
218 #[rstest(
219 expected, input, case::at_zero(19, 0),
221 case::in_middle(1999, 1990),
222 case::at_start(1999, 1980),
223 case::at_end(1999, 1999),
224 case::after_end(2019, 2000)
225 )]
226 fn test_year_group_end(expected: YearNumber, input: YearNumber) {
227 assert_eq!(expected, year_group_end(input));
228 }
229
230 #[rstest(
231 expected, input, case::at_zero(0..=19, 0),
233 case::in_middle(1980..=1999, 1990),
234 case::at_start(1980..=1999, 1980),
235 case::at_end(1980..=1999, 1999),
236 case::after_end(2000..=2019, 2000)
237 )]
238 fn test_year_group_range(expected: RangeInclusive<YearNumber>, input: YearNumber) {
239 assert_eq!(expected, year_group_range(input));
240 }
241}