use std::time::Duration;
pub fn parse_duration(s: &str) -> Option<Duration> {
let s = s.trim();
if s.is_empty() {
return None;
}
let (num_str, multiplier) = if let Some(n) = s.strip_suffix('w') {
(n, 7 * 24 * 3600)
} else if let Some(n) = s.strip_suffix('d') {
(n, 24 * 3600)
} else if let Some(n) = s.strip_suffix('h') {
(n, 3600)
} else if let Some(n) = s.strip_suffix('m') {
(n, 60)
} else if let Some(n) = s.strip_suffix('s') {
(n, 1)
} else {
return None;
};
let num: u64 = num_str.trim().parse().ok()?;
Some(Duration::from_secs(num * multiplier))
}
pub fn since_to_cutoff_ms(duration: Duration) -> i64 {
let now_ms = chrono::Utc::now().timestamp_millis();
now_ms - duration.as_millis() as i64
}
#[cfg(test)]
#[allow(non_snake_case)]
mod tests {
use super::*;
#[test]
fn parse_duration__seconds() {
assert_eq!(parse_duration("30s"), Some(Duration::from_secs(30)));
}
#[test]
fn parse_duration__minutes() {
assert_eq!(parse_duration("15m"), Some(Duration::from_secs(15 * 60)));
}
#[test]
fn parse_duration__hours() {
assert_eq!(parse_duration("2h"), Some(Duration::from_secs(2 * 3600)));
}
#[test]
fn parse_duration__days() {
assert_eq!(parse_duration("7d"), Some(Duration::from_secs(7 * 86400)));
}
#[test]
fn parse_duration__weeks() {
assert_eq!(
parse_duration("2w"),
Some(Duration::from_secs(2 * 7 * 86400))
);
}
#[test]
fn parse_duration__invalid_suffix() {
assert_eq!(parse_duration("10x"), None);
}
#[test]
fn parse_duration__invalid_number() {
assert_eq!(parse_duration("abch"), None);
}
#[test]
fn parse_duration__empty_string() {
assert_eq!(parse_duration(""), None);
}
#[test]
fn parse_duration__whitespace_handling() {
assert_eq!(parse_duration(" 5m "), Some(Duration::from_secs(5 * 60)));
}
}