{"record":{"id":"b4eb205d4ebc9f0e","repo":"clockworklabs/SpacetimeDB","slug":"duration-since-unix-epoch-overflows-i64-microsecon","errorCode":null,"errorMessage":"Duration since Unix epoch overflows i64 microseconds","messagePattern":"Duration since Unix epoch overflows i64 microseconds","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/sats/src/timestamp.rs","lineNumber":78,"sourceCode":"    /// Returns `Err(duration_before_unix_epoch)` if `self` is before `Self::UNIX_EPOCH`.\n    pub fn to_duration_since_unix_epoch(self) -> Result<Duration, Duration> {\n        let micros = self.to_micros_since_unix_epoch();\n        if micros >= 0 {\n            Ok(Duration::from_micros(micros as u64))\n        } else {\n            Err(Duration::from_micros((-micros) as u64))\n        }\n    }\n\n    /// Return a [`Timestamp`] which is [`Timestamp::UNIX_EPOCH`] plus `duration`.\n    ///\n    /// Panics if `duration.as_micros` overflows an `i64`\n    pub fn from_duration_since_unix_epoch(duration: Duration) -> Self {\n        Self::from_micros_since_unix_epoch(\n            duration\n                .as_micros()\n                .try_into()\n                .expect(\"Duration since Unix epoch overflows i64 microseconds\"),\n        )\n    }\n\n    /// Convert `self` into a [`SystemTime`] which refers to approximately the same point in time.\n    ///\n    /// This conversion may lose precision, as [`SystemTime`]'s prevision varies depending on platform.\n    /// E.g. Unix targets have microsecond precision, but Windows only 100-microsecond precision.\n    ///\n    /// This conversion may panic if `self` is out of bounds for [`SystemTime`].\n    /// We are not aware of any platforms for which [`SystemTime`] offers a smaller range than [`Timestamp`],\n    /// but such a platform may exist.\n    pub fn to_system_time(self) -> SystemTime {\n        match self.to_duration_since_unix_epoch() {\n            Ok(positive) => SystemTime::UNIX_EPOCH\n                .checked_add(positive)\n                .expect(\"Timestamp with i64 microseconds since Unix epoch overflows SystemTime\"),\n            Err(negative) => SystemTime::UNIX_EPOCH\n                .checked_sub(negative)","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/sats/src/timestamp.rs#L60-L96","documentation":"Panic in `Timestamp::from_duration_since_unix_epoch`: converting a `std::time::Duration` into a `Timestamp` (i64 microseconds since epoch) requires the microsecond count to fit in `i64` (~±292,471 years around 1970). `try_into().expect(\"Duration since Unix epoch overflows i64 microseconds\")` panics beyond that. Documented as an intentional panic.","triggerScenarios":"Calling `Timestamp::from_duration_since_unix_epoch(d)` with a Duration larger than ~292k years — typically from overflow-prone arithmetic (duration multiplication, summing intervals), `Duration::MAX`, or unvalidated parsed input. Everyday timestamps never hit this; the trigger is a computed/garbage Duration.","commonSituations":"Scheduling code computing far-future deadlines with `now + huge_offset`; parsing timestamps with wrong units (nanoseconds fed as milliseconds multiplies value by 1000 repeatedly); fuzz inputs; clock-offset calculations against `SystemTime::MAX`-style extremes.","solutions":["Log/inspect the incoming Duration at the call site to identify the arithmetic or unit bug (most common: ns vs ms vs µs confusion).","Bound computed durations with `checked_mul`/`checked_add` and a sane maximum before building a Timestamp.","Validate parsed numeric inputs (epoch values, offsets) against a plausible range (e.g. year 1..9999) before conversion.","Add tests exercising extreme values through timestamp math."],"exampleFix":"// before: unit confusion can overflow\nlet ts = Timestamp::from_duration_since_unix_epoch(Duration::from_nanos(input_ns));\n\n// after: validate range first\nlet d = Duration::from_nanos(input_ns);\nif d > Duration::from_secs(60 * 60 * 24 * 365 * 5000) { return Err(OutOfRange); }\nlet ts = Timestamp::from_duration_since_unix_epoch(d);","handlingStrategy":"validation","validationCode":"const MAX_TS: Duration = Duration::from_secs(60 * 60 * 24 * 365 * 200_000);\nfn checked_ts(d: Duration) -> Result<Timestamp, Overflow> {\n    (d <= MAX_TS).then(|| Timestamp::from_duration_since_unix_epoch(d)).ok_or(Overflow)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Double-check time units (ns vs µs vs ms) at every conversion boundary — unit bugs cause most overflow hits.","Bound deadline arithmetic with checked_* ops.","Validate parsed epoch values against a plausible year range before constructing Timestamps."],"tags":["rust","spacetimedb","timestamp","overflow","arithmetic","panic"],"backgroundTag":"arithmetic-overflow","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}