diff --git a/datafusion/sql/src/planner.rs b/datafusion/sql/src/planner.rs index 5f7d01b9f40ca..5dea4eb8714c0 100644 --- a/datafusion/sql/src/planner.rs +++ b/datafusion/sql/src/planner.rs @@ -806,11 +806,20 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> { )) } SQLDataType::Date => Ok(DataType::Date32), - SQLDataType::Time(None, tz_info) => { + SQLDataType::Time(precision, tz_info) + if precision.is_none() || [0, 3, 6, 9].contains(&precision.unwrap()) => + { if *tz_info == TimezoneInfo::None || *tz_info == TimezoneInfo::WithoutTimeZone { - Ok(DataType::Time64(TimeUnit::Nanosecond)) + let data_type = match precision { + Some(0) => DataType::Time32(TimeUnit::Second), + Some(3) => DataType::Time32(TimeUnit::Millisecond), + Some(6) => DataType::Time64(TimeUnit::Microsecond), + None | Some(9) => DataType::Time64(TimeUnit::Nanosecond), + _ => unreachable!(), + }; + Ok(data_type) } else { // We don't support TIMETZ and TIME WITH TIME ZONE for now not_impl_err!("Unsupported SQL type {sql_type}") @@ -880,7 +889,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> { | SQLDataType::CharacterLargeObject(_) | SQLDataType::CharLargeObject(_) | SQLDataType::Timestamp(_, _) - | SQLDataType::Time(Some(_), _) + | SQLDataType::Time(_, _) | SQLDataType::Dec(_) | SQLDataType::BigNumeric(_) | SQLDataType::BigDecimal(_) diff --git a/datafusion/sqllogictest/test_files/datetime/timestamps.slt b/datafusion/sqllogictest/test_files/datetime/timestamps.slt index 03be67d2143df..6b492555e00ed 100644 --- a/datafusion/sqllogictest/test_files/datetime/timestamps.slt +++ b/datafusion/sqllogictest/test_files/datetime/timestamps.slt @@ -749,6 +749,85 @@ select date '1994-01-01' - interval '1' day as date; ---- 1993-12-31 +# TIME precision selects the Arrow unit; the default remains nanoseconds. +query TTTTT +SELECT arrow_typeof(CAST(NULL AS TIME)) AS t, + arrow_typeof(CAST(NULL AS TIME(0))) AS s, + arrow_typeof(CAST(NULL AS TIME(3))) AS ms, + arrow_typeof(CAST(NULL AS TIME(6))) AS us, + arrow_typeof(CAST(NULL AS TIME(9))) AS ns; +---- +Time64(ns) Time32(s) Time32(ms) Time64(µs) Time64(ns) + +# String casts truncate fractional seconds to the requested precision. +query DDDD +SELECT CAST('12:34:56.123456789' AS TIME(0)) AS s, + CAST('12:34:56.123456789' AS TIME(3)) AS ms, + CAST('12:34:56.123456789' AS TIME(6)) AS us, + CAST('12:34:56.123456789' AS TIME(9)) AS ns; +---- +12:34:56 12:34:56.123 12:34:56.123456 12:34:56.123456789 + +# Column declarations use the same mapping, including WITHOUT TIME ZONE. +statement ok +CREATE TABLE times ( + t TIME WITHOUT TIME ZONE, + s TIME(0) WITHOUT TIME ZONE, + ms TIME(3) WITHOUT TIME ZONE, + us TIME(6) WITHOUT TIME ZONE, + ns TIME(9) WITHOUT TIME ZONE +); + +statement ok +INSERT INTO times VALUES ( + '12:34:56.123456789', '12:34:56.123456789', '12:34:56.123456789', + '12:34:56.123456789', '12:34:56.123456789' +), (NULL, NULL, NULL, NULL, NULL); + +query DDDDD rowsort +SELECT * FROM times; +---- +12:34:56.123456789 12:34:56 12:34:56.123 12:34:56.123456 12:34:56.123456789 +NULL NULL NULL NULL NULL + +query TTTTT +SELECT arrow_typeof(t), arrow_typeof(s), arrow_typeof(ms), arrow_typeof(us), arrow_typeof(ns) +FROM times LIMIT 1; +---- +Time64(ns) Time32(s) Time32(ms) Time64(µs) Time64(ns) + +# Unsupported precisions, including values beyond nanoseconds, remain errors. +statement error +SELECT CAST('12:34:56' AS TIME(1)); +---- +DataFusion error: This feature is not implemented: Unsupported SQL type TIME(1) + + +statement error +CREATE TABLE invalid_time (t TIME(12)); +---- +DataFusion error: This feature is not implemented: Unsupported SQL type TIME(12) + + +# Arrow time types have no time zone. +statement error +SELECT CAST('12:34:56' AS TIME WITH TIME ZONE); +---- +DataFusion error: This feature is not implemented: Unsupported SQL type TIME WITH TIME ZONE + + +statement error +SELECT CAST('12:34:56' AS TIME(6) WITH TIME ZONE); +---- +DataFusion error: This feature is not implemented: Unsupported SQL type TIME(6) WITH TIME ZONE + + +statement error +SELECT CAST('12:34:56' AS TIMETZ(6)); +---- +DataFusion error: This feature is not implemented: Unsupported SQL type TIMETZ(6) + + # cast_string_to_time() query DDDD select diff --git a/docs/source/user-guide/sql/data_types.md b/docs/source/user-guide/sql/data_types.md index 9f232e7ed3412..50e3d8983db33 100644 --- a/docs/source/user-guide/sql/data_types.md +++ b/docs/source/user-guide/sql/data_types.md @@ -95,7 +95,10 @@ The maximum supported precision for `DECIMAL` types is 76. | SQL DataType | Arrow DataType | | ------------------------------------------------------------- | :--------------------------------------------------------- | | `DATE` | `Date32` | -| `TIME` | `Time64(Nanosecond)` | +| `TIME`, `TIME(9)` | `Time64(Nanosecond)` | +| `TIME(0)` | `Time32(Second)` | +| `TIME(3)` | `Time32(Millisecond)` | +| `TIME(6)` | `Time64(Microsecond)` | | `TIMESTAMP`, `TIMESTAMP(p)`, `TIMESTAMP(p) WITHOUT TIME ZONE` | `Timestamp(unit, None)` | | `DATETIME`, `DATETIME(p)` | `Timestamp(unit, None)` | | `TIMESTAMPTZ(p)`, `TIMESTAMP(p) WITH TIME ZONE` | `Timestamp(unit, None)` by default — see the warning below | @@ -126,7 +129,9 @@ rows. Use DataFusion rejects each other value of `p`. If you omit `p`, the unit is `Nanosecond`. The precision goes after `TIMESTAMPTZ`, but before `WITH TIME ZONE`: use `TIMESTAMPTZ(3)` or `TIMESTAMP(3) WITH TIME ZONE`. -`DATE`, `TIME` and `INTERVAL` do not accept a precision. +`TIME` accepts the same precisions and an optional `WITHOUT TIME ZONE` clause. +`TIME WITH TIME ZONE` and `TIMETZ` are unsupported. +`DATE` and `INTERVAL` do not accept a precision. [`datafusion.execution.time_zone`]: ../configs.md [issue #25166]: https://github.com/apache/datafusion/issues/25166