@@ -42,26 +42,36 @@ fn secs_to_micros(secs: i64) -> i64 {
4242}
4343
4444/// Convert float seconds to microseconds
45- /// Returns None for NaN/Infinity/Overflow in non-ANSI mode, error in ANSI mode
45+ /// Returns None for NaN/Infinity in non-ANSI mode, error in ANSI mode
46+ /// Saturates to i64::MAX/MIN for overflow
4647#[ inline]
4748fn float_secs_to_micros ( val : f64 , enable_ansi_mode : bool ) -> Result < Option < i64 > > {
4849 if val. is_nan ( ) || val. is_infinite ( ) {
4950 if enable_ansi_mode {
50- return exec_err ! (
51- "Cannot cast {} to TIMESTAMP" ,
52- if val. is_nan( ) { "NaN" } else { "Infinity" }
53- ) ;
51+ let display_val = if val. is_nan ( ) {
52+ "NaN"
53+ } else if val. is_sign_positive ( ) {
54+ "Infinity"
55+ } else {
56+ "-Infinity"
57+ } ;
58+ return exec_err ! ( "Cannot cast {} to TIMESTAMP" , display_val) ;
5459 }
5560 return Ok ( None ) ;
5661 }
5762 let micros = val * MICROS_PER_SECOND as f64 ;
58- if micros > = i64:: MIN as f64 && micros < = i64:: MAX as f64 {
63+ if micros. floor ( ) < = i64:: MAX as f64 && micros. ceil ( ) > = i64:: MIN as f64 {
5964 Ok ( Some ( micros as i64 ) )
6065 } else {
6166 if enable_ansi_mode {
6267 return exec_err ! ( "Overflow casting {} to TIMESTAMP" , val) ;
6368 }
64- Ok ( None )
69+ // Saturate to i64::MAX or i64::MIN like Spark does for overflow
70+ if micros. is_sign_negative ( ) {
71+ Ok ( Some ( i64:: MIN ) )
72+ } else {
73+ Ok ( Some ( i64:: MAX ) )
74+ }
6575 }
6676}
6777
@@ -75,10 +85,11 @@ fn float_secs_to_micros(val: f64, enable_ansi_mode: bool) -> Result<Option<i64>>
7585/// ```
7686///
7787/// # Currently supported conversions
78- /// - Int8/Int16/Int32/Int64 -> Timestamp (target_type = 'timestamp')
88+ /// - Int8/Int16/Int32/Int64/Float32/Float64 -> Timestamp (target_type = 'timestamp')
7989///
8090/// The integer value is interpreted as seconds since the Unix epoch (1970-01-01 00:00:00 UTC)
81- /// and converted to a timestamp with microsecond precision (matches spark's spec)
91+ /// and converted to a timestamp with microsecond precision (matches spark's spec). Same is the case
92+ /// with Float but with higher precision to support micro / nanoseconds.
8293///
8394/// # Overflow behavior
8495/// Uses saturating multiplication to handle overflow - values that would overflow
@@ -905,7 +916,7 @@ mod tests {
905916
906917 #[ test]
907918 fn test_cast_float_overflow_non_ansi_mode ( ) {
908- // Value too large to fit in i64 microseconds - should return NULL in non-ANSI mode
919+ // Value too large to fit in i64 microseconds - should saturate to i64::MAX like Spark
909920 let cast = SparkCast :: new ( ) ;
910921 let large_value = 1e19 ; // Way too large for i64 microseconds
911922 let args = make_args_with_ansi_mode (
@@ -914,7 +925,23 @@ mod tests {
914925 false ,
915926 ) ;
916927 let result = cast. invoke_with_args ( args) . unwrap ( ) ;
917- assert_scalar_null ( result) ;
928+ // Spark saturates overflow to i64::MAX
929+ assert_scalar_timestamp ( result, i64:: MAX ) ;
930+ }
931+
932+ #[ test]
933+ fn test_cast_float_negative_overflow_non_ansi_mode ( ) {
934+ // Large negative value - should saturate to i64::MIN like Spark
935+ let cast = SparkCast :: new ( ) ;
936+ let large_value = -1e19 ; // Way too large negative for i64 microseconds
937+ let args = make_args_with_ansi_mode (
938+ ColumnarValue :: Scalar ( ScalarValue :: Float64 ( Some ( large_value) ) ) ,
939+ "timestamp" ,
940+ false ,
941+ ) ;
942+ let result = cast. invoke_with_args ( args) . unwrap ( ) ;
943+ // Spark saturates negative overflow to i64::MIN
944+ assert_scalar_timestamp ( result, i64:: MIN ) ;
918945 }
919946
920947 #[ test]
0 commit comments