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]

Reply via email to