Skip to content

fix: time ± interval returns a wrapped time instead of an interval#23279

Open
vismaytiwari wants to merge 1 commit into
apache:mainfrom
vismaytiwari:time-interval-returns-time
Open

fix: time ± interval returns a wrapped time instead of an interval#23279
vismaytiwari wants to merge 1 commit into
apache:mainfrom
vismaytiwari:time-interval-returns-time

Conversation

@vismaytiwari

Copy link
Copy Markdown

Which issue does this PR close?

Rationale for this change

time + interval (and interval + time / time - interval) returned an Interval instead of a time, so the value was never wrapped within the 24-hour clock:

> SELECT time '23:30' + interval '2 hours';
25 hours 30 mins   -- an Interval, not a time

PostgreSQL (and DuckDB) return a time that wraps around midnight:

01:30:00

The root cause is in type coercion: time <op> interval was coerced by widening the time operand into an Interval, so the addition happened between two intervals and the result kept the Interval type.

What changes are included in this PR?

Following the existing Date - Date special case (in the same two files), time ± interval is now handled explicitly:

  • Coercion (expr-common/src/type_coercion/binary.rs): time + interval, interval + time, and time - interval now coerce to (time, interval) with a time result type (the interval is normalized to MonthDayNano). interval - time, which is not meaningful, is left unchanged.
  • Evaluation (physical-expr/src/expressions/binary.rs): a new apply_time_interval adds/subtracts the interval's sub-day component and wraps the result modulo 24 hours. All four time units (Time32(Second|Millisecond), Time64(Microsecond|Nanosecond)) and both operand orders are supported. Only the interval's nanoseconds affect a time-of-day; whole months and days are ignored, matching PostgreSQL (e.g. time '10:00' + interval '1 day 2 hours' = 12:00:00).

time - time (→ Interval) is unchanged.

Are these changes tested?

Yes. datafusion/sqllogictest/test_files/datetime/arith_time_interval.slt was previously a characterization test that documented the incorrect Interval output; it now asserts the correct wrapped time results — including wrapping past midnight in both directions (22:00 + 3h → 01:00:00, 02:00 - 3h → 23:00:00) and ignoring whole days.

Are there any user-facing changes?

Yes — time ± interval now returns a time value (wrapped within 24 hours) instead of an Interval, aligning DataFusion with PostgreSQL and DuckDB.

@github-actions github-actions Bot added logical-expr Logical plan and expressions physical-expr Changes to the physical-expr crates sqllogictest SQL Logic Tests (.slt) labels Jul 1, 2026
`time + interval`, `interval + time`, and `time - interval` previously widened
the time operand into an interval, so `time '23:30' + interval '2 hours'`
returned a `25 hours 30 mins` interval rather than wrapping within the 24-hour
clock. Following the existing `Date - Date` special case, coercion now keeps the
result as `time`, and a new `apply_time_interval` kernel adds/subtracts the
interval's sub-day component modulo 24 hours to match PostgreSQL and DuckDB.
Whole months and days are ignored, and all four time units are supported.

Closes apache#22265
@vismaytiwari vismaytiwari force-pushed the time-interval-returns-time branch from 0a33c81 to 5b6be8b Compare July 1, 2026 13:38
@vismaytiwari

Copy link
Copy Markdown
Author

Fixed the rustfmt failure from the first run — fmt, the unit tests, and the datetime sqllogictests all pass locally. The workflows just need approval to run on the updated commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

logical-expr Logical plan and expressions physical-expr Changes to the physical-expr crates sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PostgreSQL compatibility: time + interval should wrap within the 24-hour time domain

1 participant