Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/commands/audit_logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ pub async fn list(cfg: &Config, from: String, to: String, limit: i32) -> Result<
None => AuditAPI::with_config(dd_cfg),
};

let from_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&from)?).unwrap();
let to_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&to)?).unwrap();
let from_dt = util::parse_time_to_datetime(&from)?;
let to_dt = util::parse_time_to_datetime(&to)?;

let params = ListAuditLogsOptionalParams::default()
.filter_from(from_dt)
Expand Down
6 changes: 2 additions & 4 deletions src/commands/cicd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,8 @@ pub async fn tests_list(
None => CIVisibilityTestsAPI::with_config(dd_cfg),
};

let from_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&from)?).unwrap();
let to_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&to)?).unwrap();
let from_dt = util::parse_time_to_datetime(&from)?;
let to_dt = util::parse_time_to_datetime(&to)?;

let mut params = ListCIAppTestEventsOptionalParams::default()
.filter_from(from_dt)
Expand Down
10 changes: 3 additions & 7 deletions src/commands/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,11 @@ pub async fn projected(cfg: &Config) -> Result<()> {
pub async fn by_org(cfg: &Config, start_month: String, end_month: Option<String>) -> Result<()> {
let api = make_usage_api(cfg);

let start_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&start_month)?)
.unwrap();
let start_dt = util::parse_time_to_datetime(&start_month)?;

let mut params = GetCostByOrgOptionalParams::default();
if let Some(e) = end_month {
let end_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&e)?).unwrap();
let end_dt = util::parse_time_to_datetime(&e)?;
params = params.end_month(end_dt);
}

Expand All @@ -51,8 +48,7 @@ pub async fn by_org(cfg: &Config, start_month: String, end_month: Option<String>
pub async fn attribution(cfg: &Config, start: String, fields: Option<String>) -> Result<()> {
let api = make_usage_api(cfg);

let start_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&start)?).unwrap();
let start_dt = util::parse_time_to_datetime(&start)?;

let fields_str = fields.unwrap_or_else(|| "*".to_string());
let params = GetMonthlyCostAttributionOptionalParams::default();
Expand Down
6 changes: 2 additions & 4 deletions src/commands/rum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,8 @@ pub async fn events_list(
None => RUMAPI::with_config(dd_cfg),
};

let from_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&from)?).unwrap();
let to_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&to)?).unwrap();
let from_dt = util::parse_time_to_datetime(&from)?;
let to_dt = util::parse_time_to_datetime(&to)?;

let mut params = ListRUMEventsOptionalParams::default()
.filter_from(from_dt)
Expand Down
6 changes: 2 additions & 4 deletions src/commands/security.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,8 @@ pub async fn signals_search(
None => SecurityMonitoringAPI::with_config(dd_cfg),
};

let from_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&from)?).unwrap();
let to_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&to)?).unwrap();
let from_dt = util::parse_time_to_datetime(&from)?;
let to_dt = util::parse_time_to_datetime(&to)?;

let body = SecurityMonitoringSignalListRequest::new()
.filter(
Expand Down
12 changes: 4 additions & 8 deletions src/commands/usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@ pub async fn summary(cfg: &Config, start: String, end: Option<String>) -> Result
None => UsageMeteringAPI::with_config(dd_cfg),
};

let start_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&start)?).unwrap();
let start_dt = util::parse_time_to_datetime(&start)?;

let mut params = GetUsageSummaryOptionalParams::default();
if let Some(e) = end {
let end_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&e)?).unwrap();
let end_dt = util::parse_time_to_datetime(&e)?;
params = params.end_month(end_dt);
}

Expand All @@ -40,13 +38,11 @@ pub async fn hourly(cfg: &Config, start: String, end: Option<String>) -> Result<
None => UsageMeteringAPI::with_config(dd_cfg),
};

let start_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&start)?).unwrap();
let start_dt = util::parse_time_to_datetime(&start)?;

let mut params = GetHourlyUsageAttributionOptionalParams::default();
if let Some(e) = end {
let end_dt =
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&e)?).unwrap();
let end_dt = util::parse_time_to_datetime(&e)?;
params = params.end_hr(end_dt);
}

Expand Down
47 changes: 47 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ pub fn parse_time_to_unix(input: &str) -> Result<i64> {
Ok(parse_time_to_unix_millis(input)? / 1000)
}

/// Parses a time string into a `chrono::DateTime<Utc>`.
///
/// Returns an `anyhow` error if the input cannot be parsed or if the resulting
/// timestamp falls outside chrono's representable range.
pub fn parse_time_to_datetime(input: &str) -> Result<chrono::DateTime<Utc>> {
let ms = parse_time_to_unix_millis(input)?;
chrono::DateTime::from_timestamp_millis(ms)
.ok_or_else(|| anyhow::anyhow!("timestamp out of valid range: {input:?}"))
}

/// Parses a human-readable duration string into milliseconds.
///
/// Unlike `parse_time_to_unix_millis`, this does **not** subtract from the
Expand Down Expand Up @@ -229,6 +239,43 @@ mod tests {
assert!(parse_time_to_unix_millis("").is_err());
}

#[test]
fn test_parse_time_to_datetime_relative() {
let dt = parse_time_to_datetime("1h").unwrap();
let expected = Utc::now().timestamp() - 3600;
assert!((dt.timestamp() - expected).abs() < 2);
}

#[test]
fn test_parse_time_to_datetime_long_form() {
let dt = parse_time_to_datetime("2hours").unwrap();
let expected = Utc::now().timestamp() - 7200;
assert!((dt.timestamp() - expected).abs() < 2);
}

#[test]
fn test_parse_time_to_datetime_unix_millis() {
let dt = parse_time_to_datetime("1700000000000").unwrap();
assert_eq!(dt.timestamp_millis(), 1700000000000);
}

#[test]
fn test_parse_time_to_datetime_rfc3339() {
let dt = parse_time_to_datetime("2024-01-01T00:00:00Z").unwrap();
assert_eq!(dt.timestamp_millis(), 1704067200000);
}

#[test]
fn test_parse_time_to_datetime_invalid_input() {
let err = parse_time_to_datetime("not-a-time").unwrap_err();
assert!(err.to_string().contains("unable to parse time"));
}

#[test]
fn test_parse_time_to_datetime_empty() {
assert!(parse_time_to_datetime("").is_err());
}

#[test]
fn test_parse_time_to_unix_returns_seconds() {
let secs = parse_time_to_unix("1700000000000").unwrap();
Expand Down
Loading