{"record":{"id":"66281dda7fdf1620","repo":"a-b-street/abstreet","slug":"bad-timeinterval","errorCode":null,"errorMessage":"Bad TimeInterval {} .. {}","messagePattern":"Bad TimeInterval (.+?) \\.\\. (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sim/src/lib.rs","lineNumber":505,"sourceCode":"        assert!(pos.dist_along() <= lane.length());\n        SidewalkSpot {\n            sidewalk_pos: pos,\n            connection: SidewalkPOI::SuddenlyAppear,\n        }\n    }\n}\n\n#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]\npub(crate) struct TimeInterval {\n    // TODO Private fields\n    pub start: Time,\n    pub end: Time,\n}\n\nimpl TimeInterval {\n    pub fn new(start: Time, end: Time) -> TimeInterval {\n        if end < start {\n            panic!(\"Bad TimeInterval {} .. {}\", start, end);\n        }\n        TimeInterval { start, end }\n    }\n\n    pub fn percent(&self, t: Time) -> f64 {\n        if self.start == self.end {\n            return 1.0;\n        }\n\n        let x = (t - self.start) / (self.end - self.start);\n        assert!((0.0..=1.0).contains(&x));\n        x\n    }\n\n    pub fn percent_clamp_end(&self, t: Time) -> f64 {\n        if t > self.end {\n            return 1.0;\n        }","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/sim/src/lib.rs#L487-L523","documentation":"TimeInterval::new validates that the interval start precedes its end on the simulation time axis. If end < start the interval is meaningless (negative duration) so the library panics immediately rather than constructing a broken interval that would corrupt later percent()/duration math.","triggerScenarios":"Calling TimeInterval::new(start, end) with an end Time earlier than the start Time, e.g. by passing arguments in the wrong order or computing times from reversed data.","commonSituations":"Scenario files or CSV inputs where timestamps are swapped; deriving start/end from sorted-incorrect data; off-by-one when converting raw seconds; callers hand-building intervals for map UI slices.","solutions":["Swap the arguments so TimeInterval::new receives the earlier time first","Sort or validate the source times before constructing the interval","Debug-print the two Time values; if they come from user data, fix the producer"],"exampleFix":"// before\nlet iv = TimeInterval::new(end, start);\n// after\nlet iv = TimeInterval::new(start.min(end), start.max(end));","handlingStrategy":"validation","validationCode":"fn safe_time_interval(start: Time, end: Time) -> Option<TimeInterval> {\n    if end < start { None } else { Some(TimeInterval::new(start, end)) }\n}","typeGuard":"fn valid_time_interval(start: Time, end: Time) -> bool { end >= start }","tryCatchPattern":"let iv = std::panic::catch_unwind(|| TimeInterval::new(start, end)).ok();","preventionTips":["Sort timestamps before building intervals","Never pass raw unvalidated times from files directly to TimeInterval::new"],"tags":["rust","panic","argument-validation","simulation"],"backgroundTag":"invalid-argument-value","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}