1use std::{cmp::Ordering, fmt::Display};
2
3use chrono::{NaiveDate, NaiveDateTime, Timelike};
4
5#[derive(Debug, Hash, Eq, PartialEq, Clone)]
6pub 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 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 + 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 let res = if 4 * 12 < time_delta_abs.num_weeks() {
94 format!("{} years", time_delta_abs.num_weeks() / (12 * 4))
95 } else if 5 <= time_delta_abs.num_weeks() {
97 format!("{} months", time_delta_abs.num_weeks() / 4)
98 } else if 2 <= time_delta_abs.num_weeks() {
100 format!("{} weeks", time_delta_abs.num_weeks())
101 } else if 2 <= time_delta_abs.num_days() {
103 format!("{} days", time_delta_abs.num_days())
104 } 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}