Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 66 additions & 1 deletion datafusion/physical-expr/src/datetime_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ fn _date_trunc_coarse_without_tz(
/// account that some granularities are not uniform durations of time
/// (e.g. months are not always the same lengths, leap seconds, etc)
fn date_trunc_coarse(granularity: &str, value: i64, tz: Option<Tz>) -> Result<i64> {
let value = match tz {
let value: Option<i64> = match tz {
Some(tz) => {
// Use chrono DateTime<Tz> to clear the various fields because need to clear per timezone,
// and NaiveDateTime (ISO 8601) has no concept of timezones
Expand All @@ -600,6 +600,14 @@ fn date_trunc_coarse(granularity: &str, value: i64, tz: Option<Tz>) -> Result<i6
}
}?;

// this case is caused by ambiguous time between "daylight saving time" and "standard time" in chrono
// https://github.com/apache/arrow-datafusion/issues/8899
if tz.is_some() && value.is_none() {
return not_impl_err!(
"date_trunc that converts ambiguous time between \"daylight saving time\" and \"standard (UTC) time\" is not yet implemented. Suggest to use UTC timezone in date_trunc."
);
}

// `with_x(0)` are infallible because `0` are always a valid
Ok(value.unwrap())
}
Expand Down Expand Up @@ -1685,6 +1693,21 @@ mod tests {
"2020-09-08T00:00:00+08",
],
),
(
vec![
"2023-10-28T23:00:00+00:00",
"2023-10-29T02:00:00+00:00",
"2024-10-26T23:00:00+00:00",
"2024-10-27T02:00:00+00:00",
],
Some("Europe/Berlin".into()),
vec![
"2023-10-29T00:00:00+02:00",
"2023-10-29T00:00:00+02:00",
"2024-10-27T00:00:00+02:00",
"2024-10-27T00:00:00+02:00",
],
),
];

cases.iter().for_each(|(original, tz_opt, expected)| {
Expand Down Expand Up @@ -1716,6 +1739,48 @@ mod tests {
});
}

#[test]
fn test_date_trunc_timezones_with_error() {
let cases = vec![
// daylight saving time ends at 2023-10-29T01:00:00+00:00 in Europe/Berlin
(vec!["2023-10-29T00:00:00+00:00"], "second"),
(vec!["2023-10-29T00:00:00+00:00"], "minute"),
(vec!["2023-10-29T00:00:00+00:00"], "hour"),
(vec!["2023-10-29T00:00:00+00:00"], "day"),
(vec!["2023-10-29T00:00:00+00:00"], "week"),
(vec!["2023-10-29T00:00:00+00:00"], "month"),
(vec!["2023-10-29T00:00:00+00:00"], "quarter"),
(vec!["2023-10-29T00:00:00+00:00"], "year"),
// daylight saving time ends at 2024-10-27T01:00:00+00:00 in Europe/Berlin
(vec!["2024-10-27T00:00:00+00:00"], "second"),
(vec!["2024-10-27T00:00:00+00:00"], "minute"),
(vec!["2024-10-27T00:00:00+00:00"], "hour"),
(vec!["2024-10-27T00:00:00+00:00"], "day"),
(vec!["2024-10-27T00:00:00+00:00"], "week"),
(vec!["2024-10-27T00:00:00+00:00"], "month"),
(vec!["2024-10-27T00:00:00+00:00"], "quarter"),
(vec!["2024-10-27T00:00:00+00:00"], "year"),
];

cases.iter().for_each(|(original, granularity)| {
let input = original
.iter()
.map(|s| Some(string_to_timestamp_nanos(s).unwrap()))
.collect::<TimestampNanosecondArray>()
.with_timezone_opt("Europe/Berlin".into());
let result = date_trunc(&[
ColumnarValue::Scalar(ScalarValue::from(*granularity)),
ColumnarValue::Array(Arc::new(input)),
]);
assert!(result.is_err());

assert!(matches!(
result.unwrap_err(),
DataFusionError::NotImplemented(_)
));
});
}

#[test]
fn test_date_bin_single() {
use chrono::Duration;
Expand Down