Skip to content

chore(hm-common): refactor and commonize time utils#176

Merged
markovejnovic merged 12 commits into
mainfrom
feat/hm-common-time
Jul 26, 2026
Merged

chore(hm-common): refactor and commonize time utils#176
markovejnovic merged 12 commits into
mainfrom
feat/hm-common-time

Conversation

@markovejnovic

Copy link
Copy Markdown
Contributor

Generated by an AI agent.

…onMs

Introduce a `DurationMs(u64)` newtype in hm-pipeline-ir, carried on the wire
as a bare JSON number via `#[serde(transparent)]`, with a single saturating
`From<Duration>` conversion.

Replaces the ad-hoc `elapsed().as_millis() as u64` conversions on
`BuildEvent::{StepEnd,BuildEnd}` and `StepResultSummary`, which diverged
between lossy `as u64` (3 sites in scheduler) and saturating `try_from` (1
site in cloud watch). The conversion now has exactly one home.

u64 milliseconds stays the wire representation (~584M-year ceiling):
`serde_json` cannot round-trip `u128`, and a raw `Duration` serializes as an
awkward `{ secs, nanos }` pair non-Rust consumers must special-case. The
render side rebuilds a `Duration` via `DurationMs::as_duration()`.
- Replace the inherent `as_duration` with `impl From<DurationMs> for Duration`,
  mirroring the existing `From<Duration>` and reading as `Duration::from(ms)`.
- Derive `Display` via `derive_more` (newtype-forwards to the inner `u64`)
  instead of a hand-written impl; output is unchanged.
Blanket-impl CompactDuration for any `T: Into<Duration>` instead of only
`Duration`, so `DurationMs` (and any future duration newtype) gets `.compact()`
for free — no dependency edge into hm-pipeline-ir, since the impl never names
the concrete type. Render sites drop `Duration::from(*ms).compact()` back to
`ms.compact()`.
New `hm_common::time::DurationExt` with `Duration::now_epoch_secs_i64()`:
reads the system clock, returns 0 if before the epoch, and logs via
`tracing::error!` + saturates at i64::MAX on the unreachable i64 overflow
(instead of the silent wrap the old hm-vm helper used).

Repoints hm-vm's registry off its local `epoch_secs`. Adds chrono to
hm-common for upcoming chrono-based time helpers.
parse_rfc3339 / ts_or_now are band-aids for untyped harmont-cloud
timestamp fields. TODO markers record when (SDK >=0.3 with typed
timestamps) and how they get deleted.
… via DurationMs

- `DateTimeExt::time_from_now()` on `DateTime<Utc>` — signed duration from now
  until the instant. Replaces the ad-hoc `expires_at - Utc::now()` in the cloud
  log-token refresh check. Gives the hm-common chrono dep its first consumer.
- The cloud step_end interval now routes through `TimeDelta::to_std()` +
  `DurationMs::from` instead of a hand-rolled `num_milliseconds().max(0)
  .cast_unsigned()`, so the Duration→ms conversion has a single home.
`Duration::now_epoch_secs::<T>()` for any `T: TryFrom<u64> + Bounded`, replacing
the i64-only helper. Saturates at `T::max_value()` (logging the type) instead of
wrapping. Callers use turbofish, e.g. `now_epoch_secs::<i64>()` in hm-vm.

Bonus: the saturating-overflow branch is now deterministically testable via a
narrow type (i8/u8), which the i64-only version could never reach. Adds
num-traits (for Bounded).
Name advertises the overflow behavior (saturates at T::max_value) at the call site.
@markovejnovic markovejnovic changed the title feat(hm-common): time module (epoch + now-relative) + DurationMs wire type chore(hm-common): refactor and commonize time utils Jul 26, 2026
@markovejnovic
markovejnovic merged commit 7077d83 into main Jul 26, 2026
15 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant