Skip to content

Commit a81ab3a

Browse files
authored
Decimal type support for to_timestamp (#15486)
* wip: decimal type support for to_timestamp * updated timestamp handling for cast operator * fixed clippy error * fixed fmt * updated timestamps.slt file for decimal128 type support
1 parent 087fdc3 commit a81ab3a

File tree

3 files changed

+82
-4
lines changed

3 files changed

+82
-4
lines changed

datafusion/common/src/scalar/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3036,6 +3036,34 @@ impl ScalarValue {
30363036
DataType::Timestamp(TimeUnit::Nanosecond, None),
30373037
) => ScalarValue::Int64(Some((float_ts * 1_000_000_000_f64).trunc() as i64))
30383038
.to_array()?,
3039+
(
3040+
ScalarValue::Decimal128(Some(decimal_value), _, scale),
3041+
DataType::Timestamp(time_unit, None),
3042+
) => {
3043+
let scale_factor = 10_i128.pow(*scale as u32);
3044+
let seconds = decimal_value / scale_factor;
3045+
let fraction = decimal_value % scale_factor;
3046+
3047+
let timestamp_value = match time_unit {
3048+
TimeUnit::Second => ScalarValue::Int64(Some(seconds as i64)),
3049+
TimeUnit::Millisecond => {
3050+
let millis = seconds * 1_000 + (fraction * 1_000) / scale_factor;
3051+
ScalarValue::Int64(Some(millis as i64))
3052+
}
3053+
TimeUnit::Microsecond => {
3054+
let micros =
3055+
seconds * 1_000_000 + (fraction * 1_000_000) / scale_factor;
3056+
ScalarValue::Int64(Some(micros as i64))
3057+
}
3058+
TimeUnit::Nanosecond => {
3059+
let nanos = seconds * 1_000_000_000
3060+
+ (fraction * 1_000_000_000) / scale_factor;
3061+
ScalarValue::Int64(Some(nanos as i64))
3062+
}
3063+
};
3064+
3065+
timestamp_value.to_array()?
3066+
}
30393067
_ => self.to_array()?,
30403068
};
30413069

datafusion/functions/src/datetime/to_timestamp.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,14 @@
1818
use std::any::Any;
1919
use std::sync::Arc;
2020

21+
use crate::datetime::common::*;
2122
use arrow::datatypes::DataType::*;
2223
use arrow::datatypes::TimeUnit::{Microsecond, Millisecond, Nanosecond, Second};
2324
use arrow::datatypes::{
2425
ArrowTimestampType, DataType, TimeUnit, TimestampMicrosecondType,
2526
TimestampMillisecondType, TimestampNanosecondType, TimestampSecondType,
2627
};
27-
28-
use crate::datetime::common::*;
29-
use datafusion_common::{exec_err, Result, ScalarType};
28+
use datafusion_common::{exec_err, Result, ScalarType, ScalarValue};
3029
use datafusion_expr::{
3130
ColumnarValue, Documentation, ScalarUDFImpl, Signature, Volatility,
3231
};
@@ -329,6 +328,30 @@ impl ScalarUDFImpl for ToTimestampFunc {
329328
Utf8View | LargeUtf8 | Utf8 => {
330329
to_timestamp_impl::<TimestampNanosecondType>(&args, "to_timestamp")
331330
}
331+
Decimal128(_, _) => {
332+
match &args[0] {
333+
ColumnarValue::Scalar(ScalarValue::Decimal128(
334+
Some(value),
335+
_,
336+
scale,
337+
)) => {
338+
// Convert decimal to seconds and nanoseconds
339+
let scale_factor = 10_i128.pow(*scale as u32);
340+
let seconds = value / scale_factor;
341+
let fraction = value % scale_factor;
342+
343+
let nanos = (fraction * 1_000_000_000) / scale_factor;
344+
345+
let timestamp_nanos = seconds * 1_000_000_000 + nanos;
346+
347+
Ok(ColumnarValue::Scalar(ScalarValue::TimestampNanosecond(
348+
Some(timestamp_nanos as i64),
349+
None,
350+
)))
351+
}
352+
_ => exec_err!("Invalid decimal value"),
353+
}
354+
}
332355
other => {
333356
exec_err!(
334357
"Unsupported data type {:?} for function to_timestamp",
@@ -377,7 +400,7 @@ impl ScalarUDFImpl for ToTimestampSecondsFunc {
377400
}
378401

379402
match args[0].data_type() {
380-
Null | Int32 | Int64 | Timestamp(_, None) => {
403+
Null | Int32 | Int64 | Timestamp(_, None) | Decimal128(_, _) => {
381404
args[0].cast_to(&Timestamp(Second, None), None)
382405
}
383406
Timestamp(_, Some(tz)) => args[0].cast_to(&Timestamp(Second, Some(tz)), None),

datafusion/sqllogictest/test_files/timestamps.slt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,33 @@ SELECT to_timestamp(123456789.123456789) as c1, cast(123456789.123456789 as time
416416
----
417417
1973-11-29T21:33:09.123456784 1973-11-29T21:33:09.123456784 1973-11-29T21:33:09.123456784
418418

419+
# to_timestamp Decimal128 inputs
420+
421+
query PPP
422+
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;
423+
----
424+
1970-01-01T00:00:01.100 1970-01-01T00:00:01.100 1970-01-01T00:00:01.100
425+
426+
query PPP
427+
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;
428+
----
429+
1969-12-31T23:59:58.900 1969-12-31T23:59:58.900 1969-12-31T23:59:58.900
430+
431+
query PPP
432+
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;
433+
----
434+
1970-01-01T00:00:00 1970-01-01T00:00:00 1970-01-01T00:00:00
435+
436+
query PPP
437+
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;
438+
----
439+
1970-01-01T00:00:01.234567890 1970-01-01T00:00:01.234567890 1970-01-01T00:00:01.234567890
440+
441+
query PPP
442+
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;
443+
----
444+
1973-11-29T21:33:09.123456784 1973-11-29T21:33:09.123456784 1973-11-29T21:33:09.123456784
445+
419446

420447
# from_unixtime
421448

0 commit comments

Comments
 (0)