use chrono::{DateTime, Duration, Local, Utc};
#[cfg(test)]
use chrono::TimeZone;
pub fn format_relative_date(date: DateTime<Utc>) -> String {
let local_date = date.with_timezone(&Local).date_naive();
let today = Local::now().date_naive();
let tomorrow = today + Duration::days(1);
let yesterday = today - Duration::days(1);
if local_date == today {
"Today".to_string()
} else if local_date == tomorrow {
"Tomorrow".to_string()
} else if local_date == yesterday {
"Yesterday".to_string()
} else if local_date > today && local_date <= today + Duration::days(7) {
local_date.format("%a %d").to_string()
} else {
local_date.format("%b %d").to_string()
}
}
pub fn format_due_date(date: Option<DateTime<Utc>>) -> String {
match date {
Some(d) => format_relative_date(d),
None => String::new(),
}
}
pub fn is_same_day(a: DateTime<Utc>, b: DateTime<Utc>) -> bool {
let a_local = a.with_timezone(&Local).date_naive();
let b_local = b.with_timezone(&Local).date_naive();
a_local == b_local
}
pub fn is_today(date: DateTime<Utc>) -> bool {
let local_date = date.with_timezone(&Local).date_naive();
let today = Local::now().date_naive();
local_date == today
}
pub fn is_this_week(date: DateTime<Utc>) -> bool {
let now = Utc::now();
let week_from_now = now + Duration::days(7);
date >= now && date <= week_from_now
}
pub fn days_until(date: DateTime<Utc>) -> i64 {
let local_date = date.with_timezone(&Local).date_naive();
let today = Local::now().date_naive();
(local_date - today).num_days()
}
pub fn now() -> DateTime<Utc> {
Utc::now()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_relative_date_today() {
let today = Utc::now();
assert_eq!(format_relative_date(today), "Today");
}
#[test]
fn test_format_relative_date_tomorrow() {
let tomorrow = Utc::now() + Duration::days(1);
assert_eq!(format_relative_date(tomorrow), "Tomorrow");
}
#[test]
fn test_format_relative_date_yesterday() {
let yesterday = Utc::now() - Duration::days(1);
assert_eq!(format_relative_date(yesterday), "Yesterday");
}
#[test]
fn test_format_due_date_none() {
assert_eq!(format_due_date(None), "");
}
#[test]
fn test_format_due_date_some() {
let today = Utc::now();
assert_eq!(format_due_date(Some(today)), "Today");
}
#[test]
fn test_is_same_day() {
let base = Utc.with_ymd_and_hms(2024, 6, 15, 12, 0, 0).unwrap();
let same_day = base + Duration::hours(1);
let next_day = base + Duration::days(1);
assert!(is_same_day(base, same_day));
assert!(!is_same_day(base, next_day));
}
#[test]
fn test_is_today() {
let now = Utc::now();
let tomorrow = now + Duration::days(1);
assert!(is_today(now));
assert!(!is_today(tomorrow));
}
#[test]
fn test_is_this_week() {
let now = Utc::now();
let in_3_days = now + Duration::days(3);
let in_10_days = now + Duration::days(10);
let yesterday = now - Duration::days(1);
assert!(is_this_week(in_3_days));
assert!(!is_this_week(in_10_days));
assert!(!is_this_week(yesterday));
}
#[test]
fn test_days_until_positive() {
let future = Utc::now() + Duration::days(5);
let days = days_until(future);
assert!(days >= 4 && days <= 6);
}
#[test]
fn test_days_until_negative() {
let past = Utc::now() - Duration::days(3);
let days = days_until(past);
assert!(days >= -4 && days <= -2);
}
#[test]
fn test_days_until_today() {
let today = Utc::now();
let days = days_until(today);
assert_eq!(days, 0);
}
}