diff --git a/datafusion/common/src/scalar/mod.rs b/datafusion/common/src/scalar/mod.rs index 2b758f4568760..4a58530edf9ee 100644 --- a/datafusion/common/src/scalar/mod.rs +++ b/datafusion/common/src/scalar/mod.rs @@ -3036,6 +3036,34 @@ impl ScalarValue { DataType::Timestamp(TimeUnit::Nanosecond, None), ) => ScalarValue::Int64(Some((float_ts * 1_000_000_000_f64).trunc() as i64)) .to_array()?, + ( + ScalarValue::Decimal128(Some(decimal_value), _, scale), + DataType::Timestamp(time_unit, None), + ) => { + let scale_factor = 10_i128.pow(*scale as u32); + let seconds = decimal_value / scale_factor; + let fraction = decimal_value % scale_factor; + + let timestamp_value = match time_unit { + TimeUnit::Second => ScalarValue::Int64(Some(seconds as i64)), + TimeUnit::Millisecond => { + let millis = seconds * 1_000 + (fraction * 1_000) / scale_factor; + ScalarValue::Int64(Some(millis as i64)) + } + TimeUnit::Microsecond => { + let micros = + seconds * 1_000_000 + (fraction * 1_000_000) / scale_factor; + ScalarValue::Int64(Some(micros as i64)) + } + TimeUnit::Nanosecond => { + let nanos = seconds * 1_000_000_000 + + (fraction * 1_000_000_000) / scale_factor; + ScalarValue::Int64(Some(nanos as i64)) + } + }; + + timestamp_value.to_array()? + } _ => self.to_array()?, }; diff --git a/datafusion/functions/src/datetime/to_timestamp.rs b/datafusion/functions/src/datetime/to_timestamp.rs index f1c61fe2b964d..52c86733f3327 100644 --- a/datafusion/functions/src/datetime/to_timestamp.rs +++ b/datafusion/functions/src/datetime/to_timestamp.rs @@ -18,15 +18,14 @@ use std::any::Any; use std::sync::Arc; +use crate::datetime::common::*; use arrow::datatypes::DataType::*; use arrow::datatypes::TimeUnit::{Microsecond, Millisecond, Nanosecond, Second}; use arrow::datatypes::{ ArrowTimestampType, DataType, TimeUnit, TimestampMicrosecondType, TimestampMillisecondType, TimestampNanosecondType, TimestampSecondType, }; - -use crate::datetime::common::*; -use datafusion_common::{exec_err, Result, ScalarType}; +use datafusion_common::{exec_err, Result, ScalarType, ScalarValue}; use datafusion_expr::{ ColumnarValue, Documentation, ScalarUDFImpl, Signature, Volatility, }; @@ -329,6 +328,30 @@ impl ScalarUDFImpl for ToTimestampFunc { Utf8View | LargeUtf8 | Utf8 => { to_timestamp_impl::(&args, "to_timestamp") } + Decimal128(_, _) => { + match &args[0] { + ColumnarValue::Scalar(ScalarValue::Decimal128( + Some(value), + _, + scale, + )) => { + // Convert decimal to seconds and nanoseconds + let scale_factor = 10_i128.pow(*scale as u32); + let seconds = value / scale_factor; + let fraction = value % scale_factor; + + let nanos = (fraction * 1_000_000_000) / scale_factor; + + let timestamp_nanos = seconds * 1_000_000_000 + nanos; + + Ok(ColumnarValue::Scalar(ScalarValue::TimestampNanosecond( + Some(timestamp_nanos as i64), + None, + ))) + } + _ => exec_err!("Invalid decimal value"), + } + } other => { exec_err!( "Unsupported data type {:?} for function to_timestamp", @@ -377,7 +400,7 @@ impl ScalarUDFImpl for ToTimestampSecondsFunc { } match args[0].data_type() { - Null | Int32 | Int64 | Timestamp(_, None) => { + Null | Int32 | Int64 | Timestamp(_, None) | Decimal128(_, _) => { args[0].cast_to(&Timestamp(Second, None), None) } Timestamp(_, Some(tz)) => args[0].cast_to(&Timestamp(Second, Some(tz)), None), diff --git a/datafusion/sqllogictest/test_files/timestamps.slt b/datafusion/sqllogictest/test_files/timestamps.slt index dcbcfbfa439d5..e042e3863b69f 100644 --- a/datafusion/sqllogictest/test_files/timestamps.slt +++ b/datafusion/sqllogictest/test_files/timestamps.slt @@ -416,6 +416,33 @@ SELECT to_timestamp(123456789.123456789) as c1, cast(123456789.123456789 as time ---- 1973-11-29T21:33:09.123456784 1973-11-29T21:33:09.123456784 1973-11-29T21:33:09.123456784 +# to_timestamp Decimal128 inputs + +query PPP +SELECT to_timestamp(arrow_cast(1.1, 'Decimal128(2,1)')) as c1, cast(arrow_cast(1.1, 'Decimal128(2,1)') as timestamp) as c2, arrow_cast(1.1, 'Decimal128(2,1)')::timestamp as c3; +---- +1970-01-01T00:00:01.100 1970-01-01T00:00:01.100 1970-01-01T00:00:01.100 + +query PPP +SELECT to_timestamp(arrow_cast(-1.1, 'Decimal128(2,1)')) as c1, cast(arrow_cast(-1.1, 'Decimal128(2,1)') as timestamp) as c2, arrow_cast(-1.1, 'Decimal128(2,1)')::timestamp as c3; +---- +1969-12-31T23:59:58.900 1969-12-31T23:59:58.900 1969-12-31T23:59:58.900 + +query PPP +SELECT to_timestamp(arrow_cast(0.0, 'Decimal128(2,1)')) as c1, cast(arrow_cast(0.0, 'Decimal128(2,1)') as timestamp) as c2, arrow_cast(0.0, 'Decimal128(2,1)')::timestamp as c3; +---- +1970-01-01T00:00:00 1970-01-01T00:00:00 1970-01-01T00:00:00 + +query PPP +SELECT to_timestamp(arrow_cast(1.23456789, 'Decimal128(9,8)')) as c1, cast(arrow_cast(1.23456789, 'Decimal128(9,8)') as timestamp) as c2, arrow_cast(1.23456789, 'Decimal128(9,8)')::timestamp as c3; +---- +1970-01-01T00:00:01.234567890 1970-01-01T00:00:01.234567890 1970-01-01T00:00:01.234567890 + +query PPP +SELECT to_timestamp(arrow_cast(123456789.123456789, 'Decimal128(18,9)')) as c1, cast(arrow_cast(123456789.123456789, 'Decimal128(18,9)') as timestamp) as c2, arrow_cast(123456789.123456789, 'Decimal128(18,9)')::timestamp as c3; +---- +1973-11-29T21:33:09.123456784 1973-11-29T21:33:09.123456784 1973-11-29T21:33:09.123456784 + # from_unixtime