This is an automated email from the ASF dual-hosted git repository.

alamb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-datafusion.git


The following commit(s) were added to refs/heads/master by this push:
     new bea645cc9 Full support for time32 and time64 literal values 
(`ScalarValue`) (#4156)
bea645cc9 is described below

commit bea645cc99f955e0233e742e9e4b7266cfacbbe7
Author: André Calado Coroado 
<[email protected]>
AuthorDate: Sun Nov 20 21:46:52 2022 +0100

    Full support for time32 and time64 literal values (`ScalarValue`) (#4156)
    
    * Rebase branch into most recent version of master
    
    * Implement Time32 and Time64 in hash_join and hash_util
    
    * Add review comments
    
    * Changes in proto to provide full support for Time32 and Time64
    
    * Add test to ensure Time32 and Time64 are fully supported
    
    * Changes in protos to allow full support of Time32 and Time64
    
    * Correct changes in protos to allow full support of Time32 and Time64
    
    * Fix tests
    
    * Remove core/src/physical_plan/hash_utils.rs and useless comment
    
    * Fix failing test
    
    * Update to avoid deprecated chrono feature
    
    * Remove generated file
    
    * Clippy
    
    Co-authored-by: Andrew Lamb <[email protected]>
---
 datafusion/common/src/scalar.rs                    | 157 ++++++++++++--
 .../core/src/physical_plan/joins/hash_join.rs      |  34 ++-
 datafusion/core/tests/sql/aggregates.rs            |  86 ++++++++
 datafusion/core/tests/sql/group_by.rs              | 180 ++++++++++++++++
 datafusion/core/tests/sql/mod.rs                   |  93 ++++++++
 datafusion/core/tests/sql/select.rs                | 176 +++++++++++++++
 datafusion/expr/src/type_coercion/binary.rs        |  58 ++++-
 datafusion/physical-expr/src/aggregate/min_max.rs  | 127 ++++++++++-
 .../physical-expr/src/datetime_expressions.rs      |   2 +-
 datafusion/physical-expr/src/expressions/binary.rs |  16 ++
 datafusion/proto/proto/datafusion.proto            |  21 +-
 datafusion/proto/src/from_proto.rs                 |  25 ++-
 datafusion/proto/src/generated/pbjson.rs           | 237 ++++++++++++++++++++-
 datafusion/proto/src/generated/prost.rs            |  38 +++-
 datafusion/proto/src/lib.rs                        |  18 +-
 datafusion/proto/src/to_proto.rs                   |  46 +++-
 16 files changed, 1263 insertions(+), 51 deletions(-)

diff --git a/datafusion/common/src/scalar.rs b/datafusion/common/src/scalar.rs
index 36ea78580..44bf278b9 100644
--- a/datafusion/common/src/scalar.rs
+++ b/datafusion/common/src/scalar.rs
@@ -89,8 +89,14 @@ pub enum ScalarValue {
     Date32(Option<i32>),
     /// Date stored as a signed 64bit int milliseconds since UNIX epoch 
1970-01-01
     Date64(Option<i64>),
+    /// Time stored as a signed 32bit int as seconds since midnight
+    Time32Second(Option<i32>),
+    /// Time stored as a signed 32bit int as milliseconds since midnight
+    Time32Millisecond(Option<i32>),
+    /// Time stored as a signed 64bit int as microseconds since midnight
+    Time64Microsecond(Option<i64>),
     /// Time stored as a signed 64bit int as nanoseconds since midnight
-    Time64(Option<i64>),
+    Time64Nanosecond(Option<i64>),
     /// Timestamp Second
     TimestampSecond(Option<i64>, Option<String>),
     /// Timestamp Milliseconds
@@ -170,8 +176,14 @@ impl PartialEq for ScalarValue {
             (Date32(_), _) => false,
             (Date64(v1), Date64(v2)) => v1.eq(v2),
             (Date64(_), _) => false,
-            (Time64(v1), Time64(v2)) => v1.eq(v2),
-            (Time64(_), _) => false,
+            (Time32Second(v1), Time32Second(v2)) => v1.eq(v2),
+            (Time32Second(_), _) => false,
+            (Time32Millisecond(v1), Time32Millisecond(v2)) => v1.eq(v2),
+            (Time32Millisecond(_), _) => false,
+            (Time64Microsecond(v1), Time64Microsecond(v2)) => v1.eq(v2),
+            (Time64Microsecond(_), _) => false,
+            (Time64Nanosecond(v1), Time64Nanosecond(v2)) => v1.eq(v2),
+            (Time64Nanosecond(_), _) => false,
             (TimestampSecond(v1, _), TimestampSecond(v2, _)) => v1.eq(v2),
             (TimestampSecond(_, _), _) => false,
             (TimestampMillisecond(v1, _), TimestampMillisecond(v2, _)) => 
v1.eq(v2),
@@ -263,8 +275,14 @@ impl PartialOrd for ScalarValue {
             (Date32(_), _) => None,
             (Date64(v1), Date64(v2)) => v1.partial_cmp(v2),
             (Date64(_), _) => None,
-            (Time64(v1), Time64(v2)) => v1.partial_cmp(v2),
-            (Time64(_), _) => None,
+            (Time32Second(v1), Time32Second(v2)) => v1.partial_cmp(v2),
+            (Time32Second(_), _) => None,
+            (Time32Millisecond(v1), Time32Millisecond(v2)) => 
v1.partial_cmp(v2),
+            (Time32Millisecond(_), _) => None,
+            (Time64Microsecond(v1), Time64Microsecond(v2)) => 
v1.partial_cmp(v2),
+            (Time64Microsecond(_), _) => None,
+            (Time64Nanosecond(v1), Time64Nanosecond(v2)) => v1.partial_cmp(v2),
+            (Time64Nanosecond(_), _) => None,
             (TimestampSecond(v1, _), TimestampSecond(v2, _)) => 
v1.partial_cmp(v2),
             (TimestampSecond(_, _), _) => None,
             (TimestampMillisecond(v1, _), TimestampMillisecond(v2, _)) => {
@@ -670,7 +688,10 @@ impl std::hash::Hash for ScalarValue {
             }
             Date32(v) => v.hash(state),
             Date64(v) => v.hash(state),
-            Time64(v) => v.hash(state),
+            Time32Second(v) => v.hash(state),
+            Time32Millisecond(v) => v.hash(state),
+            Time64Microsecond(v) => v.hash(state),
+            Time64Nanosecond(v) => v.hash(state),
             TimestampSecond(v, _) => v.hash(state),
             TimestampMillisecond(v, _) => v.hash(state),
             TimestampMicrosecond(v, _) => v.hash(state),
@@ -1036,7 +1057,10 @@ impl ScalarValue {
             ))),
             ScalarValue::Date32(_) => DataType::Date32,
             ScalarValue::Date64(_) => DataType::Date64,
-            ScalarValue::Time64(_) => DataType::Time64(TimeUnit::Nanosecond),
+            ScalarValue::Time32Second(_) => DataType::Time32(TimeUnit::Second),
+            ScalarValue::Time32Millisecond(_) => 
DataType::Time32(TimeUnit::Millisecond),
+            ScalarValue::Time64Microsecond(_) => 
DataType::Time64(TimeUnit::Microsecond),
+            ScalarValue::Time64Nanosecond(_) => 
DataType::Time64(TimeUnit::Nanosecond),
             ScalarValue::IntervalYearMonth(_) => {
                 DataType::Interval(IntervalUnit::YearMonth)
             }
@@ -1120,7 +1144,10 @@ impl ScalarValue {
             ScalarValue::List(v, _) => v.is_none(),
             ScalarValue::Date32(v) => v.is_none(),
             ScalarValue::Date64(v) => v.is_none(),
-            ScalarValue::Time64(v) => v.is_none(),
+            ScalarValue::Time32Second(v) => v.is_none(),
+            ScalarValue::Time32Millisecond(v) => v.is_none(),
+            ScalarValue::Time64Microsecond(v) => v.is_none(),
+            ScalarValue::Time64Nanosecond(v) => v.is_none(),
             ScalarValue::TimestampSecond(v, _) => v.is_none(),
             ScalarValue::TimestampMillisecond(v, _) => v.is_none(),
             ScalarValue::TimestampMicrosecond(v, _) => v.is_none(),
@@ -1380,8 +1407,17 @@ impl ScalarValue {
             DataType::LargeBinary => build_array_string!(LargeBinaryArray, 
LargeBinary),
             DataType::Date32 => build_array_primitive!(Date32Array, Date32),
             DataType::Date64 => build_array_primitive!(Date64Array, Date64),
+            DataType::Time32(TimeUnit::Second) => {
+                build_array_primitive!(Time32SecondArray, Time32Second)
+            }
+            DataType::Time32(TimeUnit::Millisecond) => {
+                build_array_primitive!(Time32MillisecondArray, 
Time32Millisecond)
+            }
+            DataType::Time64(TimeUnit::Microsecond) => {
+                build_array_primitive!(Time64MicrosecondArray, 
Time64Microsecond)
+            }
             DataType::Time64(TimeUnit::Nanosecond) => {
-                build_array_primitive!(Time64NanosecondArray, Time64)
+                build_array_primitive!(Time64NanosecondArray, Time64Nanosecond)
             }
             DataType::Timestamp(TimeUnit::Second, _) => {
                 build_array_primitive_tz!(TimestampSecondArray, 
TimestampSecond)
@@ -1541,10 +1577,15 @@ impl ScalarValue {
                 Arc::new(array)
             }
             // explicitly enumerate unsupported types so newly added
-            // types must be aknowledged
+            // types must be aknowledged, Time32 and Time64 types are
+            // not supported if the TimeUnit is not valid (Time32 can
+            // only be used with Second and Millisecond, Time64 only
+            // with Microsecond and Nanosecond)
             DataType::Float16
-            | DataType::Time32(_)
-            | DataType::Time64(_)
+            | DataType::Time32(TimeUnit::Microsecond)
+            | DataType::Time32(TimeUnit::Nanosecond)
+            | DataType::Time64(TimeUnit::Second)
+            | DataType::Time64(TimeUnit::Millisecond)
             | DataType::Duration(_)
             | DataType::FixedSizeList(_, _)
             | DataType::Interval(_)
@@ -1813,7 +1854,34 @@ impl ScalarValue {
             ScalarValue::Date64(e) => {
                 build_array_from_option!(Date64, Date64Array, e, size)
             }
-            ScalarValue::Time64(e) => {
+            ScalarValue::Time32Second(e) => {
+                build_array_from_option!(
+                    Time32,
+                    TimeUnit::Second,
+                    Time32SecondArray,
+                    e,
+                    size
+                )
+            }
+            ScalarValue::Time32Millisecond(e) => {
+                build_array_from_option!(
+                    Time32,
+                    TimeUnit::Millisecond,
+                    Time32MillisecondArray,
+                    e,
+                    size
+                )
+            }
+            ScalarValue::Time64Microsecond(e) => {
+                build_array_from_option!(
+                    Time64,
+                    TimeUnit::Microsecond,
+                    Time64MicrosecondArray,
+                    e,
+                    size
+                )
+            }
+            ScalarValue::Time64Nanosecond(e) => {
                 build_array_from_option!(
                     Time64,
                     TimeUnit::Nanosecond,
@@ -1957,8 +2025,17 @@ impl ScalarValue {
             DataType::Date64 => {
                 typed_cast!(array, index, Date64Array, Date64)
             }
+            DataType::Time32(TimeUnit::Second) => {
+                typed_cast!(array, index, Time32SecondArray, Time32Second)
+            }
+            DataType::Time32(TimeUnit::Millisecond) => {
+                typed_cast!(array, index, Time32MillisecondArray, 
Time32Millisecond)
+            }
+            DataType::Time64(TimeUnit::Microsecond) => {
+                typed_cast!(array, index, Time64MicrosecondArray, 
Time64Microsecond)
+            }
             DataType::Time64(TimeUnit::Nanosecond) => {
-                typed_cast!(array, index, Time64NanosecondArray, Time64)
+                typed_cast!(array, index, Time64NanosecondArray, 
Time64Nanosecond)
             }
             DataType::Timestamp(TimeUnit::Second, tz_opt) => {
                 typed_cast_tz!(
@@ -2163,7 +2240,16 @@ impl ScalarValue {
             ScalarValue::Date64(val) => {
                 eq_array_primitive!(array, index, Date64Array, val)
             }
-            ScalarValue::Time64(val) => {
+            ScalarValue::Time32Second(val) => {
+                eq_array_primitive!(array, index, Time32SecondArray, val)
+            }
+            ScalarValue::Time32Millisecond(val) => {
+                eq_array_primitive!(array, index, Time32MillisecondArray, val)
+            }
+            ScalarValue::Time64Microsecond(val) => {
+                eq_array_primitive!(array, index, Time64MicrosecondArray, val)
+            }
+            ScalarValue::Time64Nanosecond(val) => {
                 eq_array_primitive!(array, index, Time64NanosecondArray, val)
             }
             ScalarValue::TimestampSecond(val, _) => {
@@ -2295,14 +2381,16 @@ macro_rules! impl_try_from {
 impl_try_from!(Int8, i8);
 impl_try_from!(Int16, i16);
 
-// special implementation for i32 because of Date32
+// special implementation for i32 because of Date32 and Time32
 impl TryFrom<ScalarValue> for i32 {
     type Error = DataFusionError;
 
     fn try_from(value: ScalarValue) -> Result<Self> {
         match value {
             ScalarValue::Int32(Some(inner_value))
-            | ScalarValue::Date32(Some(inner_value)) => Ok(inner_value),
+            | ScalarValue::Date32(Some(inner_value))
+            | ScalarValue::Time32Second(Some(inner_value))
+            | ScalarValue::Time32Millisecond(Some(inner_value)) => 
Ok(inner_value),
             _ => Err(DataFusionError::Internal(format!(
                 "Cannot convert {:?} to {}",
                 value,
@@ -2312,7 +2400,7 @@ impl TryFrom<ScalarValue> for i32 {
     }
 }
 
-// special implementation for i64 because of TimeNanosecond
+// special implementation for i64 because of Date64, Time64 and Timestamp
 impl TryFrom<ScalarValue> for i64 {
     type Error = DataFusionError;
 
@@ -2320,7 +2408,8 @@ impl TryFrom<ScalarValue> for i64 {
         match value {
             ScalarValue::Int64(Some(inner_value))
             | ScalarValue::Date64(Some(inner_value))
-            | ScalarValue::Time64(Some(inner_value))
+            | ScalarValue::Time64Microsecond(Some(inner_value))
+            | ScalarValue::Time64Nanosecond(Some(inner_value))
             | ScalarValue::TimestampNanosecond(Some(inner_value), _)
             | ScalarValue::TimestampMicrosecond(Some(inner_value), _)
             | ScalarValue::TimestampMillisecond(Some(inner_value), _)
@@ -2394,7 +2483,14 @@ impl TryFrom<&DataType> for ScalarValue {
             DataType::LargeBinary => ScalarValue::LargeBinary(None),
             DataType::Date32 => ScalarValue::Date32(None),
             DataType::Date64 => ScalarValue::Date64(None),
-            DataType::Time64(TimeUnit::Nanosecond) => 
ScalarValue::Time64(None),
+            DataType::Time32(TimeUnit::Second) => 
ScalarValue::Time32Second(None),
+            DataType::Time32(TimeUnit::Millisecond) => {
+                ScalarValue::Time32Millisecond(None)
+            }
+            DataType::Time64(TimeUnit::Microsecond) => {
+                ScalarValue::Time64Microsecond(None)
+            }
+            DataType::Time64(TimeUnit::Nanosecond) => 
ScalarValue::Time64Nanosecond(None),
             DataType::Timestamp(TimeUnit::Second, tz_opt) => {
                 ScalarValue::TimestampSecond(None, tz_opt.clone())
             }
@@ -2515,7 +2611,10 @@ impl fmt::Display for ScalarValue {
             },
             ScalarValue::Date32(e) => format_option!(f, e)?,
             ScalarValue::Date64(e) => format_option!(f, e)?,
-            ScalarValue::Time64(e) => format_option!(f, e)?,
+            ScalarValue::Time32Second(e) => format_option!(f, e)?,
+            ScalarValue::Time32Millisecond(e) => format_option!(f, e)?,
+            ScalarValue::Time64Microsecond(e) => format_option!(f, e)?,
+            ScalarValue::Time64Nanosecond(e) => format_option!(f, e)?,
             ScalarValue::IntervalDayTime(e) => format_option!(f, e)?,
             ScalarValue::IntervalYearMonth(e) => format_option!(f, e)?,
             ScalarValue::IntervalMonthDayNano(e) => format_option!(f, e)?,
@@ -2582,7 +2681,16 @@ impl fmt::Debug for ScalarValue {
             ScalarValue::List(_, _) => write!(f, "List([{}])", self),
             ScalarValue::Date32(_) => write!(f, "Date32(\"{}\")", self),
             ScalarValue::Date64(_) => write!(f, "Date64(\"{}\")", self),
-            ScalarValue::Time64(_) => write!(f, "Time64(\"{}\")", self),
+            ScalarValue::Time32Second(_) => write!(f, "Time32Second(\"{}\")", 
self),
+            ScalarValue::Time32Millisecond(_) => {
+                write!(f, "Time32Millisecond(\"{}\")", self)
+            }
+            ScalarValue::Time64Microsecond(_) => {
+                write!(f, "Time64Microsecond(\"{}\")", self)
+            }
+            ScalarValue::Time64Nanosecond(_) => {
+                write!(f, "Time64Nanosecond(\"{}\")", self)
+            }
             ScalarValue::IntervalDayTime(_) => {
                 write!(f, "IntervalDayTime(\"{}\")", self)
             }
@@ -3212,7 +3320,10 @@ mod tests {
             make_binary_test_case!(str_vals, LargeBinaryArray, LargeBinary),
             make_test_case!(i32_vals, Date32Array, Date32),
             make_test_case!(i64_vals, Date64Array, Date64),
-            make_test_case!(i64_vals, Time64NanosecondArray, Time64),
+            make_test_case!(i32_vals, Time32SecondArray, Time32Second),
+            make_test_case!(i32_vals, Time32MillisecondArray, 
Time32Millisecond),
+            make_test_case!(i64_vals, Time64MicrosecondArray, 
Time64Microsecond),
+            make_test_case!(i64_vals, Time64NanosecondArray, Time64Nanosecond),
             make_test_case!(i64_vals, TimestampSecondArray, TimestampSecond, 
None),
             make_test_case!(
                 i64_vals,
diff --git a/datafusion/core/src/physical_plan/joins/hash_join.rs 
b/datafusion/core/src/physical_plan/joins/hash_join.rs
index 86448ff58..d557ca6ed 100644
--- a/datafusion/core/src/physical_plan/joins/hash_join.rs
+++ b/datafusion/core/src/physical_plan/joins/hash_join.rs
@@ -24,8 +24,10 @@ use arrow::{
     array::{
         as_dictionary_array, ArrayData, ArrayRef, BooleanArray, Date32Array, 
Date64Array,
         Decimal128Array, DictionaryArray, LargeStringArray, PrimitiveArray,
-        TimestampMicrosecondArray, TimestampMillisecondArray, 
TimestampSecondArray,
-        UInt32BufferBuilder, UInt32Builder, UInt64BufferBuilder, UInt64Builder,
+        Time32MillisecondArray, Time32SecondArray, Time64MicrosecondArray,
+        Time64NanosecondArray, TimestampMicrosecondArray, 
TimestampMillisecondArray,
+        TimestampSecondArray, UInt32BufferBuilder, UInt32Builder, 
UInt64BufferBuilder,
+        UInt64Builder,
     },
     compute,
     datatypes::{
@@ -1218,6 +1220,34 @@ fn equal_rows(
             DataType::Date64 => {
                 equal_rows_elem!(Date64Array, l, r, left, right, 
null_equals_null)
             }
+            DataType::Time32(time_unit) => match time_unit {
+                TimeUnit::Second => {
+                    equal_rows_elem!(Time32SecondArray, l, r, left, right, 
null_equals_null)
+                }
+                TimeUnit::Millisecond => {
+                    equal_rows_elem!(Time32MillisecondArray, l, r, left, 
right, null_equals_null)
+                }
+                _ => {
+                    err = Some(Err(DataFusionError::Internal(
+                        "Unsupported data type in hasher".to_string(),
+                    )));
+                    false
+                }
+            }
+            DataType::Time64(time_unit) => match time_unit {
+                TimeUnit::Microsecond => {
+                    equal_rows_elem!(Time64MicrosecondArray, l, r, left, 
right, null_equals_null)
+                }
+                TimeUnit::Nanosecond => {
+                    equal_rows_elem!(Time64NanosecondArray, l, r, left, right, 
null_equals_null)
+                }
+                _ => {
+                    err = Some(Err(DataFusionError::Internal(
+                        "Unsupported data type in hasher".to_string(),
+                    )));
+                    false
+                }
+            }
             DataType::Timestamp(time_unit, None) => match time_unit {
                 TimeUnit::Second => {
                     equal_rows_elem!(
diff --git a/datafusion/core/tests/sql/aggregates.rs 
b/datafusion/core/tests/sql/aggregates.rs
index 88ae4d00d..8b57a69fa 100644
--- a/datafusion/core/tests/sql/aggregates.rs
+++ b/datafusion/core/tests/sql/aggregates.rs
@@ -1539,6 +1539,92 @@ async fn aggregate_timestamps_max() -> Result<()> {
     Ok(())
 }
 
+#[tokio::test]
+async fn aggregate_times_sum() -> Result<()> {
+    let ctx = SessionContext::new();
+    ctx.register_table("t", table_with_times()).unwrap();
+
+    let results = plan_and_collect(
+        &ctx,
+        "SELECT sum(nanos), sum(micros), sum(millis), sum(secs) FROM t",
+    )
+    .await
+    .unwrap_err();
+
+    assert_eq!(results.to_string(), "Error during planning: The function Sum 
does not support inputs of type Time64(Nanosecond).");
+
+    Ok(())
+}
+
+#[tokio::test]
+async fn aggregate_times_count() -> Result<()> {
+    let ctx = SessionContext::new();
+    ctx.register_table("t", table_with_times()).unwrap();
+
+    let results = execute_to_batches(
+        &ctx,
+        "SELECT count(nanos), count(micros), count(millis), count(secs) FROM 
t",
+    )
+    .await;
+
+    let expected = vec![
+        
"+----------------+-----------------+-----------------+---------------+",
+        "| COUNT(t.nanos) | COUNT(t.micros) | COUNT(t.millis) | COUNT(t.secs) 
|",
+        
"+----------------+-----------------+-----------------+---------------+",
+        "| 4              | 4               | 4               | 4             
|",
+        
"+----------------+-----------------+-----------------+---------------+",
+    ];
+    assert_batches_sorted_eq!(expected, &results);
+
+    Ok(())
+}
+
+#[tokio::test]
+async fn aggregate_times_min() -> Result<()> {
+    let ctx = SessionContext::new();
+    ctx.register_table("t", table_with_times()).unwrap();
+
+    let results = execute_to_batches(
+        &ctx,
+        "SELECT min(nanos), min(micros), min(millis), min(secs) FROM t",
+    )
+    .await;
+
+    let expected = vec![
+        
"+--------------------+-----------------+---------------+-------------+",
+        "| MIN(t.nanos)       | MIN(t.micros)   | MIN(t.millis) | MIN(t.secs) 
|",
+        
"+--------------------+-----------------+---------------+-------------+",
+        "| 18:06:30.243620451 | 18:06:30.243620 | 18:06:30.243  | 18:06:30    
|",
+        
"+--------------------+-----------------+---------------+-------------+",
+    ];
+    assert_batches_sorted_eq!(expected, &results);
+
+    Ok(())
+}
+
+#[tokio::test]
+async fn aggregate_times_max() -> Result<()> {
+    let ctx = SessionContext::new();
+    ctx.register_table("t", table_with_times()).unwrap();
+
+    let results = execute_to_batches(
+        &ctx,
+        "SELECT max(nanos), max(micros), max(millis), max(secs) FROM t",
+    )
+    .await;
+
+    let expected = vec![
+        
"+--------------------+-----------------+---------------+-------------+",
+        "| MAX(t.nanos)       | MAX(t.micros)   | MAX(t.millis) | MAX(t.secs) 
|",
+        
"+--------------------+-----------------+---------------+-------------+",
+        "| 21:06:28.247821084 | 21:06:28.247821 | 21:06:28.247  | 21:06:28    
|",
+        
"+--------------------+-----------------+---------------+-------------+",
+    ];
+    assert_batches_sorted_eq!(expected, &results);
+
+    Ok(())
+}
+
 #[tokio::test]
 async fn aggregate_timestamps_avg() -> Result<()> {
     let ctx = SessionContext::new();
diff --git a/datafusion/core/tests/sql/group_by.rs 
b/datafusion/core/tests/sql/group_by.rs
index 56044862c..fffcc356d 100644
--- a/datafusion/core/tests/sql/group_by.rs
+++ b/datafusion/core/tests/sql/group_by.rs
@@ -485,6 +485,186 @@ async fn csv_group_by_date() -> Result<()> {
     Ok(())
 }
 
+#[tokio::test]
+async fn csv_group_by_time32second() -> Result<()> {
+    let ctx = SessionContext::new();
+    let schema = Arc::new(Schema::new(vec![
+        Field::new("time", DataType::Time32(TimeUnit::Second), false),
+        Field::new("cnt", DataType::Int32, false),
+    ]));
+    let data = RecordBatch::try_new(
+        schema.clone(),
+        vec![
+            Arc::new(Time32SecondArray::from(vec![
+                Some(5_000),
+                Some(5_000),
+                Some(5_500),
+                Some(5_500),
+                Some(5_900),
+                Some(5_900),
+            ])),
+            Arc::new(Int32Array::from(vec![
+                Some(1),
+                Some(1),
+                Some(1),
+                Some(2),
+                Some(1),
+                Some(3),
+            ])),
+        ],
+    )?;
+
+    ctx.register_batch("times", data)?;
+    let sql = "SELECT SUM(cnt) FROM times GROUP BY time";
+    let actual = execute_to_batches(&ctx, sql).await;
+    let expected = vec![
+        "+----------------+",
+        "| SUM(times.cnt) |",
+        "+----------------+",
+        "| 2              |",
+        "| 3              |",
+        "| 4              |",
+        "+----------------+",
+    ];
+    assert_batches_sorted_eq!(expected, &actual);
+    Ok(())
+}
+
+#[tokio::test]
+async fn csv_group_by_time32millisecond() -> Result<()> {
+    let ctx = SessionContext::new();
+    let schema = Arc::new(Schema::new(vec![
+        Field::new("time", DataType::Time32(TimeUnit::Millisecond), false),
+        Field::new("cnt", DataType::Int32, false),
+    ]));
+    let data = RecordBatch::try_new(
+        schema.clone(),
+        vec![
+            Arc::new(Time32MillisecondArray::from(vec![
+                Some(5_000_000),
+                Some(5_000_000),
+                Some(5_500_000),
+                Some(5_500_000),
+                Some(5_900_000),
+                Some(5_900_000),
+            ])),
+            Arc::new(Int32Array::from(vec![
+                Some(1),
+                Some(1),
+                Some(1),
+                Some(2),
+                Some(1),
+                Some(3),
+            ])),
+        ],
+    )?;
+
+    ctx.register_batch("times", data)?;
+    let sql = "SELECT SUM(cnt) FROM times GROUP BY time";
+    let actual = execute_to_batches(&ctx, sql).await;
+    let expected = vec![
+        "+----------------+",
+        "| SUM(times.cnt) |",
+        "+----------------+",
+        "| 2              |",
+        "| 3              |",
+        "| 4              |",
+        "+----------------+",
+    ];
+    assert_batches_sorted_eq!(expected, &actual);
+    Ok(())
+}
+
+#[tokio::test]
+async fn csv_group_by_time64microsecond() -> Result<()> {
+    let ctx = SessionContext::new();
+    let schema = Arc::new(Schema::new(vec![
+        Field::new("time", DataType::Time64(TimeUnit::Microsecond), false),
+        Field::new("cnt", DataType::Int64, false),
+    ]));
+    let data = RecordBatch::try_new(
+        schema.clone(),
+        vec![
+            Arc::new(Time64MicrosecondArray::from(vec![
+                Some(5_000_000_000),
+                Some(5_000_000_000),
+                Some(5_500_000_000),
+                Some(5_500_000_000),
+                Some(5_900_000_000),
+                Some(5_900_000_000),
+            ])),
+            Arc::new(Int64Array::from(vec![
+                Some(1),
+                Some(1),
+                Some(1),
+                Some(2),
+                Some(1),
+                Some(3),
+            ])),
+        ],
+    )?;
+
+    ctx.register_batch("times", data)?;
+    let sql = "SELECT SUM(cnt) FROM times GROUP BY time";
+    let actual = execute_to_batches(&ctx, sql).await;
+    let expected = vec![
+        "+----------------+",
+        "| SUM(times.cnt) |",
+        "+----------------+",
+        "| 2              |",
+        "| 3              |",
+        "| 4              |",
+        "+----------------+",
+    ];
+    assert_batches_sorted_eq!(expected, &actual);
+    Ok(())
+}
+
+#[tokio::test]
+async fn csv_group_by_time64nanosecond() -> Result<()> {
+    let ctx = SessionContext::new();
+    let schema = Arc::new(Schema::new(vec![
+        Field::new("time", DataType::Time64(TimeUnit::Nanosecond), false),
+        Field::new("cnt", DataType::Int64, false),
+    ]));
+    let data = RecordBatch::try_new(
+        schema.clone(),
+        vec![
+            Arc::new(Time64NanosecondArray::from(vec![
+                Some(5_000_000_000_000),
+                Some(5_000_000_000_000),
+                Some(5_500_000_000_000),
+                Some(5_500_000_000_000),
+                Some(5_900_000_000_000),
+                Some(5_900_000_000_000),
+            ])),
+            Arc::new(Int64Array::from(vec![
+                Some(1),
+                Some(1),
+                Some(1),
+                Some(2),
+                Some(1),
+                Some(3),
+            ])),
+        ],
+    )?;
+
+    ctx.register_batch("times", data)?;
+    let sql = "SELECT SUM(cnt) FROM times GROUP BY time";
+    let actual = execute_to_batches(&ctx, sql).await;
+    let expected = vec![
+        "+----------------+",
+        "| SUM(times.cnt) |",
+        "+----------------+",
+        "| 2              |",
+        "| 3              |",
+        "| 4              |",
+        "+----------------+",
+    ];
+    assert_batches_sorted_eq!(expected, &actual);
+    Ok(())
+}
+
 #[tokio::test]
 async fn group_by_date_trunc() -> Result<()> {
     let tmp_dir = TempDir::new()?;
diff --git a/datafusion/core/tests/sql/mod.rs b/datafusion/core/tests/sql/mod.rs
index 8466e795c..619270f20 100644
--- a/datafusion/core/tests/sql/mod.rs
+++ b/datafusion/core/tests/sql/mod.rs
@@ -1181,6 +1181,99 @@ pub fn make_timestamps() -> RecordBatch {
     .unwrap()
 }
 
+/// Return a new table provider containing all of the supported timestamp types
+pub fn table_with_times() -> Arc<dyn TableProvider> {
+    let batch = make_times();
+    let schema = batch.schema();
+    let partitions = vec![vec![batch]];
+    Arc::new(MemTable::try_new(schema, partitions).unwrap())
+}
+
+/// Return  record batch with all of the supported time types
+/// values
+///
+/// Columns are named:
+/// "nanos" --> Time64NanosecondArray
+/// "micros" --> Time64MicrosecondArray
+/// "millis" --> Time32MillisecondArray
+/// "secs" --> Time32SecondArray
+/// "names" --> StringArray
+pub fn make_times() -> RecordBatch {
+    let ts_strings = vec![
+        Some("18:06:30.243620451"),
+        Some("20:08:28.161121654"),
+        Some("19:11:04.156423842"),
+        Some("21:06:28.247821084"),
+    ];
+
+    let ts_nanos = ts_strings
+        .into_iter()
+        .map(|t| {
+            t.map(|t| {
+                let integer_sec = t
+                    .parse::<chrono::NaiveTime>()
+                    .unwrap()
+                    .num_seconds_from_midnight() as i64;
+                let extra_nano =
+                    t.parse::<chrono::NaiveTime>().unwrap().nanosecond() as 
i64;
+                // Total time in nanoseconds given by integer number of 
seconds multiplied by 10^9
+                // plus number of nanoseconds corresponding to the extra 
fraction of second
+                integer_sec * 1_000_000_000 + extra_nano
+            })
+        })
+        .collect::<Vec<_>>();
+
+    let ts_micros = ts_nanos
+        .iter()
+        .map(|t| t.as_ref().map(|ts_nanos| ts_nanos / 1000))
+        .collect::<Vec<_>>();
+
+    let ts_millis = ts_nanos
+        .iter()
+        .map(|t| t.as_ref().map(|ts_nanos| { ts_nanos / 1000000 } as i32))
+        .collect::<Vec<_>>();
+
+    let ts_secs = ts_nanos
+        .iter()
+        .map(|t| t.as_ref().map(|ts_nanos| { ts_nanos / 1000000000 } as i32))
+        .collect::<Vec<_>>();
+
+    let names = ts_nanos
+        .iter()
+        .enumerate()
+        .map(|(i, _)| format!("Row {}", i))
+        .collect::<Vec<_>>();
+
+    let arr_nanos = Time64NanosecondArray::from(ts_nanos);
+    let arr_micros = Time64MicrosecondArray::from(ts_micros);
+    let arr_millis = Time32MillisecondArray::from(ts_millis);
+    let arr_secs = Time32SecondArray::from(ts_secs);
+
+    let names = names.iter().map(|s| s.as_str()).collect::<Vec<_>>();
+    let arr_names = StringArray::from(names);
+
+    let schema = Schema::new(vec![
+        Field::new("nanos", arr_nanos.data_type().clone(), true),
+        Field::new("micros", arr_micros.data_type().clone(), true),
+        Field::new("millis", arr_millis.data_type().clone(), true),
+        Field::new("secs", arr_secs.data_type().clone(), true),
+        Field::new("name", arr_names.data_type().clone(), true),
+    ]);
+    let schema = Arc::new(schema);
+
+    RecordBatch::try_new(
+        schema,
+        vec![
+            Arc::new(arr_nanos),
+            Arc::new(arr_micros),
+            Arc::new(arr_millis),
+            Arc::new(arr_secs),
+            Arc::new(arr_names),
+        ],
+    )
+    .unwrap()
+}
+
 #[tokio::test]
 async fn nyc() -> Result<()> {
     // schema for nyxtaxi csv files
diff --git a/datafusion/core/tests/sql/select.rs 
b/datafusion/core/tests/sql/select.rs
index c3890fac4..6d7014507 100644
--- a/datafusion/core/tests/sql/select.rs
+++ b/datafusion/core/tests/sql/select.rs
@@ -909,6 +909,182 @@ async fn query_on_string_dictionary() -> Result<()> {
     Ok(())
 }
 
+#[tokio::test]
+async fn filter_with_time32second() -> Result<()> {
+    let ctx = SessionContext::new();
+    let schema = Arc::new(Schema::new(vec![
+        Field::new("time", DataType::Time32(TimeUnit::Second), false),
+        Field::new("value", DataType::Int64, false),
+    ]));
+    let data = RecordBatch::try_new(
+        schema.clone(),
+        vec![
+            Arc::new(Time32SecondArray::from(vec![
+                Some(5_000),
+                Some(5_000),
+                Some(5_500),
+                Some(5_500),
+                Some(5_900),
+                Some(5_900),
+            ])),
+            Arc::new(Int64Array::from(vec![
+                Some(2505),
+                Some(2436),
+                Some(2384),
+                Some(1815),
+                Some(2330),
+                Some(2065),
+            ])),
+        ],
+    )?;
+
+    ctx.register_batch("temporal", data)?;
+    let sql = "SELECT value FROM temporal WHERE time = '01:23:20'";
+    let actual = execute_to_batches(&ctx, sql).await;
+    let expected = vec![
+        "+-------+",
+        "| value |",
+        "+-------+",
+        "| 2436  |",
+        "| 2505  |",
+        "+-------+",
+    ];
+    assert_batches_sorted_eq!(expected, &actual);
+    Ok(())
+}
+
+#[tokio::test]
+async fn filter_with_time32millisecond() -> Result<()> {
+    let ctx = SessionContext::new();
+    let schema = Arc::new(Schema::new(vec![
+        Field::new("time", DataType::Time32(TimeUnit::Millisecond), false),
+        Field::new("value", DataType::Int64, false),
+    ]));
+    let data = RecordBatch::try_new(
+        schema.clone(),
+        vec![
+            Arc::new(Time32MillisecondArray::from(vec![
+                Some(5_000_000),
+                Some(5_000_000),
+                Some(5_500_000),
+                Some(5_500_000),
+                Some(5_900_000),
+                Some(5_900_000),
+            ])),
+            Arc::new(Int64Array::from(vec![
+                Some(2505),
+                Some(2436),
+                Some(2384),
+                Some(1815),
+                Some(2330),
+                Some(2065),
+            ])),
+        ],
+    )?;
+
+    ctx.register_batch("temporal", data)?;
+    let sql = "SELECT value FROM temporal WHERE time = '01:23:20'";
+    let actual = execute_to_batches(&ctx, sql).await;
+    let expected = vec![
+        "+-------+",
+        "| value |",
+        "+-------+",
+        "| 2436  |",
+        "| 2505  |",
+        "+-------+",
+    ];
+    assert_batches_sorted_eq!(expected, &actual);
+    Ok(())
+}
+
+#[tokio::test]
+async fn filter_with_time64microsecond() -> Result<()> {
+    let ctx = SessionContext::new();
+    let schema = Arc::new(Schema::new(vec![
+        Field::new("time", DataType::Time64(TimeUnit::Microsecond), false),
+        Field::new("value", DataType::Int64, false),
+    ]));
+    let data = RecordBatch::try_new(
+        schema.clone(),
+        vec![
+            Arc::new(Time64MicrosecondArray::from(vec![
+                Some(5_000_000_000),
+                Some(5_000_000_000),
+                Some(5_500_000_000),
+                Some(5_500_000_000),
+                Some(5_900_000_000),
+                Some(5_900_000_000),
+            ])),
+            Arc::new(Int64Array::from(vec![
+                Some(2505),
+                Some(2436),
+                Some(2384),
+                Some(1815),
+                Some(2330),
+                Some(2065),
+            ])),
+        ],
+    )?;
+
+    ctx.register_batch("temporal", data)?;
+    let sql = "SELECT value FROM temporal WHERE time = '01:23:20'";
+    let actual = execute_to_batches(&ctx, sql).await;
+    let expected = vec![
+        "+-------+",
+        "| value |",
+        "+-------+",
+        "| 2436  |",
+        "| 2505  |",
+        "+-------+",
+    ];
+    assert_batches_sorted_eq!(expected, &actual);
+    Ok(())
+}
+
+#[tokio::test]
+async fn filter_with_time64nanosecond() -> Result<()> {
+    let ctx = SessionContext::new();
+    let schema = Arc::new(Schema::new(vec![
+        Field::new("time", DataType::Time64(TimeUnit::Nanosecond), false),
+        Field::new("value", DataType::Int64, false),
+    ]));
+    let data = RecordBatch::try_new(
+        schema.clone(),
+        vec![
+            Arc::new(Time64NanosecondArray::from(vec![
+                Some(5_000_000_000_000),
+                Some(5_000_000_000_000),
+                Some(5_500_000_000_000),
+                Some(5_500_000_000_000),
+                Some(5_900_000_000_000),
+                Some(5_900_000_000_000),
+            ])),
+            Arc::new(Int64Array::from(vec![
+                Some(2505),
+                Some(2436),
+                Some(2384),
+                Some(1815),
+                Some(2330),
+                Some(2065),
+            ])),
+        ],
+    )?;
+
+    ctx.register_batch("temporal", data)?;
+    let sql = "SELECT value FROM temporal WHERE time = '01:23:20'";
+    let actual = execute_to_batches(&ctx, sql).await;
+    let expected = vec![
+        "+-------+",
+        "| value |",
+        "+-------+",
+        "| 2436  |",
+        "| 2505  |",
+        "+-------+",
+    ];
+    assert_batches_sorted_eq!(expected, &actual);
+    Ok(())
+}
+
 #[tokio::test]
 async fn query_cte_with_alias() -> Result<()> {
     let ctx = SessionContext::new();
diff --git a/datafusion/expr/src/type_coercion/binary.rs 
b/datafusion/expr/src/type_coercion/binary.rs
index 911874fb0..70e09f2fb 100644
--- a/datafusion/expr/src/type_coercion/binary.rs
+++ b/datafusion/expr/src/type_coercion/binary.rs
@@ -20,7 +20,9 @@
 use crate::type_coercion::is_numeric;
 use crate::Operator;
 use arrow::compute::can_cast_types;
-use arrow::datatypes::{DataType, DECIMAL128_MAX_PRECISION, 
DECIMAL128_MAX_SCALE};
+use arrow::datatypes::{
+    DataType, TimeUnit, DECIMAL128_MAX_PRECISION, DECIMAL128_MAX_SCALE,
+};
 use datafusion_common::DataFusionError;
 use datafusion_common::Result;
 
@@ -517,11 +519,23 @@ fn like_coercion(lhs_type: &DataType, rhs_type: 
&DataType) -> Option<DataType> {
         .or_else(|| null_coercion(lhs_type, rhs_type))
 }
 
+/// Checks if the TimeUnit associated with a Time32 or Time64 type is 
consistent,
+/// as Time32 can only be used to Second and Millisecond accuracy, while Time64
+/// is exclusively used to Microsecond and Nanosecond accuracy
+fn is_time_with_valid_unit(datatype: DataType) -> bool {
+    matches!(
+        datatype,
+        DataType::Time32(TimeUnit::Second)
+            | DataType::Time32(TimeUnit::Millisecond)
+            | DataType::Time64(TimeUnit::Microsecond)
+            | DataType::Time64(TimeUnit::Nanosecond)
+    )
+}
+
 /// Coercion rules for Temporal columns: the type that both lhs and rhs can be
 /// casted to for the purpose of a date computation
 fn temporal_coercion(lhs_type: &DataType, rhs_type: &DataType) -> 
Option<DataType> {
     use arrow::datatypes::DataType::*;
-    use arrow::datatypes::TimeUnit;
     match (lhs_type, rhs_type) {
         (Date64, Date32) => Some(Date64),
         (Date32, Date64) => Some(Date64),
@@ -529,6 +543,22 @@ fn temporal_coercion(lhs_type: &DataType, rhs_type: 
&DataType) -> Option<DataTyp
         (Date32, Utf8) => Some(Date32),
         (Utf8, Date64) => Some(Date64),
         (Date64, Utf8) => Some(Date64),
+        (Utf8, Time32(unit)) => match 
is_time_with_valid_unit(Time32(unit.clone())) {
+            false => None,
+            true => Some(Time32(unit.clone())),
+        },
+        (Time32(unit), Utf8) => match 
is_time_with_valid_unit(Time32(unit.clone())) {
+            false => None,
+            true => Some(Time32(unit.clone())),
+        },
+        (Utf8, Time64(unit)) => match 
is_time_with_valid_unit(Time64(unit.clone())) {
+            false => None,
+            true => Some(Time64(unit.clone())),
+        },
+        (Time64(unit), Utf8) => match 
is_time_with_valid_unit(Time64(unit.clone())) {
+            false => None,
+            true => Some(Time64(unit.clone())),
+        },
         (Timestamp(lhs_unit, lhs_tz), Timestamp(rhs_unit, rhs_tz)) => {
             let tz = match (lhs_tz, rhs_tz) {
                 // can't cast across timezones
@@ -830,6 +860,30 @@ mod tests {
             Operator::Lt,
             DataType::Date64
         );
+        test_coercion_binary_rule!(
+            DataType::Utf8,
+            DataType::Time32(TimeUnit::Second),
+            Operator::Eq,
+            DataType::Time32(TimeUnit::Second)
+        );
+        test_coercion_binary_rule!(
+            DataType::Utf8,
+            DataType::Time32(TimeUnit::Millisecond),
+            Operator::Eq,
+            DataType::Time32(TimeUnit::Millisecond)
+        );
+        test_coercion_binary_rule!(
+            DataType::Utf8,
+            DataType::Time64(TimeUnit::Microsecond),
+            Operator::Eq,
+            DataType::Time64(TimeUnit::Microsecond)
+        );
+        test_coercion_binary_rule!(
+            DataType::Utf8,
+            DataType::Time64(TimeUnit::Nanosecond),
+            Operator::Eq,
+            DataType::Time64(TimeUnit::Nanosecond)
+        );
         test_coercion_binary_rule!(
             DataType::Utf8,
             DataType::Utf8,
diff --git a/datafusion/physical-expr/src/aggregate/min_max.rs 
b/datafusion/physical-expr/src/aggregate/min_max.rs
index c415e7db5..2d6961bfc 100644
--- a/datafusion/physical-expr/src/aggregate/min_max.rs
+++ b/datafusion/physical-expr/src/aggregate/min_max.rs
@@ -28,6 +28,7 @@ use arrow::{
     array::{
         ArrayRef, Date32Array, Date64Array, Float32Array, Float64Array, 
Int16Array,
         Int32Array, Int64Array, Int8Array, LargeStringArray, StringArray,
+        Time32MillisecondArray, Time32SecondArray, Time64MicrosecondArray,
         Time64NanosecondArray, TimestampMicrosecondArray, 
TimestampMillisecondArray,
         TimestampNanosecondArray, TimestampSecondArray, UInt16Array, 
UInt32Array,
         UInt64Array, UInt8Array,
@@ -251,8 +252,32 @@ macro_rules! min_max_batch {
             ),
             DataType::Date32 => typed_min_max_batch!($VALUES, Date32Array, 
Date32, $OP),
             DataType::Date64 => typed_min_max_batch!($VALUES, Date64Array, 
Date64, $OP),
+            DataType::Time32(TimeUnit::Second) => {
+                typed_min_max_batch!($VALUES, Time32SecondArray, Time32Second, 
$OP)
+            }
+            DataType::Time32(TimeUnit::Millisecond) => {
+                typed_min_max_batch!(
+                    $VALUES,
+                    Time32MillisecondArray,
+                    Time32Millisecond,
+                    $OP
+                )
+            }
+            DataType::Time64(TimeUnit::Microsecond) => {
+                typed_min_max_batch!(
+                    $VALUES,
+                    Time64MicrosecondArray,
+                    Time64Microsecond,
+                    $OP
+                )
+            }
             DataType::Time64(TimeUnit::Nanosecond) => {
-                typed_min_max_batch!($VALUES, Time64NanosecondArray, Time64, 
$OP)
+                typed_min_max_batch!(
+                    $VALUES,
+                    Time64NanosecondArray,
+                    Time64Nanosecond,
+                    $OP
+                )
             }
             other => {
                 // This should have been handled before
@@ -417,10 +442,28 @@ macro_rules! min_max {
                 typed_min_max!(lhs, rhs, Date64, $OP)
             }
             (
-                ScalarValue::Time64(lhs),
-                ScalarValue::Time64(rhs),
+                ScalarValue::Time32Second(lhs),
+                ScalarValue::Time32Second(rhs),
+            ) => {
+                typed_min_max!(lhs, rhs, Time32Second, $OP)
+            }
+            (
+                ScalarValue::Time32Millisecond(lhs),
+                ScalarValue::Time32Millisecond(rhs),
             ) => {
-                typed_min_max!(lhs, rhs, Time64, $OP)
+                typed_min_max!(lhs, rhs, Time32Millisecond, $OP)
+            }
+            (
+                ScalarValue::Time64Microsecond(lhs),
+                ScalarValue::Time64Microsecond(rhs),
+            ) => {
+                typed_min_max!(lhs, rhs, Time64Microsecond, $OP)
+            }
+            (
+                ScalarValue::Time64Nanosecond(lhs),
+                ScalarValue::Time64Nanosecond(rhs),
+            ) => {
+                typed_min_max!(lhs, rhs, Time64Nanosecond, $OP)
             }
             e => {
                 return Err(DataFusionError::Internal(format!(
@@ -1073,24 +1116,90 @@ mod tests {
     }
 
     #[test]
-    fn min_time64() -> Result<()> {
+    fn min_time32second() -> Result<()> {
+        let a: ArrayRef = Arc::new(Time32SecondArray::from(vec![1, 2, 3, 4, 
5]));
+        generic_test_op!(
+            a,
+            DataType::Time32(TimeUnit::Second),
+            Min,
+            ScalarValue::Time32Second(Some(1))
+        )
+    }
+
+    #[test]
+    fn max_time32second() -> Result<()> {
+        let a: ArrayRef = Arc::new(Time32SecondArray::from(vec![1, 2, 3, 4, 
5]));
+        generic_test_op!(
+            a,
+            DataType::Time32(TimeUnit::Second),
+            Max,
+            ScalarValue::Time32Second(Some(5))
+        )
+    }
+
+    #[test]
+    fn min_time32millisecond() -> Result<()> {
+        let a: ArrayRef = Arc::new(Time32MillisecondArray::from(vec![1, 2, 3, 
4, 5]));
+        generic_test_op!(
+            a,
+            DataType::Time32(TimeUnit::Millisecond),
+            Min,
+            ScalarValue::Time32Millisecond(Some(1))
+        )
+    }
+
+    #[test]
+    fn max_time32millisecond() -> Result<()> {
+        let a: ArrayRef = Arc::new(Time32MillisecondArray::from(vec![1, 2, 3, 
4, 5]));
+        generic_test_op!(
+            a,
+            DataType::Time32(TimeUnit::Millisecond),
+            Max,
+            ScalarValue::Time32Millisecond(Some(5))
+        )
+    }
+
+    #[test]
+    fn min_time64microsecond() -> Result<()> {
+        let a: ArrayRef = Arc::new(Time64MicrosecondArray::from(vec![1, 2, 3, 
4, 5]));
+        generic_test_op!(
+            a,
+            DataType::Time64(TimeUnit::Microsecond),
+            Min,
+            ScalarValue::Time64Microsecond(Some(1))
+        )
+    }
+
+    #[test]
+    fn max_time64microsecond() -> Result<()> {
+        let a: ArrayRef = Arc::new(Time64MicrosecondArray::from(vec![1, 2, 3, 
4, 5]));
+        generic_test_op!(
+            a,
+            DataType::Time64(TimeUnit::Microsecond),
+            Max,
+            ScalarValue::Time64Microsecond(Some(5))
+        )
+    }
+
+    #[test]
+    fn min_time64nanosecond() -> Result<()> {
         let a: ArrayRef = Arc::new(Time64NanosecondArray::from(vec![1, 2, 3, 
4, 5]));
         generic_test_op!(
             a,
             DataType::Time64(TimeUnit::Nanosecond),
-            Max,
-            ScalarValue::Time64(Some(5))
+            Min,
+            ScalarValue::Time64Nanosecond(Some(1))
         )
     }
 
     #[test]
-    fn max_time64() -> Result<()> {
+    fn max_time64nanosecond() -> Result<()> {
         let a: ArrayRef = Arc::new(Time64NanosecondArray::from(vec![1, 2, 3, 
4, 5]));
         generic_test_op!(
             a,
             DataType::Time64(TimeUnit::Nanosecond),
             Max,
-            ScalarValue::Time64(Some(5))
+            ScalarValue::Time64Nanosecond(Some(5))
         )
     }
 }
diff --git a/datafusion/physical-expr/src/datetime_expressions.rs 
b/datafusion/physical-expr/src/datetime_expressions.rs
index 0a76edde5..02d9b9e82 100644
--- a/datafusion/physical-expr/src/datetime_expressions.rs
+++ b/datafusion/physical-expr/src/datetime_expressions.rs
@@ -213,7 +213,7 @@ pub fn make_current_time(
     now_ts: DateTime<Utc>,
 ) -> impl Fn(&[ColumnarValue]) -> Result<ColumnarValue> {
     let nano = Some(now_ts.timestamp_nanos() % 86400000000000);
-    move |_arg| Ok(ColumnarValue::Scalar(ScalarValue::Time64(nano)))
+    move |_arg| Ok(ColumnarValue::Scalar(ScalarValue::Time64Nanosecond(nano)))
 }
 
 fn quarter_month(date: &NaiveDateTime) -> u32 {
diff --git a/datafusion/physical-expr/src/expressions/binary.rs 
b/datafusion/physical-expr/src/expressions/binary.rs
index 2d5f0854a..dc8ee635f 100644
--- a/datafusion/physical-expr/src/expressions/binary.rs
+++ b/datafusion/physical-expr/src/expressions/binary.rs
@@ -458,6 +458,18 @@ macro_rules! binary_array_op {
             DataType::Date64 => {
                 compute_op!($LEFT, $RIGHT, $OP, Date64Array)
             }
+            DataType::Time32(TimeUnit::Second) => {
+                compute_op!($LEFT, $RIGHT, $OP, Time32SecondArray)
+            }
+            DataType::Time32(TimeUnit::Millisecond) => {
+                compute_op!($LEFT, $RIGHT, $OP, Time32MillisecondArray)
+            }
+            DataType::Time64(TimeUnit::Microsecond) => {
+                compute_op!($LEFT, $RIGHT, $OP, Time64MicrosecondArray)
+            }
+            DataType::Time64(TimeUnit::Nanosecond) => {
+                compute_op!($LEFT, $RIGHT, $OP, Time64NanosecondArray)
+            }
             DataType::Boolean => compute_bool_op!($LEFT, $RIGHT, $OP, 
BooleanArray),
             other => Err(DataFusionError::Internal(format!(
                 "Data type {:?} not supported for binary operation '{}' on dyn 
arrays",
@@ -806,6 +818,10 @@ macro_rules! binary_array_op_dyn_scalar {
             ScalarValue::Float64(v) => compute_op_dyn_scalar!($LEFT, v, $OP, 
$OP_TYPE),
             ScalarValue::Date32(_) => compute_op_scalar!($LEFT, right, $OP, 
Date32Array),
             ScalarValue::Date64(_) => compute_op_scalar!($LEFT, right, $OP, 
Date64Array),
+            ScalarValue::Time32Second(_) => compute_op_scalar!($LEFT, right, 
$OP, Time32SecondArray),
+            ScalarValue::Time32Millisecond(_) => compute_op_scalar!($LEFT, 
right, $OP, Time32MillisecondArray),
+            ScalarValue::Time64Microsecond(_) => compute_op_scalar!($LEFT, 
right, $OP, Time64MicrosecondArray),
+            ScalarValue::Time64Nanosecond(_) => compute_op_scalar!($LEFT, 
right, $OP, Time64NanosecondArray),
             ScalarValue::TimestampSecond(..) => compute_op_scalar!($LEFT, 
right, $OP, TimestampSecondArray),
             ScalarValue::TimestampMillisecond(..) => compute_op_scalar!($LEFT, 
right, $OP, TimestampMillisecondArray),
             ScalarValue::TimestampMicrosecond(..) => compute_op_scalar!($LEFT, 
right, $OP, TimestampMicrosecondArray),
diff --git a/datafusion/proto/proto/datafusion.proto 
b/datafusion/proto/proto/datafusion.proto
index 48716c558..b1ba0ec2d 100644
--- a/datafusion/proto/proto/datafusion.proto
+++ b/datafusion/proto/proto/datafusion.proto
@@ -746,10 +746,24 @@ message ScalarListValue{
     repeated ScalarValue values = 2;
 }
 
+message ScalarTime32Value {
+  oneof value {
+    int32 time32_second_value = 1;
+    int32 time32_millisecond_value = 2;
+  };
+}
+
+message ScalarTime64Value {
+  oneof value {
+    int64 time64_microsecond_value = 1;
+    int64 time64_nanosecond_value = 2;
+  };
+}
+
 message ScalarTimestampValue {
   oneof value {
-    int64  time_microsecond_value = 1;
-    int64  time_nanosecond_value = 2;
+    int64 time_microsecond_value = 1;
+    int64 time_nanosecond_value = 2;
     int64 time_second_value = 3;
     int64 time_millisecond_value = 4;
   };
@@ -804,6 +818,7 @@ message ScalarValue{
         double float64_value = 13;
         // Literal Date32 value always has a unit of day
         int32  date_32_value = 14;
+        ScalarTime32Value time32_value = 15;
         ScalarListValue list_value = 17;
         //WAS: ScalarType null_list_value = 18;
 
@@ -815,7 +830,7 @@ message ScalarValue{
         ScalarDictionaryValue dictionary_value = 27;
         bytes binary_value = 28;
         bytes large_binary_value = 29;
-        int64 time64_value = 30;
+        ScalarTime64Value time64_value = 30;
         IntervalMonthDayNanoValue interval_month_day_nano = 31;
         StructValue struct_value = 32;
         ScalarFixedSizeBinary fixed_size_binary_value = 34;
diff --git a/datafusion/proto/src/from_proto.rs 
b/datafusion/proto/src/from_proto.rs
index 8f6377739..1de84ad94 100644
--- a/datafusion/proto/src/from_proto.rs
+++ b/datafusion/proto/src/from_proto.rs
@@ -583,7 +583,30 @@ impl TryFrom<&protobuf::ScalarValue> for ScalarValue {
                 )
             }
             Value::Date64Value(v) => Self::Date64(Some(*v)),
-            Value::Time64Value(v) => Self::Time64(Some(*v)),
+            Value::Time32Value(v) => {
+                let time_value =
+                    v.value.as_ref().ok_or_else(|| Error::required("value"))?;
+                match time_value {
+                    protobuf::scalar_time32_value::Value::Time32SecondValue(t) 
=> {
+                        Self::Time32Second(Some(*t))
+                    }
+                    
protobuf::scalar_time32_value::Value::Time32MillisecondValue(t) => {
+                        Self::Time32Millisecond(Some(*t))
+                    }
+                }
+            }
+            Value::Time64Value(v) => {
+                let time_value =
+                    v.value.as_ref().ok_or_else(|| Error::required("value"))?;
+                match time_value {
+                    
protobuf::scalar_time64_value::Value::Time64MicrosecondValue(t) => {
+                        Self::Time64Microsecond(Some(*t))
+                    }
+                    
protobuf::scalar_time64_value::Value::Time64NanosecondValue(t) => {
+                        Self::Time64Nanosecond(Some(*t))
+                    }
+                }
+            }
             Value::IntervalYearmonthValue(v) => 
Self::IntervalYearMonth(Some(*v)),
             Value::IntervalDaytimeValue(v) => Self::IntervalDayTime(Some(*v)),
             Value::TimestampValue(v) => {
diff --git a/datafusion/proto/src/generated/pbjson.rs 
b/datafusion/proto/src/generated/pbjson.rs
index 6f9636f6b..ee0142011 100644
--- a/datafusion/proto/src/generated/pbjson.rs
+++ b/datafusion/proto/src/generated/pbjson.rs
@@ -10085,6 +10085,224 @@ impl<'de> serde::Deserialize<'de> for ScalarListValue 
{
         deserializer.deserialize_struct("datafusion.ScalarListValue", FIELDS, 
GeneratedVisitor)
     }
 }
+impl serde::Serialize for ScalarTime32Value {
+    #[allow(deprecated)]
+    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, 
S::Error>
+    where
+        S: serde::Serializer,
+    {
+        use serde::ser::SerializeStruct;
+        let mut len = 0;
+        if self.value.is_some() {
+            len += 1;
+        }
+        let mut struct_ser = 
serializer.serialize_struct("datafusion.ScalarTime32Value", len)?;
+        if let Some(v) = self.value.as_ref() {
+            match v {
+                scalar_time32_value::Value::Time32SecondValue(v) => {
+                    struct_ser.serialize_field("time32SecondValue", v)?;
+                }
+                scalar_time32_value::Value::Time32MillisecondValue(v) => {
+                    struct_ser.serialize_field("time32MillisecondValue", v)?;
+                }
+            }
+        }
+        struct_ser.end()
+    }
+}
+impl<'de> serde::Deserialize<'de> for ScalarTime32Value {
+    #[allow(deprecated)]
+    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        const FIELDS: &[&str] = &[
+            "time32_second_value",
+            "time32SecondValue",
+            "time32_millisecond_value",
+            "time32MillisecondValue",
+        ];
+
+        #[allow(clippy::enum_variant_names)]
+        enum GeneratedField {
+            Time32SecondValue,
+            Time32MillisecondValue,
+        }
+        impl<'de> serde::Deserialize<'de> for GeneratedField {
+            fn deserialize<D>(deserializer: D) -> 
std::result::Result<GeneratedField, D::Error>
+            where
+                D: serde::Deserializer<'de>,
+            {
+                struct GeneratedVisitor;
+
+                impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+                    type Value = GeneratedField;
+
+                    fn expecting(&self, formatter: &mut 
std::fmt::Formatter<'_>) -> std::fmt::Result {
+                        write!(formatter, "expected one of: {:?}", &FIELDS)
+                    }
+
+                    #[allow(unused_variables)]
+                    fn visit_str<E>(self, value: &str) -> 
std::result::Result<GeneratedField, E>
+                    where
+                        E: serde::de::Error,
+                    {
+                        match value {
+                            "time32SecondValue" | "time32_second_value" => 
Ok(GeneratedField::Time32SecondValue),
+                            "time32MillisecondValue" | 
"time32_millisecond_value" => Ok(GeneratedField::Time32MillisecondValue),
+                            _ => Err(serde::de::Error::unknown_field(value, 
FIELDS)),
+                        }
+                    }
+                }
+                deserializer.deserialize_identifier(GeneratedVisitor)
+            }
+        }
+        struct GeneratedVisitor;
+        impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+            type Value = ScalarTime32Value;
+
+            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> 
std::fmt::Result {
+                formatter.write_str("struct datafusion.ScalarTime32Value")
+            }
+
+            fn visit_map<V>(self, mut map: V) -> 
std::result::Result<ScalarTime32Value, V::Error>
+                where
+                    V: serde::de::MapAccess<'de>,
+            {
+                let mut value__ = None;
+                while let Some(k) = map.next_key()? {
+                    match k {
+                        GeneratedField::Time32SecondValue => {
+                            if value__.is_some() {
+                                return 
Err(serde::de::Error::duplicate_field("time32SecondValue"));
+                            }
+                            value__ = 
map.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x|
 scalar_time32_value::Value::Time32SecondValue(x.0));
+                        }
+                        GeneratedField::Time32MillisecondValue => {
+                            if value__.is_some() {
+                                return 
Err(serde::de::Error::duplicate_field("time32MillisecondValue"));
+                            }
+                            value__ = 
map.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x|
 scalar_time32_value::Value::Time32MillisecondValue(x.0));
+                        }
+                    }
+                }
+                Ok(ScalarTime32Value {
+                    value: value__,
+                })
+            }
+        }
+        deserializer.deserialize_struct("datafusion.ScalarTime32Value", 
FIELDS, GeneratedVisitor)
+    }
+}
+impl serde::Serialize for ScalarTime64Value {
+    #[allow(deprecated)]
+    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, 
S::Error>
+    where
+        S: serde::Serializer,
+    {
+        use serde::ser::SerializeStruct;
+        let mut len = 0;
+        if self.value.is_some() {
+            len += 1;
+        }
+        let mut struct_ser = 
serializer.serialize_struct("datafusion.ScalarTime64Value", len)?;
+        if let Some(v) = self.value.as_ref() {
+            match v {
+                scalar_time64_value::Value::Time64MicrosecondValue(v) => {
+                    struct_ser.serialize_field("time64MicrosecondValue", 
ToString::to_string(&v).as_str())?;
+                }
+                scalar_time64_value::Value::Time64NanosecondValue(v) => {
+                    struct_ser.serialize_field("time64NanosecondValue", 
ToString::to_string(&v).as_str())?;
+                }
+            }
+        }
+        struct_ser.end()
+    }
+}
+impl<'de> serde::Deserialize<'de> for ScalarTime64Value {
+    #[allow(deprecated)]
+    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        const FIELDS: &[&str] = &[
+            "time64_microsecond_value",
+            "time64MicrosecondValue",
+            "time64_nanosecond_value",
+            "time64NanosecondValue",
+        ];
+
+        #[allow(clippy::enum_variant_names)]
+        enum GeneratedField {
+            Time64MicrosecondValue,
+            Time64NanosecondValue,
+        }
+        impl<'de> serde::Deserialize<'de> for GeneratedField {
+            fn deserialize<D>(deserializer: D) -> 
std::result::Result<GeneratedField, D::Error>
+            where
+                D: serde::Deserializer<'de>,
+            {
+                struct GeneratedVisitor;
+
+                impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+                    type Value = GeneratedField;
+
+                    fn expecting(&self, formatter: &mut 
std::fmt::Formatter<'_>) -> std::fmt::Result {
+                        write!(formatter, "expected one of: {:?}", &FIELDS)
+                    }
+
+                    #[allow(unused_variables)]
+                    fn visit_str<E>(self, value: &str) -> 
std::result::Result<GeneratedField, E>
+                    where
+                        E: serde::de::Error,
+                    {
+                        match value {
+                            "time64MicrosecondValue" | 
"time64_microsecond_value" => Ok(GeneratedField::Time64MicrosecondValue),
+                            "time64NanosecondValue" | 
"time64_nanosecond_value" => Ok(GeneratedField::Time64NanosecondValue),
+                            _ => Err(serde::de::Error::unknown_field(value, 
FIELDS)),
+                        }
+                    }
+                }
+                deserializer.deserialize_identifier(GeneratedVisitor)
+            }
+        }
+        struct GeneratedVisitor;
+        impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
+            type Value = ScalarTime64Value;
+
+            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> 
std::fmt::Result {
+                formatter.write_str("struct datafusion.ScalarTime64Value")
+            }
+
+            fn visit_map<V>(self, mut map: V) -> 
std::result::Result<ScalarTime64Value, V::Error>
+                where
+                    V: serde::de::MapAccess<'de>,
+            {
+                let mut value__ = None;
+                while let Some(k) = map.next_key()? {
+                    match k {
+                        GeneratedField::Time64MicrosecondValue => {
+                            if value__.is_some() {
+                                return 
Err(serde::de::Error::duplicate_field("time64MicrosecondValue"));
+                            }
+                            value__ = 
map.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x|
 scalar_time64_value::Value::Time64MicrosecondValue(x.0));
+                        }
+                        GeneratedField::Time64NanosecondValue => {
+                            if value__.is_some() {
+                                return 
Err(serde::de::Error::duplicate_field("time64NanosecondValue"));
+                            }
+                            value__ = 
map.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x|
 scalar_time64_value::Value::Time64NanosecondValue(x.0));
+                        }
+                    }
+                }
+                Ok(ScalarTime64Value {
+                    value: value__,
+                })
+            }
+        }
+        deserializer.deserialize_struct("datafusion.ScalarTime64Value", 
FIELDS, GeneratedVisitor)
+    }
+}
 impl serde::Serialize for ScalarTimestampValue {
     #[allow(deprecated)]
     fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, 
S::Error>
@@ -10405,6 +10623,9 @@ impl serde::Serialize for ScalarValue {
                 scalar_value::Value::Date32Value(v) => {
                     struct_ser.serialize_field("date32Value", v)?;
                 }
+                scalar_value::Value::Time32Value(v) => {
+                    struct_ser.serialize_field("time32Value", v)?;
+                }
                 scalar_value::Value::ListValue(v) => {
                     struct_ser.serialize_field("listValue", v)?;
                 }
@@ -10433,7 +10654,7 @@ impl serde::Serialize for ScalarValue {
                     struct_ser.serialize_field("largeBinaryValue", 
pbjson::private::base64::encode(&v).as_str())?;
                 }
                 scalar_value::Value::Time64Value(v) => {
-                    struct_ser.serialize_field("time64Value", 
ToString::to_string(&v).as_str())?;
+                    struct_ser.serialize_field("time64Value", v)?;
                 }
                 scalar_value::Value::IntervalMonthDayNano(v) => {
                     struct_ser.serialize_field("intervalMonthDayNano", v)?;
@@ -10486,6 +10707,8 @@ impl<'de> serde::Deserialize<'de> for ScalarValue {
             "float64Value",
             "date_32_value",
             "date32Value",
+            "time32_value",
+            "time32Value",
             "list_value",
             "listValue",
             "decimal128_value",
@@ -10531,6 +10754,7 @@ impl<'de> serde::Deserialize<'de> for ScalarValue {
             Float32Value,
             Float64Value,
             Date32Value,
+            Time32Value,
             ListValue,
             Decimal128Value,
             Date64Value,
@@ -10580,6 +10804,7 @@ impl<'de> serde::Deserialize<'de> for ScalarValue {
                             "float32Value" | "float32_value" => 
Ok(GeneratedField::Float32Value),
                             "float64Value" | "float64_value" => 
Ok(GeneratedField::Float64Value),
                             "date32Value" | "date_32_value" => 
Ok(GeneratedField::Date32Value),
+                            "time32Value" | "time32_value" => 
Ok(GeneratedField::Time32Value),
                             "listValue" | "list_value" => 
Ok(GeneratedField::ListValue),
                             "decimal128Value" | "decimal128_value" => 
Ok(GeneratedField::Decimal128Value),
                             "date64Value" | "date_64_value" => 
Ok(GeneratedField::Date64Value),
@@ -10706,6 +10931,13 @@ impl<'de> serde::Deserialize<'de> for ScalarValue {
                             }
                             value__ = 
map.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x|
 scalar_value::Value::Date32Value(x.0));
                         }
+                        GeneratedField::Time32Value => {
+                            if value__.is_some() {
+                                return 
Err(serde::de::Error::duplicate_field("time32Value"));
+                            }
+                            value__ = 
map.next_value::<::std::option::Option<_>>()?.map(scalar_value::Value::Time32Value)
+;
+                        }
                         GeneratedField::ListValue => {
                             if value__.is_some() {
                                 return 
Err(serde::de::Error::duplicate_field("listValue"));
@@ -10768,7 +11000,8 @@ impl<'de> serde::Deserialize<'de> for ScalarValue {
                             if value__.is_some() {
                                 return 
Err(serde::de::Error::duplicate_field("time64Value"));
                             }
-                            value__ = 
map.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x|
 scalar_value::Value::Time64Value(x.0));
+                            value__ = 
map.next_value::<::std::option::Option<_>>()?.map(scalar_value::Value::Time64Value)
+;
                         }
                         GeneratedField::IntervalMonthDayNano => {
                             if value__.is_some() {
diff --git a/datafusion/proto/src/generated/prost.rs 
b/datafusion/proto/src/generated/prost.rs
index f34cc5c2a..25297787a 100644
--- a/datafusion/proto/src/generated/prost.rs
+++ b/datafusion/proto/src/generated/prost.rs
@@ -851,6 +851,36 @@ pub struct ScalarListValue {
     pub values: ::prost::alloc::vec::Vec<ScalarValue>,
 }
 #[derive(Clone, PartialEq, ::prost::Message)]
+pub struct ScalarTime32Value {
+    #[prost(oneof = "scalar_time32_value::Value", tags = "1, 2")]
+    pub value: ::core::option::Option<scalar_time32_value::Value>,
+}
+/// Nested message and enum types in `ScalarTime32Value`.
+pub mod scalar_time32_value {
+    #[derive(Clone, PartialEq, ::prost::Oneof)]
+    pub enum Value {
+        #[prost(int32, tag = "1")]
+        Time32SecondValue(i32),
+        #[prost(int32, tag = "2")]
+        Time32MillisecondValue(i32),
+    }
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct ScalarTime64Value {
+    #[prost(oneof = "scalar_time64_value::Value", tags = "1, 2")]
+    pub value: ::core::option::Option<scalar_time64_value::Value>,
+}
+/// Nested message and enum types in `ScalarTime64Value`.
+pub mod scalar_time64_value {
+    #[derive(Clone, PartialEq, ::prost::Oneof)]
+    pub enum Value {
+        #[prost(int64, tag = "1")]
+        Time64MicrosecondValue(i64),
+        #[prost(int64, tag = "2")]
+        Time64NanosecondValue(i64),
+    }
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
 pub struct ScalarTimestampValue {
     #[prost(string, tag = "5")]
     pub timezone: ::prost::alloc::string::String,
@@ -908,7 +938,7 @@ pub struct ScalarFixedSizeBinary {
 pub struct ScalarValue {
     #[prost(
         oneof = "scalar_value::Value",
-        tags = "33, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 20, 21, 
24, 25, 26, 27, 28, 29, 30, 31, 32, 34"
+        tags = "33, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 20, 
21, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34"
     )]
     pub value: ::core::option::Option<scalar_value::Value>,
 }
@@ -949,6 +979,8 @@ pub mod scalar_value {
         /// Literal Date32 value always has a unit of day
         #[prost(int32, tag = "14")]
         Date32Value(i32),
+        #[prost(message, tag = "15")]
+        Time32Value(super::ScalarTime32Value),
         /// WAS: ScalarType null_list_value = 18;
         #[prost(message, tag = "17")]
         ListValue(super::ScalarListValue),
@@ -968,8 +1000,8 @@ pub mod scalar_value {
         BinaryValue(::prost::alloc::vec::Vec<u8>),
         #[prost(bytes, tag = "29")]
         LargeBinaryValue(::prost::alloc::vec::Vec<u8>),
-        #[prost(int64, tag = "30")]
-        Time64Value(i64),
+        #[prost(message, tag = "30")]
+        Time64Value(super::ScalarTime64Value),
         #[prost(message, tag = "31")]
         IntervalMonthDayNano(super::IntervalMonthDayNanoValue),
         #[prost(message, tag = "32")]
diff --git a/datafusion/proto/src/lib.rs b/datafusion/proto/src/lib.rs
index 2166b1d8b..315fbd0dd 100644
--- a/datafusion/proto/src/lib.rs
+++ b/datafusion/proto/src/lib.rs
@@ -606,9 +606,21 @@ mod roundtrip_tests {
             ScalarValue::Date32(Some(0)),
             ScalarValue::Date32(Some(i32::MAX)),
             ScalarValue::Date32(None),
-            ScalarValue::Time64(Some(0)),
-            ScalarValue::Time64(Some(i64::MAX)),
-            ScalarValue::Time64(None),
+            ScalarValue::Date64(Some(0)),
+            ScalarValue::Date64(Some(i64::MAX)),
+            ScalarValue::Date64(None),
+            ScalarValue::Time32Second(Some(0)),
+            ScalarValue::Time32Second(Some(i32::MAX)),
+            ScalarValue::Time32Second(None),
+            ScalarValue::Time32Millisecond(Some(0)),
+            ScalarValue::Time32Millisecond(Some(i32::MAX)),
+            ScalarValue::Time32Millisecond(None),
+            ScalarValue::Time64Microsecond(Some(0)),
+            ScalarValue::Time64Microsecond(Some(i64::MAX)),
+            ScalarValue::Time64Microsecond(None),
+            ScalarValue::Time64Nanosecond(Some(0)),
+            ScalarValue::Time64Nanosecond(Some(i64::MAX)),
+            ScalarValue::Time64Nanosecond(None),
             ScalarValue::TimestampNanosecond(Some(0), None),
             ScalarValue::TimestampNanosecond(Some(i64::MAX), None),
             ScalarValue::TimestampNanosecond(Some(0), Some("UTC".to_string())),
diff --git a/datafusion/proto/src/to_proto.rs b/datafusion/proto/src/to_proto.rs
index 95224bf8e..6e152cc0c 100644
--- a/datafusion/proto/src/to_proto.rs
+++ b/datafusion/proto/src/to_proto.rs
@@ -1075,8 +1075,50 @@ impl TryFrom<&ScalarValue> for protobuf::ScalarValue {
                 })
             }
 
-            datafusion::scalar::ScalarValue::Time64(v) => {
-                create_proto_scalar(v, &data_type, |v| Value::Time64Value(*v))
+            datafusion::scalar::ScalarValue::Time32Second(v) => {
+                create_proto_scalar(v, &data_type, |v| {
+                    Value::Time32Value(protobuf::ScalarTime32Value {
+                        value: Some(
+                            
protobuf::scalar_time32_value::Value::Time32SecondValue(*v),
+                        ),
+                    })
+                })
+            }
+
+            datafusion::scalar::ScalarValue::Time32Millisecond(v) => {
+                create_proto_scalar(v, &data_type, |v| {
+                    Value::Time32Value(protobuf::ScalarTime32Value {
+                        value: Some(
+                            
protobuf::scalar_time32_value::Value::Time32MillisecondValue(
+                                *v,
+                            ),
+                        ),
+                    })
+                })
+            }
+
+            datafusion::scalar::ScalarValue::Time64Microsecond(v) => {
+                create_proto_scalar(v, &data_type, |v| {
+                    Value::Time64Value(protobuf::ScalarTime64Value {
+                        value: Some(
+                            
protobuf::scalar_time64_value::Value::Time64MicrosecondValue(
+                                *v,
+                            ),
+                        ),
+                    })
+                })
+            }
+
+            datafusion::scalar::ScalarValue::Time64Nanosecond(v) => {
+                create_proto_scalar(v, &data_type, |v| {
+                    Value::Time64Value(protobuf::ScalarTime64Value {
+                        value: Some(
+                            
protobuf::scalar_time64_value::Value::Time64NanosecondValue(
+                                *v,
+                            ),
+                        ),
+                    })
+                })
             }
 
             datafusion::scalar::ScalarValue::IntervalMonthDayNano(v) => {

Reply via email to