1use chrono::{NaiveDate, Datelike, Duration, Local};
2
3fn parse_us_date(s: &str) -> Option<NaiveDate> {
6 NaiveDate::parse_from_str(s.trim(), "%m/%d/%Y").ok()
7}
8
9pub fn eom<T: Into<DateInput>>(input: T) -> NaiveDate {
13 match input.into() {
14 DateInput::Date(date) => end_of_month(date),
15 DateInput::String(s) => {
16 parse_us_date(&s)
17 .map(end_of_month)
18 .unwrap_or_else(|| end_of_month(Local::now().naive_local().date()))
19 }
20 DateInput::None => end_of_month(Local::now().naive_local().date()),
21 }
22}
23
24fn end_of_month(date: NaiveDate) -> NaiveDate {
25 let year = date.year();
26 let month = date.month();
27 let first_day_next_month = if month == 12 {
28 NaiveDate::from_ymd_opt(year + 1, 1, 1)
29 } else {
30 NaiveDate::from_ymd_opt(year, month + 1, 1)
31 }
32 .expect("Invalid date computation");
33 first_day_next_month - Duration::days(1)
34}
35
36pub enum DateInput {
37 Date(NaiveDate),
38 String(String),
39 None,
40}
41
42impl From<&str> for DateInput {
43 fn from(s: &str) -> Self {
44 DateInput::String(s.to_string())
45 }
46}
47
48impl From<NaiveDate> for DateInput {
49 fn from(date: NaiveDate) -> Self {
50 DateInput::Date(date)
51 }
52}
53
54impl From<Option<NaiveDate>> for DateInput {
55 fn from(opt: Option<NaiveDate>) -> Self {
56 match opt {
57 Some(d) => DateInput::Date(d),
58 None => DateInput::None,
59 }
60 }
61}