Skip to main content

vault_tasks_core/
date.rs

1use std::{cmp::Ordering, fmt::Display};
2
3use chrono::{NaiveDate, NaiveDateTime, Timelike};
4
5#[derive(Debug, Hash, Eq, PartialEq, Clone)]
6/// This type accounts for the case where the task has a due date but no exact due time
7pub enum Date {
8    Day(NaiveDate),
9    DayTime(NaiveDateTime),
10}
11impl Display for Date {
12    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13        match self {
14            Self::Day(date) => write!(f, "{date}"),
15            Self::DayTime(date) => write!(f, "{date}"),
16        }
17    }
18}
19impl PartialOrd for Date {
20    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
21        match (self, other) {
22            (Date::Day(a), Date::Day(b)) => Some(a.cmp(b)),
23            (Date::Day(a), Date::DayTime(b)) => Some(a.cmp(&b.date())),
24            (Date::DayTime(a), Date::Day(b)) => Some(a.date().cmp(b)),
25            (Date::DayTime(a), Date::DayTime(b)) => Some(a.cmp(b)),
26        }
27    }
28}
29impl Date {
30    #[must_use]
31    pub fn to_string_format(&self, american_format: bool) -> String {
32        let format_date = if american_format {
33            "%Y-%m-%d"
34        } else {
35            "%d-%m-%Y"
36        };
37        let format_datetime = if american_format {
38            "%Y-%m-%d %T"
39        } else {
40            "%d-%m-%Y %T"
41        };
42
43        match self {
44            Self::Day(date) => date.format(format_date).to_string(),
45            Self::DayTime(date) => date.format(format_datetime).to_string(),
46        }
47    }
48
49    #[must_use]
50    pub fn get_relative_str(&self) -> String {
51        // This truncation prevents errors such as 23:59:59:999... instead of 24 hours
52        let now = chrono::Local::now()
53            .with_second(0)
54            .unwrap_or_default()
55            .with_nanosecond(0)
56            .unwrap_or_default();
57
58        let time_delta = match self {
59            Date::Day(naive_date) => now.date_naive().signed_duration_since(*naive_date),
60            Date::DayTime(naive_date_time) => {
61                now.date_naive()
62                    .signed_duration_since(naive_date_time.date())
63                    // same truncation here
64                    + now.time().signed_duration_since(
65                        naive_date_time
66                            .time()
67                            .with_second(0)
68                            .unwrap_or_default()
69                            .with_nanosecond(0)
70                            .unwrap_or_default()
71                        )
72            }
73        };
74        let (prefix, suffix) = match time_delta.num_seconds().cmp(&0) {
75            Ordering::Less => (String::from("in "), String::new()),
76            Ordering::Equal => (String::new(), String::new()),
77            Ordering::Greater => (String::new(), String::from(" ago")),
78        };
79
80        let time_delta_abs = time_delta.abs();
81
82        if time_delta_abs.is_zero() {
83            return String::from("today");
84        }
85        if time_delta.num_seconds() < 0 && time_delta_abs.num_hours() == 24 {
86            return String::from("tomorrow");
87        }
88        if time_delta.num_seconds() > 0 && time_delta_abs.num_hours() == 24 {
89            return String::from("yesterday");
90        }
91
92        // >= 13 months -> show years
93        let res = if 4 * 12 < time_delta_abs.num_weeks() {
94            format!("{} years", time_delta_abs.num_weeks() / (12 * 4))
95            // >= 5 weeks -> show months
96        } else if 5 <= time_delta_abs.num_weeks() {
97            format!("{} months", time_delta_abs.num_weeks() / 4)
98            // >= 2 weeks -> show weeks
99        } else if 2 <= time_delta_abs.num_weeks() {
100            format!("{} weeks", time_delta_abs.num_weeks())
101            // >= 2 days -> show days
102        } else if 2 <= time_delta_abs.num_days() {
103            format!("{} days", time_delta_abs.num_days())
104            // >= 2 hours -> show hours
105        } else if 2 <= time_delta_abs.num_hours() {
106            format!("{} hours", time_delta_abs.num_hours())
107        } else {
108            format!("{} minutes", time_delta_abs.num_minutes())
109        };
110        format!("{prefix}{res}{suffix}")
111    }
112}