Commit 1e58928
authored
## Which issue does this PR close?
- Closes #22265
- Closes #22255
## 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)` — the interval normalized to `MonthDayNano`, and the
time operand kept at its own unit, which is also the result type.
`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, for all four time units
(`Time32(Second|Millisecond)`, `Time64(Microsecond|Nanosecond)`) and
both operand orders. The result **keeps the input time's unit**,
mirroring `timestamp/date + interval` (which preserve their unit and
apply the interval at that resolution); interval precision finer than
the time's unit is truncated, so `time(s) + interval '1 nanosecond'` is
a no-op, exactly like `timestamp(s) + interval '1 nanosecond'`. Only the
interval's sub-day portion affects 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`), ignoring whole days, preserving
each input time unit (`arrow_typeof` per unit), and the finer-than-unit
interval truncation.
## 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. The result keeps the input time's unit rather than widening it
(see the 55.0.0 upgrade guide).
1 parent d5703bd commit 1e58928
4 files changed
Lines changed: 347 additions & 26 deletions
File tree
- datafusion
- expr-common/src/type_coercion
- physical-expr/src/expressions
- sqllogictest/test_files/datetime
- docs/source/library-user-guide/upgrading
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
267 | 267 | | |
268 | 268 | | |
269 | 269 | | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
270 | 287 | | |
271 | 288 | | |
272 | 289 | | |
| |||
362 | 379 | | |
363 | 380 | | |
364 | 381 | | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
365 | 399 | | |
366 | 400 | | |
367 | 401 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
271 | 271 | | |
272 | 272 | | |
273 | 273 | | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
274 | 457 | | |
275 | 458 | | |
276 | 459 | | |
| |||
353 | 536 | | |
354 | 537 | | |
355 | 538 | | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
356 | 551 | | |
357 | 552 | | |
358 | 553 | | |
| |||
0 commit comments