diff --git a/crates/polars-plan/src/dsl/function_expr/temporal.rs b/crates/polars-plan/src/dsl/function_expr/temporal.rs index 2a4e76fd326fc..3c35f9f8bc970 100644 --- a/crates/polars-plan/src/dsl/function_expr/temporal.rs +++ b/crates/polars-plan/src/dsl/function_expr/temporal.rs @@ -267,9 +267,7 @@ pub(super) fn date_offset(s: &[Series]) -> PolarsResult { 1 => match offsets.get(0) { Some(offset) => { let offset = Duration::parse(offset); - tz.is_none() - || tz.as_deref() == Some("UTC") - || offset.is_constant_duration() + offset.is_constant_duration(tz.as_deref()) }, None => false, }, diff --git a/crates/polars-time/src/windows/duration.rs b/crates/polars-time/src/windows/duration.rs index f0be3e241366d..92bf8089b96bb 100644 --- a/crates/polars-time/src/windows/duration.rs +++ b/crates/polars-time/src/windows/duration.rs @@ -32,7 +32,7 @@ pub struct Duration { months: i64, // the number of weeks for the duration weeks: i64, - // the number of nanoseconds for the duration + // the number of days for the duration days: i64, // the number of nanoseconds for the duration nsecs: i64, @@ -363,8 +363,15 @@ impl Duration { self.nsecs == 0 } - pub fn is_constant_duration(&self) -> bool { - self.months == 0 && self.weeks == 0 && self.days == 0 + pub fn is_constant_duration(&self, time_zone: Option<&str>) -> bool { + if time_zone.is_none() || time_zone == Some("UTC") { + self.months == 0 + } else { + // For non-native, non-UTC time zones, 1 calendar day is not + // necessarily 24 hours due to daylight savings time. + self.months == 0 && self.weeks == 0 && self.days == 0 + } + } } /// Returns the nanoseconds from the `Duration` without the weeks or months part. @@ -372,7 +379,7 @@ impl Duration { self.nsecs } - /// Estimated duration of the window duration. Not a very good one if months != 0. + /// Estimated duration of the window duration. Not a very good one if not a constant duration. #[doc(hidden)] pub const fn duration_ns(&self) -> i64 { self.months * 28 * 24 * 3600 * NANOSECONDS