This is an automated email from the ASF dual-hosted git repository.
github-bot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git
The following commit(s) were added to refs/heads/main by this push:
new 41a0b85afa Add support for additional numeric types in to_timestamp
functions (#19663)
41a0b85afa is described below
commit 41a0b85afaa3c3ece38d59d3ceed36d62ca5e889
Author: Goksel Kabadayi <[email protected]>
AuthorDate: Sun Jan 11 06:25:26 2026 +0300
Add support for additional numeric types in to_timestamp functions (#19663)
## Which issue does this PR close?
Closes #19117.
## Rationale for this change
The `to_timestamp` function family lacks consistency in supported
argument types. While `to_timestamp` accepts Float64 and Decimal128
types, the related functions (`to_timestamp_seconds`,
`to_timestamp_millis`, `to_timestamp_micros`, `to_timestamp_nanos`)
don't offer the same support. Additionally, the documentation claims
support for "unsigned integer" types, but this isn't fully implemented.
## What changes are included in this PR?
Standardizes all `to_timestamp` variants to uniformly support:
- All signed integer types: Int8, Int16, Int32, Int64
- All unsigned integer types: UInt8, UInt16, UInt32, UInt64
- Float32 and Float64
- Decimal128 (for millis/micros/nanos variants)
## Are these changes tested?
Yes, added comprehensive SQL logic tests in
`datafusion/sqllogictest/test_files/datetime/timestamps.slt`.
## Are there any user-facing changes?
Users can now pass additional numeric types to `to_timestamp` functions.
This is a backward-compatible enhancement.
---
datafusion/functions/src/datetime/to_timestamp.rs | 197 +++--
.../test_files/datetime/timestamps.slt | 832 +++++++++++++++++++++
2 files changed, 987 insertions(+), 42 deletions(-)
diff --git a/datafusion/functions/src/datetime/to_timestamp.rs
b/datafusion/functions/src/datetime/to_timestamp.rs
index 58077694b0..1c5d3dbd88 100644
--- a/datafusion/functions/src/datetime/to_timestamp.rs
+++ b/datafusion/functions/src/datetime/to_timestamp.rs
@@ -19,8 +19,11 @@ use std::any::Any;
use std::sync::Arc;
use crate::datetime::common::*;
-use arrow::array::Float64Array;
use arrow::array::timezone::Tz;
+use arrow::array::{
+ Array, Decimal128Array, Float16Array, Float32Array, Float64Array,
+ TimestampNanosecondArray,
+};
use arrow::datatypes::DataType::*;
use arrow::datatypes::TimeUnit::{Microsecond, Millisecond, Nanosecond, Second};
use arrow::datatypes::{
@@ -28,7 +31,6 @@ use arrow::datatypes::{
TimestampNanosecondType, TimestampSecondType,
};
use datafusion_common::config::ConfigOptions;
-use datafusion_common::format::DEFAULT_CAST_OPTIONS;
use datafusion_common::{Result, ScalarType, ScalarValue, exec_err};
use datafusion_expr::{
ColumnarValue, Documentation, ScalarUDF, ScalarUDFImpl, Signature,
Volatility,
@@ -325,6 +327,45 @@ impl_to_timestamp_constructors!(ToTimestampMillisFunc);
impl_to_timestamp_constructors!(ToTimestampMicrosFunc);
impl_to_timestamp_constructors!(ToTimestampNanosFunc);
+fn decimal_to_nanoseconds(value: i128, scale: i8) -> i64 {
+ let nanos_exponent = 9_i16 - scale as i16;
+ let timestamp_nanos = if nanos_exponent >= 0 {
+ value * 10_i128.pow(nanos_exponent as u32)
+ } else {
+ value / 10_i128.pow(nanos_exponent.unsigned_abs() as u32)
+ };
+ timestamp_nanos as i64
+}
+
+fn decimal128_to_timestamp_nanos(
+ arg: &ColumnarValue,
+ tz: Option<Arc<str>>,
+) -> Result<ColumnarValue> {
+ match arg {
+ ColumnarValue::Scalar(ScalarValue::Decimal128(Some(value), _, scale))
=> {
+ let timestamp_nanos = decimal_to_nanoseconds(*value, *scale);
+ Ok(ColumnarValue::Scalar(ScalarValue::TimestampNanosecond(
+ Some(timestamp_nanos),
+ tz,
+ )))
+ }
+ ColumnarValue::Scalar(ScalarValue::Decimal128(None, _, _)) => Ok(
+ ColumnarValue::Scalar(ScalarValue::TimestampNanosecond(None, tz)),
+ ),
+ ColumnarValue::Array(arr) => {
+ let decimal_arr = downcast_arg!(arr, Decimal128Array);
+ let scale = decimal_arr.scale();
+ let result: TimestampNanosecondArray = decimal_arr
+ .iter()
+ .map(|v| v.map(|val| decimal_to_nanoseconds(val, scale)))
+ .collect();
+ let result = result.with_timezone_opt(tz);
+ Ok(ColumnarValue::Array(Arc::new(result)))
+ }
+ _ => exec_err!("Invalid Decimal128 value for to_timestamp"),
+ }
+}
+
/// to_timestamp SQL function
///
/// Note: `to_timestamp` returns `Timestamp(Nanosecond)` though its arguments
are interpreted as **seconds**.
@@ -380,48 +421,39 @@ impl ScalarUDFImpl for ToTimestampFunc {
let tz = self.timezone.clone();
match args[0].data_type() {
- Int32 | Int64 => args[0]
+ Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64 =>
args[0]
.cast_to(&Timestamp(Second, None), None)?
.cast_to(&Timestamp(Nanosecond, tz), None),
Null | Timestamp(_, _) => args[0].cast_to(&Timestamp(Nanosecond,
tz), None),
+ Float16 => {
+ let arr = args[0].to_array(1)?;
+ let f16_arr = downcast_arg!(&arr, Float16Array);
+ let result: TimestampNanosecondArray =
+ f16_arr.unary(|x| (x.to_f64() * 1_000_000_000.0) as i64);
+
Ok(ColumnarValue::Array(Arc::new(result.with_timezone_opt(tz))))
+ }
+ Float32 => {
+ let arr = args[0].to_array(1)?;
+ let f32_arr = downcast_arg!(&arr, Float32Array);
+ let result: TimestampNanosecondArray =
+ f32_arr.unary(|x| (x as f64 * 1_000_000_000.0) as i64);
+
Ok(ColumnarValue::Array(Arc::new(result.with_timezone_opt(tz))))
+ }
Float64 => {
- let rescaled = arrow::compute::kernels::numeric::mul(
- &args[0].to_array(1)?,
- &arrow::array::Scalar::new(Float64Array::from(vec![
- 1_000_000_000f64,
- ])),
- )?;
- Ok(ColumnarValue::Array(arrow::compute::cast_with_options(
- &rescaled,
- &Timestamp(Nanosecond, tz),
- &DEFAULT_CAST_OPTIONS,
- )?))
+ let arr = args[0].to_array(1)?;
+ let f64_arr = downcast_arg!(&arr, Float64Array);
+ let result: TimestampNanosecondArray =
+ f64_arr.unary(|x| (x * 1_000_000_000.0) as i64);
+
Ok(ColumnarValue::Array(Arc::new(result.with_timezone_opt(tz))))
+ }
+ Decimal32(_, _) | Decimal64(_, _) | Decimal256(_, _) => {
+ let arg = args[0].cast_to(&Decimal128(38, 9), None)?;
+ decimal128_to_timestamp_nanos(&arg, tz)
}
+ Decimal128(_, _) => decimal128_to_timestamp_nanos(&args[0], tz),
Utf8View | LargeUtf8 | Utf8 => {
to_timestamp_impl::<TimestampNanosecondType>(&args,
"to_timestamp", &tz)
}
- 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),
- tz,
- )))
- }
- _ => exec_err!("Invalid decimal value"),
- }
- }
other => {
exec_err!("Unsupported data type {other} for function
to_timestamp")
}
@@ -473,9 +505,23 @@ impl ScalarUDFImpl for ToTimestampSecondsFunc {
let tz = self.timezone.clone();
match args[0].data_type() {
- Null | Int32 | Int64 | Timestamp(_, _) | Decimal128(_, _) => {
- args[0].cast_to(&Timestamp(Second, tz), None)
- }
+ Null
+ | Int8
+ | Int16
+ | Int32
+ | Int64
+ | UInt8
+ | UInt16
+ | UInt32
+ | UInt64
+ | Timestamp(_, _)
+ | Decimal32(_, _)
+ | Decimal64(_, _)
+ | Decimal128(_, _)
+ | Decimal256(_, _) => args[0].cast_to(&Timestamp(Second, tz),
None),
+ Float16 | Float32 | Float64 => args[0]
+ .cast_to(&Int64, None)?
+ .cast_to(&Timestamp(Second, tz), None),
Utf8View | LargeUtf8 | Utf8 =>
to_timestamp_impl::<TimestampSecondType>(
&args,
"to_timestamp_seconds",
@@ -533,9 +579,25 @@ impl ScalarUDFImpl for ToTimestampMillisFunc {
}
match args[0].data_type() {
- Null | Int32 | Int64 | Timestamp(_, _) => {
+ Null
+ | Int8
+ | Int16
+ | Int32
+ | Int64
+ | UInt8
+ | UInt16
+ | UInt32
+ | UInt64
+ | Timestamp(_, _)
+ | Decimal32(_, _)
+ | Decimal64(_, _)
+ | Decimal128(_, _)
+ | Decimal256(_, _) => {
args[0].cast_to(&Timestamp(Millisecond,
self.timezone.clone()), None)
}
+ Float16 | Float32 | Float64 => args[0]
+ .cast_to(&Int64, None)?
+ .cast_to(&Timestamp(Millisecond, self.timezone.clone()), None),
Utf8View | LargeUtf8 | Utf8 =>
to_timestamp_impl::<TimestampMillisecondType>(
&args,
"to_timestamp_millis",
@@ -593,9 +655,25 @@ impl ScalarUDFImpl for ToTimestampMicrosFunc {
}
match args[0].data_type() {
- Null | Int32 | Int64 | Timestamp(_, _) => {
+ Null
+ | Int8
+ | Int16
+ | Int32
+ | Int64
+ | UInt8
+ | UInt16
+ | UInt32
+ | UInt64
+ | Timestamp(_, _)
+ | Decimal32(_, _)
+ | Decimal64(_, _)
+ | Decimal128(_, _)
+ | Decimal256(_, _) => {
args[0].cast_to(&Timestamp(Microsecond,
self.timezone.clone()), None)
}
+ Float16 | Float32 | Float64 => args[0]
+ .cast_to(&Int64, None)?
+ .cast_to(&Timestamp(Microsecond, self.timezone.clone()), None),
Utf8View | LargeUtf8 | Utf8 =>
to_timestamp_impl::<TimestampMicrosecondType>(
&args,
"to_timestamp_micros",
@@ -653,9 +731,25 @@ impl ScalarUDFImpl for ToTimestampNanosFunc {
}
match args[0].data_type() {
- Null | Int32 | Int64 | Timestamp(_, _) => {
+ Null
+ | Int8
+ | Int16
+ | Int32
+ | Int64
+ | UInt8
+ | UInt16
+ | UInt32
+ | UInt64
+ | Timestamp(_, _)
+ | Decimal32(_, _)
+ | Decimal64(_, _)
+ | Decimal128(_, _)
+ | Decimal256(_, _) => {
args[0].cast_to(&Timestamp(Nanosecond, self.timezone.clone()),
None)
}
+ Float16 | Float32 | Float64 => args[0]
+ .cast_to(&Int64, None)?
+ .cast_to(&Timestamp(Nanosecond, self.timezone.clone()), None),
Utf8View | LargeUtf8 | Utf8 =>
to_timestamp_impl::<TimestampNanosecondType>(
&args,
"to_timestamp_nanos",
@@ -1735,4 +1829,23 @@ mod tests {
assert_contains!(actual, expected);
}
}
+
+ #[test]
+ fn test_decimal_to_nanoseconds_negative_scale() {
+ // scale -2: internal value 5 represents 5 * 10^2 = 500 seconds
+ let nanos = decimal_to_nanoseconds(5, -2);
+ assert_eq!(nanos, 500_000_000_000); // 500 seconds in nanoseconds
+
+ // scale -1: internal value 10 represents 10 * 10^1 = 100 seconds
+ let nanos = decimal_to_nanoseconds(10, -1);
+ assert_eq!(nanos, 100_000_000_000);
+
+ // scale 0: internal value 5 represents 5 seconds
+ let nanos = decimal_to_nanoseconds(5, 0);
+ assert_eq!(nanos, 5_000_000_000);
+
+ // scale 3: internal value 1500 represents 1.5 seconds
+ let nanos = decimal_to_nanoseconds(1500, 3);
+ assert_eq!(nanos, 1_500_000_000);
+ }
}
diff --git a/datafusion/sqllogictest/test_files/datetime/timestamps.slt
b/datafusion/sqllogictest/test_files/datetime/timestamps.slt
index 1b80bfdaad..efa7a536c8 100644
--- a/datafusion/sqllogictest/test_files/datetime/timestamps.slt
+++ b/datafusion/sqllogictest/test_files/datetime/timestamps.slt
@@ -4475,6 +4475,838 @@ FROM (VALUES
1970-01-01T00:00:00.000000005Z
+##########
+## to_timestamp functions with all numeric types
+##########
+
+# Test to_timestamp with all integer types
+# Int8
+query P
+SELECT to_timestamp(arrow_cast(0, 'Int8'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp(arrow_cast(100, 'Int8'));
+----
+1970-01-01T00:01:40
+
+# Int16
+query P
+SELECT to_timestamp(arrow_cast(0, 'Int16'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp(arrow_cast(1000, 'Int16'));
+----
+1970-01-01T00:16:40
+
+# Int32
+query P
+SELECT to_timestamp(arrow_cast(0, 'Int32'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp(arrow_cast(86400, 'Int32'));
+----
+1970-01-02T00:00:00
+
+# Int64
+query P
+SELECT to_timestamp(arrow_cast(0, 'Int64'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp(arrow_cast(86400, 'Int64'));
+----
+1970-01-02T00:00:00
+
+# UInt8
+query P
+SELECT to_timestamp(arrow_cast(0, 'UInt8'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp(arrow_cast(100, 'UInt8'));
+----
+1970-01-01T00:01:40
+
+# UInt16
+query P
+SELECT to_timestamp(arrow_cast(0, 'UInt16'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp(arrow_cast(1000, 'UInt16'));
+----
+1970-01-01T00:16:40
+
+# UInt32
+query P
+SELECT to_timestamp(arrow_cast(0, 'UInt32'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp(arrow_cast(86400, 'UInt32'));
+----
+1970-01-02T00:00:00
+
+# UInt64
+query P
+SELECT to_timestamp(arrow_cast(0, 'UInt64'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp(arrow_cast(86400, 'UInt64'));
+----
+1970-01-02T00:00:00
+
+# Float16
+query P
+SELECT to_timestamp(arrow_cast(0.0, 'Float16'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp(arrow_cast(1.5, 'Float16'));
+----
+1970-01-01T00:00:01.500
+
+# Float32
+query P
+SELECT to_timestamp(arrow_cast(0.0, 'Float32'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp(arrow_cast(1.5, 'Float32'));
+----
+1970-01-01T00:00:01.500
+
+# Float64
+query P
+SELECT to_timestamp(arrow_cast(0.0, 'Float64'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp(arrow_cast(1.5, 'Float64'));
+----
+1970-01-01T00:00:01.500
+
+# Test to_timestamp_seconds with all integer types
+# Int8
+query P
+SELECT to_timestamp_seconds(arrow_cast(0, 'Int8'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp_seconds(arrow_cast(100, 'Int8'));
+----
+1970-01-01T00:01:40
+
+# Int16
+query P
+SELECT to_timestamp_seconds(arrow_cast(1000, 'Int16'));
+----
+1970-01-01T00:16:40
+
+# Int32
+query P
+SELECT to_timestamp_seconds(arrow_cast(86400, 'Int32'));
+----
+1970-01-02T00:00:00
+
+# Int64
+query P
+SELECT to_timestamp_seconds(arrow_cast(86400, 'Int64'));
+----
+1970-01-02T00:00:00
+
+# UInt8
+query P
+SELECT to_timestamp_seconds(arrow_cast(100, 'UInt8'));
+----
+1970-01-01T00:01:40
+
+# UInt16
+query P
+SELECT to_timestamp_seconds(arrow_cast(1000, 'UInt16'));
+----
+1970-01-01T00:16:40
+
+# UInt32
+query P
+SELECT to_timestamp_seconds(arrow_cast(86400, 'UInt32'));
+----
+1970-01-02T00:00:00
+
+# UInt64
+query P
+SELECT to_timestamp_seconds(arrow_cast(86400, 'UInt64'));
+----
+1970-01-02T00:00:00
+
+# Float16
+query P
+SELECT to_timestamp_seconds(arrow_cast(1.9, 'Float16'));
+----
+1970-01-01T00:00:01
+
+# Float32
+query P
+SELECT to_timestamp_seconds(arrow_cast(1.9, 'Float32'));
+----
+1970-01-01T00:00:01
+
+# Float64
+query P
+SELECT to_timestamp_seconds(arrow_cast(1.9, 'Float64'));
+----
+1970-01-01T00:00:01
+
+# Test to_timestamp_millis with all integer types
+# Int8
+query P
+SELECT to_timestamp_millis(arrow_cast(0, 'Int8'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp_millis(arrow_cast(100, 'Int8'));
+----
+1970-01-01T00:00:00.100
+
+# Int16
+query P
+SELECT to_timestamp_millis(arrow_cast(1000, 'Int16'));
+----
+1970-01-01T00:00:01
+
+# Int32
+query P
+SELECT to_timestamp_millis(arrow_cast(86400000, 'Int32'));
+----
+1970-01-02T00:00:00
+
+# Int64
+query P
+SELECT to_timestamp_millis(arrow_cast(86400000, 'Int64'));
+----
+1970-01-02T00:00:00
+
+# UInt8
+query P
+SELECT to_timestamp_millis(arrow_cast(100, 'UInt8'));
+----
+1970-01-01T00:00:00.100
+
+# UInt16
+query P
+SELECT to_timestamp_millis(arrow_cast(1000, 'UInt16'));
+----
+1970-01-01T00:00:01
+
+# UInt32
+query P
+SELECT to_timestamp_millis(arrow_cast(86400000, 'UInt32'));
+----
+1970-01-02T00:00:00
+
+# UInt64
+query P
+SELECT to_timestamp_millis(arrow_cast(86400000, 'UInt64'));
+----
+1970-01-02T00:00:00
+
+# Float16
+query P
+SELECT to_timestamp_millis(arrow_cast(1000, 'Float16'));
+----
+1970-01-01T00:00:01
+
+# Float32
+query P
+SELECT to_timestamp_millis(arrow_cast(1000.9, 'Float32'));
+----
+1970-01-01T00:00:01
+
+# Float64
+query P
+SELECT to_timestamp_millis(arrow_cast(1000.9, 'Float64'));
+----
+1970-01-01T00:00:01
+
+# Test to_timestamp_micros with all integer types
+# Int8
+query P
+SELECT to_timestamp_micros(arrow_cast(0, 'Int8'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp_micros(arrow_cast(100, 'Int8'));
+----
+1970-01-01T00:00:00.000100
+
+# Int16
+query P
+SELECT to_timestamp_micros(arrow_cast(1000, 'Int16'));
+----
+1970-01-01T00:00:00.001
+
+# Int32
+query P
+SELECT to_timestamp_micros(arrow_cast(1000000, 'Int32'));
+----
+1970-01-01T00:00:01
+
+# Int64
+query P
+SELECT to_timestamp_micros(arrow_cast(86400000000, 'Int64'));
+----
+1970-01-02T00:00:00
+
+# UInt8
+query P
+SELECT to_timestamp_micros(arrow_cast(100, 'UInt8'));
+----
+1970-01-01T00:00:00.000100
+
+# UInt16
+query P
+SELECT to_timestamp_micros(arrow_cast(1000, 'UInt16'));
+----
+1970-01-01T00:00:00.001
+
+# UInt32
+query P
+SELECT to_timestamp_micros(arrow_cast(1000000, 'UInt32'));
+----
+1970-01-01T00:00:01
+
+# UInt64
+query P
+SELECT to_timestamp_micros(arrow_cast(1000000, 'UInt64'));
+----
+1970-01-01T00:00:01
+
+# Float16
+query P
+SELECT to_timestamp_micros(arrow_cast(1000, 'Float16'));
+----
+1970-01-01T00:00:00.001
+
+# Float32
+query P
+SELECT to_timestamp_micros(arrow_cast(1000000.9, 'Float32'));
+----
+1970-01-01T00:00:01
+
+# Float64
+query P
+SELECT to_timestamp_micros(arrow_cast(1000000.9, 'Float64'));
+----
+1970-01-01T00:00:01
+
+# Test to_timestamp_nanos with all integer types
+# Int8
+query P
+SELECT to_timestamp_nanos(arrow_cast(0, 'Int8'));
+----
+1970-01-01T00:00:00
+
+query P
+SELECT to_timestamp_nanos(arrow_cast(100, 'Int8'));
+----
+1970-01-01T00:00:00.000000100
+
+# Int16
+query P
+SELECT to_timestamp_nanos(arrow_cast(1000, 'Int16'));
+----
+1970-01-01T00:00:00.000001
+
+# Int32
+query P
+SELECT to_timestamp_nanos(arrow_cast(1000000000, 'Int32'));
+----
+1970-01-01T00:00:01
+
+# Int64
+query P
+SELECT to_timestamp_nanos(arrow_cast(86400000000000, 'Int64'));
+----
+1970-01-02T00:00:00
+
+# UInt8
+query P
+SELECT to_timestamp_nanos(arrow_cast(100, 'UInt8'));
+----
+1970-01-01T00:00:00.000000100
+
+# UInt16
+query P
+SELECT to_timestamp_nanos(arrow_cast(1000, 'UInt16'));
+----
+1970-01-01T00:00:00.000001
+
+# UInt32
+query P
+SELECT to_timestamp_nanos(arrow_cast(1000000000, 'UInt32'));
+----
+1970-01-01T00:00:01
+
+# UInt64
+query P
+SELECT to_timestamp_nanos(arrow_cast(1000000000, 'UInt64'));
+----
+1970-01-01T00:00:01
+
+# Float16
+query P
+SELECT to_timestamp_nanos(arrow_cast(1000, 'Float16'));
+----
+1970-01-01T00:00:00.000001
+
+# Float32
+query P
+SELECT to_timestamp_nanos(arrow_cast(1000000000.9, 'Float32'));
+----
+1970-01-01T00:00:01
+
+# Float64
+query P
+SELECT to_timestamp_nanos(arrow_cast(1000000000.9, 'Float64'));
+----
+1970-01-01T00:00:01
+
+# Verify arrow_typeof for all to_timestamp functions with various input types
+query T
+SELECT arrow_typeof(to_timestamp(arrow_cast(0, 'Int8')));
+----
+Timestamp(ns)
+
+query T
+SELECT arrow_typeof(to_timestamp(arrow_cast(0, 'UInt64')));
+----
+Timestamp(ns)
+
+query T
+SELECT arrow_typeof(to_timestamp(arrow_cast(0.0, 'Float32')));
+----
+Timestamp(ns)
+
+query T
+SELECT arrow_typeof(to_timestamp_seconds(arrow_cast(0, 'Int8')));
+----
+Timestamp(s)
+
+query T
+SELECT arrow_typeof(to_timestamp_seconds(arrow_cast(0, 'UInt64')));
+----
+Timestamp(s)
+
+query T
+SELECT arrow_typeof(to_timestamp_seconds(arrow_cast(0.0, 'Float32')));
+----
+Timestamp(s)
+
+query T
+SELECT arrow_typeof(to_timestamp_millis(arrow_cast(0, 'Int8')));
+----
+Timestamp(ms)
+
+query T
+SELECT arrow_typeof(to_timestamp_millis(arrow_cast(0, 'UInt64')));
+----
+Timestamp(ms)
+
+query T
+SELECT arrow_typeof(to_timestamp_millis(arrow_cast(0.0, 'Float32')));
+----
+Timestamp(ms)
+
+query T
+SELECT arrow_typeof(to_timestamp_micros(arrow_cast(0, 'Int8')));
+----
+Timestamp(µs)
+
+query T
+SELECT arrow_typeof(to_timestamp_micros(arrow_cast(0, 'UInt64')));
+----
+Timestamp(µs)
+
+query T
+SELECT arrow_typeof(to_timestamp_micros(arrow_cast(0.0, 'Float32')));
+----
+Timestamp(µs)
+
+query T
+SELECT arrow_typeof(to_timestamp_nanos(arrow_cast(0, 'Int8')));
+----
+Timestamp(ns)
+
+query T
+SELECT arrow_typeof(to_timestamp_nanos(arrow_cast(0, 'UInt64')));
+----
+Timestamp(ns)
+
+query T
+SELECT arrow_typeof(to_timestamp_nanos(arrow_cast(0.0, 'Float32')));
+----
+Timestamp(ns)
+
+# Test decimal type support for all to_timestamp functions
+# Decimal32
+query P
+SELECT to_timestamp(arrow_cast(1.5, 'Decimal32(5,1)'));
+----
+1970-01-01T00:00:01.500
+
+query P
+SELECT to_timestamp_seconds(arrow_cast(86400, 'Decimal32(9,0)'));
+----
+1970-01-02T00:00:00
+
+query P
+SELECT to_timestamp_millis(arrow_cast(1000, 'Decimal32(9,0)'));
+----
+1970-01-01T00:00:01
+
+query P
+SELECT to_timestamp_micros(arrow_cast(1000000, 'Decimal32(9,0)'));
+----
+1970-01-01T00:00:01
+
+query P
+SELECT to_timestamp_nanos(arrow_cast(1000000, 'Decimal32(9,0)'));
+----
+1970-01-01T00:00:00.001
+
+# Decimal64
+query P
+SELECT to_timestamp(arrow_cast(1.5, 'Decimal64(10,1)'));
+----
+1970-01-01T00:00:01.500
+
+query P
+SELECT to_timestamp_seconds(arrow_cast(86400, 'Decimal64(18,0)'));
+----
+1970-01-02T00:00:00
+
+query P
+SELECT to_timestamp_millis(arrow_cast(86400000, 'Decimal64(18,0)'));
+----
+1970-01-02T00:00:00
+
+query P
+SELECT to_timestamp_micros(arrow_cast(86400000000, 'Decimal64(18,0)'));
+----
+1970-01-02T00:00:00
+
+query P
+SELECT to_timestamp_nanos(arrow_cast(86400000000000, 'Decimal64(18,0)'));
+----
+1970-01-02T00:00:00
+
+# Decimal128
+query P
+SELECT to_timestamp(arrow_cast(1.5, 'Decimal128(10,1)'));
+----
+1970-01-01T00:00:01.500
+
+query P
+SELECT to_timestamp_seconds(arrow_cast(86400, 'Decimal128(10,0)'));
+----
+1970-01-02T00:00:00
+
+query P
+SELECT to_timestamp_millis(arrow_cast(86400000, 'Decimal128(15,0)'));
+----
+1970-01-02T00:00:00
+
+query P
+SELECT to_timestamp_micros(arrow_cast(86400000000, 'Decimal128(15,0)'));
+----
+1970-01-02T00:00:00
+
+query P
+SELECT to_timestamp_nanos(arrow_cast(86400000000000, 'Decimal128(20,0)'));
+----
+1970-01-02T00:00:00
+
+# Decimal256
+query P
+SELECT to_timestamp(arrow_cast(1.5, 'Decimal256(10,1)'));
+----
+1970-01-01T00:00:01.500
+
+query P
+SELECT to_timestamp_seconds(arrow_cast(86400, 'Decimal256(38,0)'));
+----
+1970-01-02T00:00:00
+
+query P
+SELECT to_timestamp_millis(arrow_cast(86400000, 'Decimal256(38,0)'));
+----
+1970-01-02T00:00:00
+
+query P
+SELECT to_timestamp_micros(arrow_cast(86400000000, 'Decimal256(38,0)'));
+----
+1970-01-02T00:00:00
+
+query P
+SELECT to_timestamp_nanos(arrow_cast(86400000000000, 'Decimal256(38,0)'));
+----
+1970-01-02T00:00:00
+
+# Verify arrow_typeof for decimal inputs
+query T
+SELECT arrow_typeof(to_timestamp(arrow_cast(0, 'Decimal128(10,0)')));
+----
+Timestamp(ns)
+
+query T
+SELECT arrow_typeof(to_timestamp_seconds(arrow_cast(0, 'Decimal128(10,0)')));
+----
+Timestamp(s)
+
+query T
+SELECT arrow_typeof(to_timestamp_millis(arrow_cast(0, 'Decimal128(10,0)')));
+----
+Timestamp(ms)
+
+query T
+SELECT arrow_typeof(to_timestamp_micros(arrow_cast(0, 'Decimal128(10,0)')));
+----
+Timestamp(µs)
+
+query T
+SELECT arrow_typeof(to_timestamp_nanos(arrow_cast(0, 'Decimal128(10,0)')));
+----
+Timestamp(ns)
+
+# Test decimal array inputs for to_timestamp
+statement ok
+CREATE TABLE test_decimal_timestamps (
+ d128 DECIMAL(20, 9),
+ d256 DECIMAL(40, 9)
+) AS VALUES
+ (1.5, 1.5),
+ (86400.123456789, 86400.123456789),
+ (0.0, 0.0),
+ (NULL, NULL);
+
+query P
+SELECT to_timestamp(d128) FROM test_decimal_timestamps ORDER BY d128 NULLS
LAST;
+----
+1970-01-01T00:00:00
+1970-01-01T00:00:01.500
+1970-01-02T00:00:00.123456789
+NULL
+
+query P
+SELECT to_timestamp(d256) FROM test_decimal_timestamps ORDER BY d256 NULLS
LAST;
+----
+1970-01-01T00:00:00
+1970-01-01T00:00:01.500
+1970-01-02T00:00:00.123456789
+NULL
+
+statement ok
+DROP TABLE test_decimal_timestamps;
+
+# Test negative values
+# to_timestamp with negative seconds
+# Int8
+query P
+SELECT to_timestamp(arrow_cast(-1, 'Int8'));
+----
+1969-12-31T23:59:59
+
+# Int16
+query P
+SELECT to_timestamp(arrow_cast(-1, 'Int16'));
+----
+1969-12-31T23:59:59
+
+# Int32
+query P
+SELECT to_timestamp(arrow_cast(-86400, 'Int32'));
+----
+1969-12-31T00:00:00
+
+# Int64
+query P
+SELECT to_timestamp(arrow_cast(-1, 'Int64'));
+----
+1969-12-31T23:59:59
+
+# Float64
+query P
+SELECT to_timestamp(arrow_cast(-0.5, 'Float64'));
+----
+1969-12-31T23:59:59.500
+
+# to_timestamp_seconds with negative values
+# Int8
+query P
+SELECT to_timestamp_seconds(arrow_cast(-1, 'Int8'));
+----
+1969-12-31T23:59:59
+
+# Int16
+query P
+SELECT to_timestamp_seconds(arrow_cast(-1, 'Int16'));
+----
+1969-12-31T23:59:59
+
+# Int32
+query P
+SELECT to_timestamp_seconds(arrow_cast(-86400, 'Int32'));
+----
+1969-12-31T00:00:00
+
+# Int64
+query P
+SELECT to_timestamp_seconds(arrow_cast(-1, 'Int64'));
+----
+1969-12-31T23:59:59
+
+# to_timestamp_millis with negative values
+# Int8
+query P
+SELECT to_timestamp_millis(arrow_cast(-1, 'Int8'));
+----
+1969-12-31T23:59:59.999
+
+# Int16
+query P
+SELECT to_timestamp_millis(arrow_cast(-1, 'Int16'));
+----
+1969-12-31T23:59:59.999
+
+# Int32
+query P
+SELECT to_timestamp_millis(arrow_cast(-1000, 'Int32'));
+----
+1969-12-31T23:59:59
+
+# Int64
+query P
+SELECT to_timestamp_millis(arrow_cast(-1, 'Int64'));
+----
+1969-12-31T23:59:59.999
+
+# to_timestamp_micros with negative values
+# Int8
+query P
+SELECT to_timestamp_micros(arrow_cast(-1, 'Int8'));
+----
+1969-12-31T23:59:59.999999
+
+# Int16
+query P
+SELECT to_timestamp_micros(arrow_cast(-1, 'Int16'));
+----
+1969-12-31T23:59:59.999999
+
+# Int32
+query P
+SELECT to_timestamp_micros(arrow_cast(-1000000, 'Int32'));
+----
+1969-12-31T23:59:59
+
+# Int64
+query P
+SELECT to_timestamp_micros(arrow_cast(-1, 'Int64'));
+----
+1969-12-31T23:59:59.999999
+
+# to_timestamp_nanos with negative values
+# Int8
+query P
+SELECT to_timestamp_nanos(arrow_cast(-1, 'Int8'));
+----
+1969-12-31T23:59:59.999999999
+
+# Int16
+query P
+SELECT to_timestamp_nanos(arrow_cast(-1, 'Int16'));
+----
+1969-12-31T23:59:59.999999999
+
+# Int32
+query P
+SELECT to_timestamp_nanos(arrow_cast(-1000000000, 'Int32'));
+----
+1969-12-31T23:59:59
+
+# Int64
+query P
+SELECT to_timestamp_nanos(arrow_cast(-1000000000, 'Int64'));
+----
+1969-12-31T23:59:59
+
+query P
+SELECT to_timestamp_nanos(arrow_cast(-1, 'Int64'));
+----
+1969-12-31T23:59:59.999999999
+
+# Test large unsigned values
+query P
+SELECT to_timestamp_seconds(arrow_cast(4294967295, 'UInt64'));
+----
+2106-02-07T06:28:15
+
+# Large UInt64 value for milliseconds
+query P
+SELECT to_timestamp_millis(arrow_cast(4294967295000, 'UInt64'));
+----
+2106-02-07T06:28:15
+
+# Test UInt64 value larger than i64::MAX (9223372036854775808 = i64::MAX + 1)
+query error Cast error: Can't cast value 9223372036854775808 to type Int64
+SELECT to_timestamp_nanos(arrow_cast(9223372036854775808, 'UInt64'));
+
+# Test boundary values for to_timestamp
+query P
+SELECT to_timestamp(arrow_cast(9223372036, 'Int64'));
+----
+2262-04-11T23:47:16
+
+# Minimum value for to_timestamp
+query P
+SELECT to_timestamp(arrow_cast(-9223372036, 'Int64'));
+----
+1677-09-21T00:12:44
+
+# Overflow error when value exceeds valid range
+query error Arithmetic overflow
+SELECT to_timestamp(arrow_cast(9223372037, 'Int64'));
+
+# Float truncation behavior
+query P
+SELECT to_timestamp_seconds(arrow_cast(-1.9, 'Float64'));
+----
+1969-12-31T23:59:59
+
+query P
+SELECT to_timestamp_millis(arrow_cast(-1.9, 'Float64'));
+----
+1969-12-31T23:59:59.999
+
+
##########
## Common timestamp data
##########
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]