ykisana opened a new pull request, #59725:
URL: https://github.com/apache/spark/pull/59725
### What changes were proposed in this pull request?
`IntervalUtils.parseSecondNano` took the sign of the fractional part from
the parsed whole seconds:
```scala
val seconds = parseSeconds(secondsStr)
Math.addExact(seconds, parseNanos(nanosStr, seconds < 0))
```
When casting a string to `INTERVAL SECOND`, a negative input such as `-0.5`
reaches this method as `"-0.5"`. Its whole seconds parse to `0`, so `seconds <
0` is false and the fraction is added as a positive value. This PR takes the
sign from the seconds string instead (`secondsStr.startsWith("-")`). For any
input whose whole seconds are non-zero, the result is unchanged.
The other callers of `parseSecondNano` (`toDTInterval` for `... TO SECOND`
types) pass unsigned seconds and apply the sign outside, so they are not
affected.
### Why are the changes needed?
Every value between -1 and 0 seconds loses its sign when cast from a string:
```sql
SELECT CAST('-0.5' AS INTERVAL SECOND);
-- before: INTERVAL '00.5' SECOND
-- after: INTERVAL '-00.5' SECOND
```
Spark formats such an interval as `INTERVAL '-00.5' SECOND`, and the JSON
and CSV readers parse `INTERVAL SECOND` with the same code. As a result,
writing `-0.5` seconds to JSON or CSV and reading it back returns `+0.5`
seconds.
### Does this PR introduce _any_ user-facing change?
Yes, it fixes a bug. Casting a string to `INTERVAL SECOND` now keeps the
sign of values between -1 and 0 seconds, as the `INTERVAL '-0.5' SECOND`
literal already does. JSON and CSV round trips of such values are fixed as
well. The bug reproduces on 3.5.8, 4.0.0, 4.1.1 and 4.2.0.
### How was this patch tested?
Added `SPARK-60484: Cast string to INTERVAL SECOND keeps the sign of values
in (-1, 0)` to `CastSuiteBase`. It covers:
- plain strings (`-0.5`, `-00.5`, `-0.000001`, `-0.999999999`, and `0.5` as
a positive control);
- the literal forms (`INTERVAL '-0.5' SECOND`, `INTERVAL -'0.5' SECOND`,
`INTERVAL -'-0.5' SECOND`);
- an interval -> string -> interval round trip.
Without the fix, the new test fails in all three suites with `cast(-0.5 as
interval second), actual: 500000, expected: -500000`.
```
build/sbt 'catalyst/testOnly *CastWithAnsiOffSuite *CastWithAnsiOnSuite
*TryCastSuite *IntervalUtilsSuite'
```
All 458 tests in these four suites pass with the fix. Scalastyle (`build/sbt
catalyst/scalastyle catalyst/Test/scalastyle`) reports 0 errors.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 5.5)
Fix, tests, PR description, and comments by Claude.
All code manually reviewed by @ykisana.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]