systemprompt_cli/commands/infrastructure/logs/
duration.rs1use anyhow::{anyhow, Result};
2use chrono::{DateTime, Duration, Utc};
3
4pub fn parse_duration(s: &str) -> Result<Duration> {
5 let s = s.trim().to_lowercase();
6
7 if let Some(days) = s.strip_suffix('d') {
8 let num: i64 = days
9 .parse()
10 .map_err(|_| anyhow!("Invalid duration: {}", s))?;
11 return Ok(Duration::days(num));
12 }
13
14 if let Some(hours) = s.strip_suffix('h') {
15 let num: i64 = hours
16 .parse()
17 .map_err(|_| anyhow!("Invalid duration: {}", s))?;
18 return Ok(Duration::hours(num));
19 }
20
21 if let Some(mins) = s.strip_suffix('m') {
22 let num: i64 = mins
23 .parse()
24 .map_err(|_| anyhow!("Invalid duration: {}", s))?;
25 return Ok(Duration::minutes(num));
26 }
27
28 if let Ok(num) = s.parse::<i64>() {
29 return Ok(Duration::days(num));
30 }
31
32 Err(anyhow!(
33 "Invalid duration format: {}. Use formats like '7d', '24h', '30m'",
34 s
35 ))
36}
37
38pub fn parse_since(since: Option<&String>) -> Result<Option<DateTime<Utc>>> {
39 let Some(s) = since else {
40 return Ok(None);
41 };
42
43 let s = s.trim().to_lowercase();
44
45 if let Ok(duration) = parse_duration(&s) {
46 return Ok(Some(Utc::now() - duration));
47 }
48
49 if let Ok(date) = chrono::NaiveDate::parse_from_str(&s, "%Y-%m-%d") {
50 let datetime = date
51 .and_hms_opt(0, 0, 0)
52 .ok_or_else(|| anyhow!("Invalid date: {}", s))?;
53 return Ok(Some(DateTime::from_naive_utc_and_offset(datetime, Utc)));
54 }
55
56 if let Ok(datetime) = chrono::NaiveDateTime::parse_from_str(&s, "%Y-%m-%dT%H:%M:%S") {
57 return Ok(Some(DateTime::from_naive_utc_and_offset(datetime, Utc)));
58 }
59
60 Err(anyhow!(
61 "Invalid --since format: {}. Use formats like '1h', '24h', '7d', '2026-01-13', or \
62 '2026-01-13T10:00:00'",
63 s
64 ))
65}