pub use std::time::Duration;
use eyre::{Result, bail};
use jiff::{Span, Timestamp, Zoned, civil::date};
pub const HOURLY: Duration = Duration::from_secs(60 * 60);
pub const DAILY: Duration = Duration::from_secs(60 * 60 * 24);
pub const WEEKLY: Duration = Duration::from_secs(60 * 60 * 24 * 7);
pub fn parse_duration(s: &str) -> Result<Duration> {
match s.parse::<Span>() {
Ok(span) => {
let duration = span.to_duration(date(2025, 1, 1))?;
if duration.is_negative() {
bail!("duration must not be negative: {}", s);
}
Ok(duration.unsigned_abs())
}
Err(_) => Ok(Duration::from_secs(s.parse()?)),
}
}
pub fn parse_into_timestamp(s: &str) -> Result<Timestamp> {
if let Ok(ts) = s.parse::<Timestamp>() {
return Ok(ts);
}
if let Ok(zoned) = s.parse::<Zoned>() {
return Ok(zoned.timestamp());
}
if let Ok(civil_date) = s.parse::<jiff::civil::Date>() {
let datetime = civil_date.at(23, 59, 59, 0);
let ts = datetime.to_zoned(jiff::tz::TimeZone::UTC)?.timestamp();
return Ok(ts);
}
if let Ok(span) = s.parse::<Span>() {
let duration = span.to_duration(date(2025, 1, 1))?;
if duration.is_negative() {
bail!("duration must not be negative: {}", s);
}
let now = Timestamp::now();
let now_zoned = now.to_zoned(jiff::tz::TimeZone::UTC);
let past = now_zoned.checked_sub(span)?;
return Ok(past.timestamp());
}
bail!(
"Invalid date or duration: {s}. Expected formats: '2024-06-01', '2024-06-01T12:00:00Z', '90d', '1y'"
)
}