Joel Robin created SPARK-61087:
----------------------------------

             Summary: to_time, try_to_time and time_from_seconds return TIME(6) 
values with more than 6 fractional digits
                 Key: SPARK-61087
                 URL: https://issues.apache.org/jira/browse/SPARK-61087
             Project: Spark
          Issue Type: Bug
          Components: SQL
    Affects Versions: 5.0.0
         Environment: Apache Spark master branch, commit 
352ac9bf22806d6aa2047bf3c62096697d0afeb1 (5.0.0-SNAPSHOT). Local mode, Java 21.
            Reporter: Joel Robin


h3. Problem

The functions {{to_time}}, {{try_to_time}} and {{time_from_seconds}} return the 
type {{TIME(6)}}. But the values that they return can have 7, 8 or 9 fractional 
digits. A {{TIME(6)}} value must not have more than 6 fractional digits.

Other parts of Spark obey this rule. {{CAST}} truncates a value to the 
precision of the target type. The Parquet reader truncates a value to the 
precision of the column. These three functions do not truncate the value.

The {{TIME}} type is a preview feature. To use it, set 
{{spark.sql.timeType.enabled=true}}.

h3. Steps to reproduce

{code:sql}
SET spark.sql.timeType.enabled=true;

SELECT typeof(to_time('00:00:00.1234567')) AS type,
       to_time('00:00:00.1234567') AS to_time_v,
       try_to_time('00:00:00.123456789') AS try_to_time_v,
       time_from_seconds(0.0000001) AS from_secs_v;

SELECT CAST(TIME'00:00:00.1234567' AS TIME(6)) AS expected_time6;

SELECT to_time('00:00:00.1234567') = TIME'00:00:00.123456' AS eq,
       time_from_seconds(0.0000001) = TIME'00:00:00' AS eq2;

SELECT time_from_seconds(0.0000001) AS v UNION SELECT TIME'00:00:00';
{code}

h3. Actual and expected results

||Expression||Actual result||Expected result||
|{{typeof(to_time('00:00:00.1234567'))}}|{{time(6)}}|{{time(6)}}|
|{{to_time('00:00:00.1234567')}}|{{00:00:00.1234567}}|{{00:00:00.123456}}|
|{{try_to_time('00:00:00.123456789')}}|{{00:00:00.123456789}}|{{00:00:00.123456}}|
|{{time_from_seconds(0.0000001)}}|{{00:00:00.0000001}}|{{00:00:00}}|
|{{CAST(TIME'00:00:00.1234567' AS 
TIME(6))}}|{{00:00:00.123456}}|{{00:00:00.123456}}|
|{{eq}}|{{false}}|{{true}}|
|{{eq2}}|{{false}}|{{true}}|
|{{UNION}} query|2 rows|1 row|

This is not only a display problem. The collected {{java.time.LocalTime}} 
values have {{toNanoOfDay()}} equal to 123456700, 123456789 and 100.

h3. Other effects

1. The problem also occurs with a column argument, not only with a constant. It 
also occurs when you give a format, for example {{to_time('00:00:00.1234567', 
'HH:mm:ss.SSSSSSS')}}. It also occurs with a {{DOUBLE}} argument to 
{{time_from_seconds}}.
2. {{CAST(to_time(s) AS TIME(6))}} does not change the value, because the type 
of the input is already {{TIME(6)}}.
3. The result of a query can change with the optimizer rules. Use this view:
{code:sql}
CREATE OR REPLACE TEMP VIEW src AS
  SELECT CONCAT('00:00:00.123456', CAST(id + 7 AS STRING)) AS s FROM range(1);

SELECT CAST(to_time(s) AS TIME(7)) = TIME'00:00:00.1234567' FROM src;
{code}
The query returns {{false}}. Then set 
{{spark.sql.optimizer.excludedRules=org.apache.spark.sql.catalyst.optimizer.UnwrapCastInBinaryComparison}}.
 Now the same query returns {{true}}. This rule expects that a {{TIME(p)}} 
value has a maximum of p fractional digits.
4. A write to Parquet and a read from Parquet change the value. Before the 
write, {{time_from_seconds(0.0000001) = TIME'00:00:00'}} is {{false}}. Write 
the value to a {{TIME(6)}} column of a Parquet table, and read it again. Now 
the comparison is {{true}}.

h3. Cause

1. {{ToTime}} sets the result type to {{TimeType()}}, which is {{TIME(6)}}. 
{{ToTimeParser.parse}} returns the nanoseconds from the parser, and does not 
truncate them. {{try_to_time}} is {{TryEval(ToTime(...))}}, thus it has the 
same problem.
2. SPARK-57551 changed {{SparkDateTimeUtils.stringToTime}} to keep the digits 7 
to 9. That change added truncation to {{CAST}}, but not to {{ToTime}}. Before 
SPARK-57551, the default parser kept only microseconds. I found this in the 
diff. I did not run the earlier commit.
3. {{TimeFromBase}} sets the result type to {{TIME(6)}}. 
{{DateTimeUtils.timeFromSeconds(Decimal)}} and 
{{DateTimeUtils.timeFromSeconds(Double)}} multiply the seconds by 
{{NANOS_PER_SECOND}}. They do not truncate the result. This code has not 
changed since SPARK-54442.

h3. Possible fix

Truncate the result to the precision of the result type with 
{{DateTimeUtils.truncateTimeToPrecision}}. {{CAST}} uses the same function.

h3. Related issues

* SPARK-60547 reports the same problem for {{createDataFrame}}.
* The JSON, CSV and XML readers have a similar problem when the schema has a 
{{TIME(p)}} column. This example returns {{00:00:00.1234567}}:
{code:sql}
SELECT from_json('{"t":"00:00:00.1234567"}', 't TIME(6)').t;
{code}
This ticket does not include these readers.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to