From 582d9d5cdc21ad8561dd82dc17381f88ea5949bd Mon Sep 17 00:00:00 2001 From: Andy Grove Date: Mon, 13 Jul 2026 20:46:55 -0600 Subject: [PATCH] perf: optimize to_local_time in datafusion-functions --- .../functions/src/datetime/to_local_time.rs | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/datafusion/functions/src/datetime/to_local_time.rs b/datafusion/functions/src/datetime/to_local_time.rs index 5bd1978893d54..52ac76243e0f1 100644 --- a/datafusion/functions/src/datetime/to_local_time.rs +++ b/datafusion/functions/src/datetime/to_local_time.rs @@ -15,9 +15,9 @@ // specific language governing permissions and limitations // under the License. -use std::ops::Add; use std::sync::Arc; +use arrow::array::temporal_conversions::{MICROSECONDS, MILLISECONDS, NANOSECONDS}; use arrow::array::timezone::Tz; use arrow::array::{ArrayRef, PrimitiveArray}; use arrow::datatypes::DataType::Timestamp; @@ -26,7 +26,7 @@ use arrow::datatypes::{ ArrowTimestampType, DataType, TimestampMicrosecondType, TimestampMillisecondType, TimestampNanosecondType, TimestampSecondType, }; -use chrono::{DateTime, MappedLocalTime, Offset, TimeDelta, TimeZone, Utc}; +use chrono::{DateTime, MappedLocalTime, Offset, TimeZone, Utc}; use datafusion_common::cast::as_primitive_array; use datafusion_common::{ @@ -337,24 +337,23 @@ pub fn adjust_to_local_time(ts: i64, tz: Tz) -> Result adjusted_date_time.timestamp_nanos_opt().ok_or_else(|| - internal_datafusion_err!( - "Failed to convert DateTime to timestamp in nanosecond. This error may occur if the date is out of range. The supported date ranges are between 1677-09-21T00:12:43.145224192 and 2262-04-11T23:47:16.854775807" - ) - ), - Microsecond => Ok(adjusted_date_time.timestamp_micros()), - Millisecond => Ok(adjusted_date_time.timestamp_millis()), - Second => Ok(adjusted_date_time.timestamp()), - } + // Shifting an instant by a whole number of seconds is exact in every + // timestamp unit, so the offset is applied directly to the i64 value rather + // than round-tripping through chrono's calendar arithmetic. The scaling + // cannot overflow: the largest possible offset is 26 hours (93,600 seconds). + let offset = offset_seconds + * match T::UNIT { + Nanosecond => NANOSECONDS, + Microsecond => MICROSECONDS, + Millisecond => MILLISECONDS, + Second => 1, + }; + + ts.checked_add(offset).ok_or_else(|| { + internal_datafusion_err!( + "Failed to adjust timestamp to local time: the adjusted value is out of range" + ) + }) } #[cfg(test)]