ralph/productivity/date_utils.rs
1//! Date key utilities for productivity tracking.
2//!
3//! Responsibilities:
4//! - Parse and format date keys (YYYY-MM-DD format).
5//! - Calculate date offsets and previous/next days.
6//!
7//! Not handled here:
8//! - Persistence or business logic (see other modules).
9//!
10//! Invariants/assumptions:
11//! - Date keys are always in YYYY-MM-DD format.
12//! - Uses the `time` crate for calendar math (handles leap years, month boundaries).
13
14use time::macros::format_description;
15use time::{Date, Duration};
16
17/// Parse a date key (YYYY-MM-DD) into a `time::Date`.
18pub fn parse_date_key(date_key: &str) -> Option<Date> {
19 let trimmed = date_key.trim();
20 if trimmed.is_empty() {
21 return None;
22 }
23 Date::parse(trimmed, &format_description!("[year]-[month]-[day]")).ok()
24}
25
26/// Format a `time::Date` as a date key (YYYY-MM-DD).
27pub fn format_date_key(date: Date) -> String {
28 format!(
29 "{:04}-{:02}-{:02}",
30 date.year(),
31 u8::from(date.month()),
32 date.day()
33 )
34}
35
36/// Return `date_key` offset by `delta_days`.
37///
38/// `delta_days = -1` means previous day.
39pub fn date_key_add_days(date_key: &str, delta_days: i64) -> Option<String> {
40 let date = parse_date_key(date_key)?;
41 let date = date.checked_add(Duration::days(delta_days))?;
42 Some(format_date_key(date))
43}
44
45/// Return the previous day's date key.
46pub fn previous_date_key(date_key: &str) -> Option<String> {
47 date_key_add_days(date_key, -1)
48}