Skip to content

Commit 98148f9

Browse files
Merge pull request #402 from datadog-labs/refactor/timestamp-parsing-helper
refactor(util): extract parse_time_to_datetime helper
2 parents 715f007 + d62e14d commit 98148f9

7 files changed

Lines changed: 62 additions & 31 deletions

File tree

src/commands/audit_logs.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@ pub async fn list(cfg: &Config, from: String, to: String, limit: i32) -> Result<
1818
None => AuditAPI::with_config(dd_cfg),
1919
};
2020

21-
let from_dt =
22-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&from)?).unwrap();
23-
let to_dt =
24-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&to)?).unwrap();
21+
let from_dt = util::parse_time_to_datetime(&from)?;
22+
let to_dt = util::parse_time_to_datetime(&to)?;
2523

2624
let params = ListAuditLogsOptionalParams::default()
2725
.filter_from(from_dt)

src/commands/cicd.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,8 @@ pub async fn tests_list(
7373
None => CIVisibilityTestsAPI::with_config(dd_cfg),
7474
};
7575

76-
let from_dt =
77-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&from)?).unwrap();
78-
let to_dt =
79-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&to)?).unwrap();
76+
let from_dt = util::parse_time_to_datetime(&from)?;
77+
let to_dt = util::parse_time_to_datetime(&to)?;
8078

8179
let mut params = ListCIAppTestEventsOptionalParams::default()
8280
.filter_from(from_dt)

src/commands/cost.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,11 @@ pub async fn projected(cfg: &Config) -> Result<()> {
3030
pub async fn by_org(cfg: &Config, start_month: String, end_month: Option<String>) -> Result<()> {
3131
let api = make_usage_api(cfg);
3232

33-
let start_dt =
34-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&start_month)?)
35-
.unwrap();
33+
let start_dt = util::parse_time_to_datetime(&start_month)?;
3634

3735
let mut params = GetCostByOrgOptionalParams::default();
3836
if let Some(e) = end_month {
39-
let end_dt =
40-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&e)?).unwrap();
37+
let end_dt = util::parse_time_to_datetime(&e)?;
4138
params = params.end_month(end_dt);
4239
}
4340

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

54-
let start_dt =
55-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&start)?).unwrap();
51+
let start_dt = util::parse_time_to_datetime(&start)?;
5652

5753
let fields_str = fields.unwrap_or_else(|| "*".to_string());
5854
let params = GetMonthlyCostAttributionOptionalParams::default();

src/commands/rum.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,8 @@ pub async fn events_list(
9191
None => RUMAPI::with_config(dd_cfg),
9292
};
9393

94-
let from_dt =
95-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&from)?).unwrap();
96-
let to_dt =
97-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&to)?).unwrap();
94+
let from_dt = util::parse_time_to_datetime(&from)?;
95+
let to_dt = util::parse_time_to_datetime(&to)?;
9896

9997
let mut params = ListRUMEventsOptionalParams::default()
10098
.filter_from(from_dt)

src/commands/security.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,8 @@ pub async fn signals_search(
232232
None => SecurityMonitoringAPI::with_config(dd_cfg),
233233
};
234234

235-
let from_dt =
236-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&from)?).unwrap();
237-
let to_dt =
238-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&to)?).unwrap();
235+
let from_dt = util::parse_time_to_datetime(&from)?;
236+
let to_dt = util::parse_time_to_datetime(&to)?;
239237

240238
let body = SecurityMonitoringSignalListRequest::new()
241239
.filter(

src/commands/usage.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,11 @@ pub async fn summary(cfg: &Config, start: String, end: Option<String>) -> Result
1616
None => UsageMeteringAPI::with_config(dd_cfg),
1717
};
1818

19-
let start_dt =
20-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&start)?).unwrap();
19+
let start_dt = util::parse_time_to_datetime(&start)?;
2120

2221
let mut params = GetUsageSummaryOptionalParams::default();
2322
if let Some(e) = end {
24-
let end_dt =
25-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&e)?).unwrap();
23+
let end_dt = util::parse_time_to_datetime(&e)?;
2624
params = params.end_month(end_dt);
2725
}
2826

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

43-
let start_dt =
44-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&start)?).unwrap();
41+
let start_dt = util::parse_time_to_datetime(&start)?;
4542

4643
let mut params = GetHourlyUsageAttributionOptionalParams::default();
4744
if let Some(e) = end {
48-
let end_dt =
49-
chrono::DateTime::from_timestamp_millis(util::parse_time_to_unix_millis(&e)?).unwrap();
45+
let end_dt = util::parse_time_to_datetime(&e)?;
5046
params = params.end_hr(end_dt);
5147
}
5248

src/util.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ pub fn parse_time_to_unix(input: &str) -> Result<i64> {
8787
Ok(parse_time_to_unix_millis(input)? / 1000)
8888
}
8989

90+
/// Parses a time string into a `chrono::DateTime<Utc>`.
91+
///
92+
/// Returns an `anyhow` error if the input cannot be parsed or if the resulting
93+
/// timestamp falls outside chrono's representable range.
94+
pub fn parse_time_to_datetime(input: &str) -> Result<chrono::DateTime<Utc>> {
95+
let ms = parse_time_to_unix_millis(input)?;
96+
chrono::DateTime::from_timestamp_millis(ms)
97+
.ok_or_else(|| anyhow::anyhow!("timestamp out of valid range: {input:?}"))
98+
}
99+
90100
/// Parses a human-readable duration string into milliseconds.
91101
///
92102
/// Unlike `parse_time_to_unix_millis`, this does **not** subtract from the
@@ -229,6 +239,43 @@ mod tests {
229239
assert!(parse_time_to_unix_millis("").is_err());
230240
}
231241

242+
#[test]
243+
fn test_parse_time_to_datetime_relative() {
244+
let dt = parse_time_to_datetime("1h").unwrap();
245+
let expected = Utc::now().timestamp() - 3600;
246+
assert!((dt.timestamp() - expected).abs() < 2);
247+
}
248+
249+
#[test]
250+
fn test_parse_time_to_datetime_long_form() {
251+
let dt = parse_time_to_datetime("2hours").unwrap();
252+
let expected = Utc::now().timestamp() - 7200;
253+
assert!((dt.timestamp() - expected).abs() < 2);
254+
}
255+
256+
#[test]
257+
fn test_parse_time_to_datetime_unix_millis() {
258+
let dt = parse_time_to_datetime("1700000000000").unwrap();
259+
assert_eq!(dt.timestamp_millis(), 1700000000000);
260+
}
261+
262+
#[test]
263+
fn test_parse_time_to_datetime_rfc3339() {
264+
let dt = parse_time_to_datetime("2024-01-01T00:00:00Z").unwrap();
265+
assert_eq!(dt.timestamp_millis(), 1704067200000);
266+
}
267+
268+
#[test]
269+
fn test_parse_time_to_datetime_invalid_input() {
270+
let err = parse_time_to_datetime("not-a-time").unwrap_err();
271+
assert!(err.to_string().contains("unable to parse time"));
272+
}
273+
274+
#[test]
275+
fn test_parse_time_to_datetime_empty() {
276+
assert!(parse_time_to_datetime("").is_err());
277+
}
278+
232279
#[test]
233280
fn test_parse_time_to_unix_returns_seconds() {
234281
let secs = parse_time_to_unix("1700000000000").unwrap();

0 commit comments

Comments
 (0)