Skip to main content

karbon_framework/util/
date.rs

1use chrono::{NaiveDate, NaiveDateTime, Utc};
2
3/// Date/time helpers
4pub struct DateHelper;
5
6impl DateHelper {
7    /// Human-readable "time ago" string (French)
8    pub fn time_ago(date: &NaiveDateTime) -> String {
9        let now = Utc::now().naive_utc();
10        let diff = now.signed_duration_since(*date);
11
12        let seconds = diff.num_seconds();
13        let minutes = diff.num_minutes();
14        let hours = diff.num_hours();
15        let days = diff.num_days();
16
17        if seconds < 0 {
18            return "Dans le futur".to_string();
19        }
20
21        if seconds < 60 {
22            "A l'instant".to_string()
23        } else if minutes < 60 {
24            format!("Il y a {} min", minutes)
25        } else if hours < 24 {
26            format!("Il y a {} h", hours)
27        } else if days < 30 {
28            format!("Il y a {} j", days)
29        } else if days < 365 {
30            let months = days / 30;
31            format!("Il y a {} mois", months)
32        } else {
33            let years = days / 365;
34            if years == 1 {
35                "Il y a 1 an".to_string()
36            } else {
37                format!("Il y a {} ans", years)
38            }
39        }
40    }
41
42    /// Format a NaiveDateTime with the given chrono format string
43    pub fn format(date: &NaiveDateTime, fmt: &str) -> String {
44        date.format(fmt).to_string()
45    }
46
47    /// Format as "dd/mm/YYYY"
48    pub fn format_fr(date: &NaiveDateTime) -> String {
49        date.format("%d/%m/%Y").to_string()
50    }
51
52    /// Format as "dd/mm/YYYY HH:MM"
53    pub fn format_fr_time(date: &NaiveDateTime) -> String {
54        date.format("%d/%m/%Y %H:%M").to_string()
55    }
56
57    /// Format as "YYYY-MM-DD"
58    pub fn format_iso(date: &NaiveDateTime) -> String {
59        date.format("%Y-%m-%d").to_string()
60    }
61
62    /// Format as "YYYY-MM-DD HH:MM:SS"
63    pub fn format_iso_time(date: &NaiveDateTime) -> String {
64        date.format("%Y-%m-%d %H:%M:%S").to_string()
65    }
66
67    /// Check if a datetime is in the past
68    pub fn is_past(date: &NaiveDateTime) -> bool {
69        *date < Utc::now().naive_utc()
70    }
71
72    /// Check if a datetime is in the future
73    pub fn is_future(date: &NaiveDateTime) -> bool {
74        *date > Utc::now().naive_utc()
75    }
76
77    /// Check if a date is today
78    pub fn is_today(date: &NaiveDate) -> bool {
79        *date == Utc::now().date_naive()
80    }
81
82    /// Difference in days between two dates
83    pub fn diff_in_days(from: &NaiveDateTime, to: &NaiveDateTime) -> i64 {
84        to.signed_duration_since(*from).num_days()
85    }
86
87    /// Difference in hours between two datetimes
88    pub fn diff_in_hours(from: &NaiveDateTime, to: &NaiveDateTime) -> i64 {
89        to.signed_duration_since(*from).num_hours()
90    }
91
92    /// Difference in minutes between two datetimes
93    pub fn diff_in_minutes(from: &NaiveDateTime, to: &NaiveDateTime) -> i64 {
94        to.signed_duration_since(*from).num_minutes()
95    }
96
97    /// Get the current UTC datetime
98    pub fn now() -> NaiveDateTime {
99        Utc::now().naive_utc()
100    }
101
102    /// Get today's date
103    pub fn today() -> NaiveDate {
104        Utc::now().date_naive()
105    }
106
107    /// Get the start of the day (00:00:00)
108    pub fn start_of_day(date: &NaiveDate) -> NaiveDateTime {
109        date.and_hms_opt(0, 0, 0).unwrap()
110    }
111
112    /// Get the end of the day (23:59:59)
113    pub fn end_of_day(date: &NaiveDate) -> NaiveDateTime {
114        date.and_hms_opt(23, 59, 59).unwrap()
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    fn make_dt(y: i32, m: u32, d: u32, h: u32, mi: u32, s: u32) -> NaiveDateTime {
123        NaiveDate::from_ymd_opt(y, m, d)
124            .unwrap()
125            .and_hms_opt(h, mi, s)
126            .unwrap()
127    }
128
129    #[test]
130    fn test_format_fr() {
131        let dt = make_dt(2024, 3, 15, 14, 30, 0);
132        assert_eq!(DateHelper::format_fr(&dt), "15/03/2024");
133    }
134
135    #[test]
136    fn test_format_fr_time() {
137        let dt = make_dt(2024, 3, 15, 14, 30, 0);
138        assert_eq!(DateHelper::format_fr_time(&dt), "15/03/2024 14:30");
139    }
140
141    #[test]
142    fn test_format_iso() {
143        let dt = make_dt(2024, 3, 15, 14, 30, 0);
144        assert_eq!(DateHelper::format_iso(&dt), "2024-03-15");
145    }
146
147    #[test]
148    fn test_format_iso_time() {
149        let dt = make_dt(2024, 3, 15, 14, 30, 0);
150        assert_eq!(DateHelper::format_iso_time(&dt), "2024-03-15 14:30:00");
151    }
152
153    #[test]
154    fn test_format_custom() {
155        let dt = make_dt(2024, 3, 15, 14, 30, 0);
156        assert_eq!(DateHelper::format(&dt, "%H:%M"), "14:30");
157    }
158
159    #[test]
160    fn test_is_past() {
161        let past = make_dt(2020, 1, 1, 0, 0, 0);
162        assert!(DateHelper::is_past(&past));
163    }
164
165    #[test]
166    fn test_is_future() {
167        let future = make_dt(2099, 1, 1, 0, 0, 0);
168        assert!(DateHelper::is_future(&future));
169    }
170
171    #[test]
172    fn test_diff_in_days() {
173        let from = make_dt(2024, 1, 1, 0, 0, 0);
174        let to = make_dt(2024, 1, 11, 0, 0, 0);
175        assert_eq!(DateHelper::diff_in_days(&from, &to), 10);
176    }
177
178    #[test]
179    fn test_diff_in_hours() {
180        let from = make_dt(2024, 1, 1, 0, 0, 0);
181        let to = make_dt(2024, 1, 1, 5, 0, 0);
182        assert_eq!(DateHelper::diff_in_hours(&from, &to), 5);
183    }
184
185    #[test]
186    fn test_diff_in_minutes() {
187        let from = make_dt(2024, 1, 1, 0, 0, 0);
188        let to = make_dt(2024, 1, 1, 1, 30, 0);
189        assert_eq!(DateHelper::diff_in_minutes(&from, &to), 90);
190    }
191
192    #[test]
193    fn test_start_of_day() {
194        let date = NaiveDate::from_ymd_opt(2024, 3, 15).unwrap();
195        let start = DateHelper::start_of_day(&date);
196        assert_eq!(start, make_dt(2024, 3, 15, 0, 0, 0));
197    }
198
199    #[test]
200    fn test_end_of_day() {
201        let date = NaiveDate::from_ymd_opt(2024, 3, 15).unwrap();
202        let end = DateHelper::end_of_day(&date);
203        assert_eq!(end, make_dt(2024, 3, 15, 23, 59, 59));
204    }
205
206    #[test]
207    fn test_time_ago_future() {
208        let future = make_dt(2099, 1, 1, 0, 0, 0);
209        assert_eq!(DateHelper::time_ago(&future), "Dans le futur");
210    }
211}