chrono_datepicker_core/
viewed_date.rs

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
13/// Trait used for the variable that describes the currently viewed datepicker.
14pub trait ViewedDate {
15    /// returns a date with the first day of the previous month
16    fn previous_month(&self) -> NaiveDate;
17
18    /// returns a date with the first day of the next month
19    fn next_month(&self) -> NaiveDate;
20
21    /// returns a date with the first day of the previous year
22    fn previous_year(&self) -> NaiveDate;
23
24    /// returns a date with the first day of the next year
25    fn next_year(&self) -> NaiveDate;
26
27    /// returns a date with the first day of the last year of the previous year group
28    fn previous_year_group(&self) -> NaiveDate;
29
30    /// returns a date with the first day of the first year of the next year group
31    fn next_year_group(&self) -> NaiveDate;
32
33    /// returns a date with the first day of the currently set month
34    fn first_day_of_month(&self) -> NaiveDate;
35
36    /// returns true if the currently `ViewedDate` with the given `DialogViewType` includes the given date
37    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, //
115        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, //
124        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, //
133        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, //
142        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, //
151        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, //
162        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, //
173        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, //
182        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, //
208        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, //
220        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, //
232        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}