Skip to main content

rok_utils/data/
dates.rs

1#[cfg(feature = "dates")]
2use chrono::{DateTime, Duration, NaiveDate, Utc};
3
4#[cfg(feature = "dates")]
5pub fn now() -> DateTime<Utc> {
6    Utc::now()
7}
8
9#[cfg(feature = "dates")]
10pub fn today() -> NaiveDate {
11    Utc::now().date_naive()
12}
13
14#[cfg(feature = "dates")]
15pub fn yesterday() -> NaiveDate {
16    (Utc::now() - Duration::days(1)).date_naive()
17}
18
19#[cfg(feature = "dates")]
20pub fn tomorrow() -> NaiveDate {
21    (Utc::now() + Duration::days(1)).date_naive()
22}
23
24#[cfg(feature = "dates")]
25pub fn format_date(dt: &DateTime<Utc>, fmt: &str) -> String {
26    dt.format(fmt).to_string()
27}
28
29#[cfg(feature = "dates")]
30pub fn parse_date(s: &str, fmt: &str) -> Result<DateTime<Utc>, crate::errors::RokError> {
31    NaiveDate::parse_from_str(s, fmt)
32        .map(|d| d.and_hms_opt(0, 0, 0).unwrap().and_utc())
33        .map_err(|e| crate::errors::RokError::InvalidDate(e.to_string()))
34}
35
36#[cfg(feature = "dates")]
37pub fn diff_days(a: &NaiveDate, b: &NaiveDate) -> i64 {
38    (*a - *b).num_days()
39}
40
41#[cfg(feature = "dates")]
42pub fn human_diff(dt: &DateTime<Utc>) -> String {
43    let now = Utc::now();
44    let diff = now.signed_duration_since(*dt);
45
46    if diff.num_seconds() < 0 {
47        let abs = -diff;
48        if abs.num_days() > 0 {
49            return format!("in {} days", abs.num_days());
50        }
51        if abs.num_hours() > 0 {
52            return format!("in {} hours", abs.num_hours());
53        }
54        if abs.num_minutes() > 0 {
55            return format!("in {} minutes", abs.num_minutes());
56        }
57        return "in a few seconds".to_string();
58    }
59
60    if diff.num_days() > 0 {
61        return format!("{} days ago", diff.num_days());
62    }
63    if diff.num_hours() > 0 {
64        return format!("{} hours ago", diff.num_hours());
65    }
66    if diff.num_minutes() > 0 {
67        return format!("{} minutes ago", diff.num_minutes());
68    }
69    "a few seconds ago".to_string()
70}
71
72#[cfg(feature = "dates")]
73pub fn add_days(dt: &NaiveDate, days: i64) -> NaiveDate {
74    *dt + Duration::days(days)
75}
76
77#[cfg(feature = "dates")]
78pub fn add_hours(dt: &DateTime<Utc>, hours: i64) -> DateTime<Utc> {
79    *dt + Duration::hours(hours)
80}
81
82#[cfg(feature = "dates")]
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use chrono::Datelike;
87
88    #[test]
89    fn test_now() {
90        let n = now();
91        assert!(n.timestamp() > 0);
92    }
93
94    #[test]
95    fn test_today() {
96        let t = today();
97        assert!(t.year() > 2020);
98    }
99
100    #[test]
101    fn test_diff_days() {
102        let a = NaiveDate::from_ymd_opt(2025, 1, 10).unwrap();
103        let b = NaiveDate::from_ymd_opt(2025, 1, 5).unwrap();
104        assert_eq!(diff_days(&a, &b), 5);
105    }
106
107    #[test]
108    fn test_add_days() {
109        let d = NaiveDate::from_ymd_opt(2025, 1, 1).unwrap();
110        let result = add_days(&d, 5);
111        assert_eq!(result.day(), 6);
112    }
113
114    #[test]
115    fn test_add_hours() {
116        let dt = Utc::now();
117        let result = add_hours(&dt, 24);
118        assert!(result > dt);
119    }
120}