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}