clockworklabs/SpacetimeDB · error
SystemTime predates the Unix epoch
Error message
SystemTime predates the Unix epoch
What it means
Panic in `Timestamp::from_system_time`: `system_time.duration_since(UNIX_EPOCH).expect("SystemTime predates the Unix epoch")` fires when the given `SystemTime` is before 1970-01-01, because `duration_since` returns an `Err` for pre-epoch times. Unlike `to_system_time`, which handles negatives via a checked branch, this constructor simply refuses pre-epoch input.
Solutions
- At the call site, branch on `t.duration_since(SystemTime::UNIX_EPOCH)`: use `Ok(d) => Timestamp::from_duration_since_unix_epoch(d)` and handle `Err(e)` (e) explicitly (clamp, reject, or use the pre-epoch-capable constructor if one is added).
- Fix the machine/VM clock (enable NTP; check RTC battery) if `SystemTime::now()` itself is pre-1970.
- Reject or clamp pre-epoch input at your API boundary before it reaches this conversion.
- Add a test feeding `UNIX_EPOCH - 1s` to catch regressions in time-handling paths.
Example fix
// before: panics for pre-epoch times
let ts = Timestamp::from_system_time(t);
// after: handle the pre-epoch case explicitly
let ts = match t.duration_since(SystemTime::UNIX_EPOCH) {
Ok(d) => Timestamp::from_duration_since_unix_epoch(d),
Err(e) => return Err(InvalidTime::pre_epoch(e.duration())),
}; Defensive patterns
Strategy: validation
Validate before calling
fn system_time_to_timestamp(t: SystemTime) -> Result<Timestamp, PreEpoch> {
match t.duration_since(SystemTime::UNIX_EPOCH) {
Ok(d) => Ok(Timestamp::from_duration_since_unix_epoch(d)),
Err(_) => Err(PreEpoch), // or clamp to Timestamp::UNIX_EPOCH
}
} Prevention
- Never pass `UNIX_EPOCH - d` arithmetic results into from_system_time.
- Run NTP and verify `date -u` on hosts/VMs that feed clock values.
- Reject or clamp pre-epoch inputs at API boundaries.
When it happens
Trigger: Calling `Timestamp::from_system_time(t)` with a time earlier than the Unix epoch — e.g. a machine clock set before 1970 (broken RTC, VM clock bugs), archived/historical dates, or negative `Duration` arithmetic applied to `UNIX_EPOCH` (e.g. `UNIX_EPOCH - Duration::from_secs(10)`).
Common situations: Systems with dead CMOS batteries or NTP-starved VMs booting to pre-1970 dates; tests using `SystemTime::UNIX_EPOCH - x`; library code ingesting file timestamps or birthdates that legitimately predate 1970; containers with clock skews.
Related errors
- timestamp before unix epoch
- Timestamp with i64 microseconds before Unix epoch overflows…
- Timestamp with i64 microseconds since Unix epoch overflows…
- Duration since Unix epoch overflows i64 microseconds
- Duration overflows i64 microseconds
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/ca5553d8cb753647.
Report an issue: GitHub.
Appendix: source
Thrown at crates/sats/src/timestamp.rs:110
Ok(positive) => SystemTime::UNIX_EPOCH
.checked_add(positive)
.expect("Timestamp with i64 microseconds since Unix epoch overflows SystemTime"),
Err(negative) => SystemTime::UNIX_EPOCH
.checked_sub(negative)
.expect("Timestamp with i64 microseconds before Unix epoch overflows SystemTime"),
}
}
/// Convert a [`SystemTime`] into a [`Timestamp`] which refers to approximately the same point in time.
///
/// This conversion may panic if `system_time` is out of bounds for [`Duration`].
/// [`SystemTime`]'s range is larger than [`Timestamp`] on both Unix and Windows targets,
/// so times in the far past or far future may panic.
/// [`Timestamp`]'s range is approximately 292 years before and after the Unix epoch.
pub fn from_system_time(system_time: SystemTime) -> Self {
let duration = system_time
.duration_since(SystemTime::UNIX_EPOCH)
.expect("SystemTime predates the Unix epoch");
Self::from_duration_since_unix_epoch(duration)
}
/// Returns the [`Duration`] delta between `self` and `earlier`, if `earlier` predates `self`.
///
/// Returns `None` if `earlier` is strictly greater than `self`,
/// or if the difference between `earlier` and `self` overflows an `i64`.
pub fn duration_since(self, earlier: Timestamp) -> Option<Duration> {
self.time_duration_since(earlier)?.to_duration().ok()
}
/// Returns the [`TimeDuration`] delta between `self` and `earlier`.
///
/// The result may be negative if `earlier` is actually later than `self`.
///
/// Returns `None` if the subtraction overflows or underflows `i64` microseconds.
pub fn time_duration_since(self, earlier: Timestamp) -> Option<TimeDuration> {
let delta = selfView on GitHub (pinned to 6dee26c6ef)