{"record":{"id":"748e886ec582304b","repo":"clockworklabs/SpacetimeDB","slug":"timestamp-with-i64-microseconds-before-unix-epoch","errorCode":null,"errorMessage":"Timestamp with i64 microseconds before Unix epoch overflows SystemTime","messagePattern":"Timestamp with i64 microseconds before Unix epoch overflows SystemTime","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/sats/src/timestamp.rs","lineNumber":97,"sourceCode":"        )\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)\n                .expect(\"Timestamp with i64 microseconds before Unix epoch overflows SystemTime\"),\n        }\n    }\n\n    /// Convert a [`SystemTime`] into a [`Timestamp`] which refers to approximately the same point in time.\n    ///\n    /// This conversion may panic if `system_time` is out of bounds for [`Duration`].\n    /// [`SystemTime`]'s range is larger than [`Timestamp`] on both Unix and Windows targets,\n    /// so times in the far past or far future may panic.\n    /// [`Timestamp`]'s range is approximately 292 years before and after the Unix epoch.\n    pub fn from_system_time(system_time: SystemTime) -> Self {\n        let duration = system_time\n            .duration_since(SystemTime::UNIX_EPOCH)\n            .expect(\"SystemTime predates the Unix epoch\");\n        Self::from_duration_since_unix_epoch(duration)\n    }\n\n    /// Returns the [`Duration`] delta between `self` and `earlier`, if `earlier` predates `self`.\n    ///","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/sats/src/timestamp.rs#L79-L115","documentation":"Panic in `Timestamp::to_system_time` for the negative branch: a pre-epoch Timestamp is converted with `UNIX_EPOCH.checked_sub(duration).expect(\"...before Unix epoch overflows SystemTime\")`. If the negative offset is beyond what the platform `SystemTime` can represent (before its minimum), the checked subtraction returns None and this panics. Docs state they are unaware of a platform with a smaller range than Timestamp, so hits are rare.","triggerScenarios":"Calling `to_system_time()` on a Timestamp far before 1970 (down to ~year -292k) on a platform whose `SystemTime` minimum is later than that (e.g. Windows epochs or 32-bit targets). Alternatively a corrupt/mis-decoded negative Timestamp (e.g. sign mishandling during deserialization) reaching this branch.","commonSituations":"Code converting arbitrary/historical dates or negative-delta arithmetic results to SystemTime; 32-bit or Windows targets with narrower time ranges; deserialization bugs producing garbage negative micros; fuzzed inputs.","solutions":["Validate the Timestamp's magnitude (e.g. |micros| within 1970±9999 years) before `to_system_time()`.","Trace where the pre-epoch Timestamp came from — legitimate historical data or a unit/sign bug in your time math.","On narrow platforms, avoid the round-trip: keep values as Timestamp and convert only for display with an explicit range check.","Centralize conversion in a checked helper returning Option instead of calling to_system_time directly."],"exampleFix":"// before: panics on extreme pre-epoch values on narrow platforms\nlet st = ts.to_system_time();\n\n// after: range-checked helper\nfn checked_to_system_time(ts: Timestamp) -> Option<SystemTime> {\n    let micros = ts.micros_since_unix_epoch();\n    (micros.unsigned_abs() < 8_640_000_000_000_000 * 10_000).then(|| ts.to_system_time())\n}","handlingStrategy":"validation","validationCode":"fn pre_epoch_to_system_time(ts: Timestamp) -> Option<SystemTime> {\n    let micros = ts.micros_since_unix_epoch();\n    (micros > -31_557_600_000_000 * 10_000 && micros < 0).then(|| ts.to_system_time())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Range-check extreme pre-epoch Timestamps before to_system_time().","Treat garbage negative Timestamps as a deserialization bug until proven otherwise.","Prefer keeping time values as Timestamp and converting only at display edges."],"tags":["rust","spacetimedb","timestamp","systemtime","overflow","pre-epoch","panic"],"backgroundTag":"timestamp-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"}