diff --git a/Cargo.lock b/Cargo.lock
index 6c1c1439b1..60f49db51b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -793,9 +793,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chrono"
-version = "0.4.30"
+version = "0.4.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877"
+checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
diff --git a/influxdb_influxql_parser/src/literal.rs b/influxdb_influxql_parser/src/literal.rs
index a301c82027..3611987051 100644
--- a/influxdb_influxql_parser/src/literal.rs
+++ b/influxdb_influxql_parser/src/literal.rs
@@ -592,10 +592,9 @@ mod test {
 
         // infallible
         let ts = nanos_to_timestamp(i64::MAX);
-        assert_eq!(ts.timestamp_nanos(), i64::MAX);
+        assert_eq!(ts.timestamp_nanos_opt().unwrap(), i64::MAX);
 
-        // let ts = nanos_to_timestamp(i64::MIN);
-        // This line panics with an arithmetic overflow.
-        // assert_eq!(ts.timestamp_nanos(), i64::MIN);
+        let ts = nanos_to_timestamp(i64::MIN);
+        assert_eq!(ts.timestamp_nanos_opt().unwrap(), i64::MIN);
     }
 }
diff --git a/influxdb_influxql_parser/src/time_range.rs b/influxdb_influxql_parser/src/time_range.rs
index afb9526430..4d07580d34 100644
--- a/influxdb_influxql_parser/src/time_range.rs
+++ b/influxdb_influxql_parser/src/time_range.rs
@@ -252,7 +252,14 @@ pub fn split_cond(
                     };
 
                     let ts = match expr {
-                        Expr::Literal(Literal::Timestamp(ts)) => ts.timestamp_nanos(),
+                        Expr::Literal(Literal::Timestamp(ts)) => match ts.timestamp_nanos_opt() {
+                            Some(ts) => ts,
+                            None => {
+                                return ControlFlow::Break(error::map::internal(
+                                    "timestamp out o range",
+                                ));
+                            }
+                        },
                         expr => {
                             return ControlFlow::Break(error::map::internal(format!(
                                 "expected Timestamp, got: {}",
@@ -390,7 +397,9 @@ impl TimeRange {
 /// Simplifies an InfluxQL duration `expr` to a nanosecond interval represented as an `i64`.
 pub fn duration_expr_to_nanoseconds(ctx: &ReduceContext, expr: &Expr) -> Result<i64, ExprError> {
     match reduce_time_expr(ctx, expr)? {
-        Expr::Literal(Literal::Timestamp(v)) => Ok(v.timestamp_nanos()),
+        Expr::Literal(Literal::Timestamp(v)) => v
+            .timestamp_nanos_opt()
+            .ok_or_else(|| error::map::expr("timestamp out of range")),
         _ => error::expr("invalid duration expression"),
     }
 }
@@ -637,10 +646,15 @@ fn expr_to_duration(ctx: &ReduceContext, expr: Expr) -> ExprResult {
     Ok(lit(match expr {
         Expr::Literal(Literal::Duration(v)) => v,
         Expr::Literal(Literal::Integer(v)) => Duration(v),
-        Expr::Literal(Literal::Timestamp(v)) => Duration(v.timestamp_nanos()),
-        Expr::Literal(Literal::String(v)) => {
-            Duration(parse_timestamp_nanos(&v, ctx.tz)?.timestamp_nanos())
-        }
+        Expr::Literal(Literal::Timestamp(v)) => Duration(
+            v.timestamp_nanos_opt()
+                .ok_or_else(|| error::map::expr("timestamp out of range"))?,
+        ),
+        Expr::Literal(Literal::String(v)) => Duration(
+            parse_timestamp_nanos(&v, ctx.tz)?
+                .timestamp_nanos_opt()
+                .ok_or_else(|| error::map::expr("timestamp out of range"))?,
+        ),
         _ => return error::expr(format!("unable to cast {expr} to duration")),
     }))
 }
diff --git a/iox_data_generator/src/bin/iox_data_generator.rs b/iox_data_generator/src/bin/iox_data_generator.rs
index 5a56e7063c..3355b2852b 100644
--- a/iox_data_generator/src/bin/iox_data_generator.rs
+++ b/iox_data_generator/src/bin/iox_data_generator.rs
@@ -139,12 +139,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
     }
 
     let execution_start_time = Local::now();
+    let execution_start_time_nanos = execution_start_time
+        .timestamp_nanos_opt()
+        .expect("'now' is in nano range");
 
     let start_datetime = datetime_nanoseconds(config.start.as_deref(), execution_start_time);
     let end_datetime = datetime_nanoseconds(config.end.as_deref(), execution_start_time);
 
-    let start_display = start_datetime.unwrap_or_else(|| execution_start_time.timestamp_nanos());
-    let end_display = end_datetime.unwrap_or_else(|| execution_start_time.timestamp_nanos());
+    let start_display = start_datetime.unwrap_or(execution_start_time_nanos);
+    let end_display = end_datetime.unwrap_or(execution_start_time_nanos);
 
     let continue_on = config.do_continue;
 
@@ -201,7 +204,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
         &mut points_writer_builder,
         start_datetime,
         end_datetime,
-        execution_start_time.timestamp_nanos(),
+        execution_start_time_nanos,
         continue_on,
         config.batch_size,
         config.print,
@@ -231,7 +234,9 @@ fn datetime_nanoseconds(arg: Option<&str>, now: DateTime<Local>) -> Option<i64>
                 now - chrono_duration
             });
 
-        datetime.timestamp_nanos()
+        datetime
+            .timestamp_nanos_opt()
+            .expect("timestamp out of range")
     })
 }
 
@@ -255,7 +260,9 @@ mod test {
     fn relative() {
         let fixed_now = Local::now();
         let ns = datetime_nanoseconds(Some("1hr"), fixed_now);
-        let expected = (fixed_now - chrono::Duration::hours(1)).timestamp_nanos();
+        let expected = (fixed_now - chrono::Duration::hours(1))
+            .timestamp_nanos_opt()
+            .unwrap();
         assert_eq!(ns, Some(expected));
     }
 }
diff --git a/iox_query/src/frontend.rs b/iox_query/src/frontend.rs
index f3ac2892d0..7a6bd640b6 100644
--- a/iox_query/src/frontend.rs
+++ b/iox_query/src/frontend.rs
@@ -39,12 +39,14 @@ mod test {
                 .start_timestamp
                 .value()
                 .expect("start timestamp")
-                .timestamp_nanos();
+                .timestamp_nanos_opt()
+                .expect("start timestamp in range");
             let end_ts = $EXTRACTED
                 .end_timestamp
                 .value()
                 .expect("end timestamp")
-                .timestamp_nanos();
+                .timestamp_nanos_opt()
+                .expect("end timestamp in range");
 
             assert!(start_ts > 0, "start timestamp was non zero");
             assert!(end_ts > 0, "end timestamp was non zero");
diff --git a/iox_query_influxql/src/plan/planner.rs b/iox_query_influxql/src/plan/planner.rs
index 313eb13ee5..cf8d72f180 100644
--- a/iox_query_influxql/src/plan/planner.rs
+++ b/iox_query_influxql/src/plan/planner.rs
@@ -1843,10 +1843,10 @@ impl<'a> InfluxQLToLogicalPlan<'a> {
                 Literal::Float(v) => Ok(lit(*v)),
                 Literal::String(v) => Ok(lit(v)),
                 Literal::Boolean(v) => Ok(lit(*v)),
-                Literal::Timestamp(v) => Ok(lit(ScalarValue::TimestampNanosecond(
-                    Some(v.timestamp_nanos()),
-                    None,
-                ))),
+                Literal::Timestamp(v) => v
+                    .timestamp_nanos_opt()
+                    .ok_or_else(|| error::map::query("timestamp out of range"))
+                    .map(|ts| lit(ScalarValue::TimestampNanosecond(Some(ts), None))),
                 Literal::Duration(v) => {
                     Ok(lit(ScalarValue::IntervalMonthDayNano(Some((**v).into()))))
                 }
@@ -2210,9 +2210,14 @@ impl<'a> InfluxQLToLogicalPlan<'a> {
         let time_range = if time_range.is_unbounded() {
             TimeRange {
                 lower: Some(match cutoff {
-                    MetadataCutoff::Absolute(dt) => dt.timestamp_nanos(),
+                    MetadataCutoff::Absolute(dt) => dt
+                        .timestamp_nanos_opt()
+                        .ok_or_else(|| error::map::query("timestamp out of range"))?,
                     MetadataCutoff::Relative(delta) => {
-                        start_time.timestamp_nanos() - delta.as_nanos() as i64
+                        start_time
+                            .timestamp_nanos_opt()
+                            .ok_or_else(|| error::map::query("timestamp out of range"))?
+                            - delta.as_nanos() as i64
                     }
                 }),
                 upper: None,
diff --git a/iox_query_influxql/src/plan/rewriter.rs b/iox_query_influxql/src/plan/rewriter.rs
index 5a0ad563a1..be45122959 100644
--- a/iox_query_influxql/src/plan/rewriter.rs
+++ b/iox_query_influxql/src/plan/rewriter.rs
@@ -123,7 +123,7 @@ impl RewriteSelect {
         let time_range = match (interval, time_range.upper) {
             (Some(interval), None) if interval.duration > 0 => TimeRange {
                 lower: time_range.lower,
-                upper: Some(now.timestamp_nanos()),
+                upper: Some(now.timestamp_nanos_opt().expect("'now' in nano range")),
             },
             _ => time_range,
         };
diff --git a/iox_time/Cargo.toml b/iox_time/Cargo.toml
index ea982fe647..d8ef58e829 100644
--- a/iox_time/Cargo.toml
+++ b/iox_time/Cargo.toml
@@ -7,7 +7,7 @@ edition.workspace = true
 license.workspace = true
 
 [dependencies]
-chrono = { version = "0.4.30", default-features = false, features = ["clock", "std"] }
+chrono = { version = "0.4.31", default-features = false, features = ["clock", "std"] }
 parking_lot = "0.12"
 tokio = { version = "1.32", features = ["macros", "parking_lot", "rt-multi-thread", "sync", "time"] }
 workspace-hack = { version = "0.1", path = "../workspace-hack" }
diff --git a/iox_time/src/lib.rs b/iox_time/src/lib.rs
index 3fd72e2cdf..853155d231 100644
--- a/iox_time/src/lib.rs
+++ b/iox_time/src/lib.rs
@@ -111,7 +111,8 @@ impl Time {
 
     /// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC
     pub fn timestamp_nanos(&self) -> i64 {
-        self.0.timestamp_nanos()
+        // TODO: ensure that this can never over-/underflow
+        self.0.timestamp_nanos_opt().expect("nanos in range")
     }
 
     /// Returns the number of seconds since January 1, 1970 UTC
@@ -539,7 +540,7 @@ mod test {
             );
             assert_eq!(
                 time,
-                Time::from_timestamp_nanos(date_time.timestamp_nanos())
+                Time::from_timestamp_nanos(date_time.timestamp_nanos_opt().unwrap())
             );
             assert_eq!(
                 Time::from_timestamp_millis(date_time.timestamp_millis()).unwrap(),
@@ -549,7 +550,10 @@ mod test {
                 )
             );
 
-            assert_eq!(time.timestamp_nanos(), date_time.timestamp_nanos());
+            assert_eq!(
+                time.timestamp_nanos(),
+                date_time.timestamp_nanos_opt().unwrap()
+            );
             assert_eq!(time.timestamp_millis(), date_time.timestamp_millis());
             assert_eq!(time.to_rfc3339(), date_time.to_rfc3339());
 
diff --git a/predicate/src/delete_predicate.rs b/predicate/src/delete_predicate.rs
index 75a37878da..b298a630d5 100644
--- a/predicate/src/delete_predicate.rs
+++ b/predicate/src/delete_predicate.rs
@@ -206,7 +206,11 @@ fn parse_time(input: &str) -> Result<i64> {
     // See examples here https://docs.influxdata.com/influxdb/v2.0/reference/cli/influx/delete/#delete-all-points-within-a-specified-time-frame
     let datetime_result = DateTime::parse_from_rfc3339(input);
     match datetime_result {
-        Ok(datetime) => Ok(datetime.timestamp_nanos()),
+        Ok(datetime) => datetime
+            .timestamp_nanos_opt()
+            .ok_or_else(|| Error::InvalidTimestamp {
+                value: datetime.to_string(),
+            }),
         Err(timestamp_err) => {
             // See if it is in nanosecond form
             let time_result = input.parse::<i64>();
diff --git a/query_functions/src/window/internal.rs b/query_functions/src/window/internal.rs
index 474a4c8127..8000881f50 100644
--- a/query_functions/src/window/internal.rs
+++ b/query_functions/src/window/internal.rs
@@ -199,7 +199,9 @@ fn to_timestamp_nanos_utc(
     let ndatetime = NaiveDateTime::new(ndate, ntime);
 
     let datetime = DateTime::<Utc>::from_naive_utc_and_offset(ndatetime, Utc);
-    datetime.timestamp_nanos()
+    datetime
+        .timestamp_nanos_opt()
+        .expect("timestamp nanos in range")
 }
 
 impl Add<Duration> for i64 {
@@ -386,7 +388,7 @@ mod tests {
     /// t: mustParseTime("1970-02-01T00:00:00Z"),
     fn must_parse_time(s: &str) -> i64 {
         let datetime = DateTime::parse_from_rfc3339(s).unwrap();
-        datetime.timestamp_nanos()
+        datetime.timestamp_nanos_opt().unwrap()
     }
 
     /// TestWindow_GetEarliestBounds
diff --git a/router/Cargo.toml b/router/Cargo.toml
index 03ba9c8e1b..001ce6608c 100644
--- a/router/Cargo.toml
+++ b/router/Cargo.toml
@@ -48,7 +48,7 @@ workspace-hack = { version = "0.1", path = "../workspace-hack" }
 [dev-dependencies]
 assert_matches = "1.5"
 base64 = "0.21.4"
-chrono = { version = "0.4.30", default-features = false }
+chrono = { version = "0.4.31", default-features = false }
 criterion = { version = "0.5", default-features = false, features = [
     "async_tokio",
     "rayon",
diff --git a/trace_exporters/src/jaeger.rs b/trace_exporters/src/jaeger.rs
index d7a6155719..1e6e4fbdb5 100644
--- a/trace_exporters/src/jaeger.rs
+++ b/trace_exporters/src/jaeger.rs
@@ -139,7 +139,19 @@ impl JaegerAgentExporter {
                 service_name: self.service_name.clone(),
                 tags: self.tags.clone(),
             },
-            spans: spans.into_iter().map(Into::into).collect(),
+            spans: spans
+                .into_iter()
+                .filter_map(|span| match jaeger::Span::try_from(span) {
+                    Ok(span) => Some(span),
+                    Err(e) => {
+                        warn!(
+                            %e,
+                            "cannot convert span to jaeger format",
+                        );
+                        None
+                    }
+                })
+                .collect(),
             seq_no,
             stats: None,
         }
diff --git a/trace_exporters/src/jaeger/span.rs b/trace_exporters/src/jaeger/span.rs
index 83addb6d97..f6234f5e3c 100644
--- a/trace_exporters/src/jaeger/span.rs
+++ b/trace_exporters/src/jaeger/span.rs
@@ -13,8 +13,10 @@ fn split_trace_id(trace_id: TraceId) -> (i64, i64) {
     (trace_id_high, trace_id_low)
 }
 
-impl From<Span> for jaeger::Span {
-    fn from(mut s: Span) -> Self {
+impl TryFrom<Span> for jaeger::Span {
+    type Error = String;
+
+    fn try_from(mut s: Span) -> Result<Self, Self::Error> {
         let (trace_id_high, trace_id_low) = split_trace_id(s.ctx.trace_id);
 
         // A parent span id of 0 indicates no parent span ID (span IDs are non-zero)
@@ -22,10 +24,17 @@ impl From<Span> for jaeger::Span {
 
         let (start_time, duration) = match (s.start, s.end) {
             (Some(start), Some(end)) => (
-                start.timestamp_nanos() / 1000,
+                start.timestamp_nanos_opt().ok_or_else(|| {
+                    format!("start timestamp cannot be represented as nanos: {start}")
+                })? / 1000,
                 (end - start).num_microseconds().expect("no overflow"),
             ),
-            (Some(start), _) => (start.timestamp_nanos() / 1000, 0),
+            (Some(start), _) => (
+                start.timestamp_nanos_opt().ok_or_else(|| {
+                    format!("start timestamp cannot be represented as nanos: {start}")
+                })? / 1000,
+                0,
+            ),
             _ => (0, 0),
         };
 
@@ -57,7 +66,12 @@ impl From<Span> for jaeger::Span {
 
         let logs = match s.events.is_empty() {
             true => None,
-            false => Some(s.events.into_iter().map(Into::into).collect()),
+            false => Some(
+                s.events
+                    .into_iter()
+                    .map(TryInto::try_into)
+                    .collect::<Result<_, _>>()?,
+            ),
         };
 
         let references = if s.ctx.links.is_empty() {
@@ -81,7 +95,7 @@ impl From<Span> for jaeger::Span {
             )
         };
 
-        Self {
+        Ok(Self {
             trace_id_low,
             trace_id_high,
             span_id: s.ctx.span_id.get() as i64,
@@ -93,14 +107,18 @@ impl From<Span> for jaeger::Span {
             duration,
             tags,
             logs,
-        }
+        })
     }
 }
 
-impl From<SpanEvent> for jaeger::Log {
-    fn from(event: SpanEvent) -> Self {
-        Self {
-            timestamp: event.time.timestamp_nanos() / 1000,
+impl TryFrom<SpanEvent> for jaeger::Log {
+    type Error = String;
+
+    fn try_from(event: SpanEvent) -> Result<Self, Self::Error> {
+        Ok(Self {
+            timestamp: event.time.timestamp_nanos_opt().ok_or_else(|| {
+                format!("timestamp cannot be represented as nanos: {}", event.time)
+            })? / 1000,
             fields: vec![jaeger::Tag {
                 key: "event".to_string(),
                 v_type: jaeger::TagType::String,
@@ -110,7 +128,7 @@ impl From<SpanEvent> for jaeger::Log {
                 v_long: None,
                 v_binary: None,
             }],
-        }
+        })
     }
 }