This is an automated email from the ASF dual-hosted git repository.
zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-go.git
The following commit(s) were added to refs/heads/main by this push:
new 4b94a0b2 fix(arrow/scalar): support same-width time casts (#1040)
4b94a0b2 is described below
commit 4b94a0b245cdb76b03ca812084c46b1988dcd6e7
Author: Minh Vu <[email protected]>
AuthorDate: Tue Jul 28 17:08:01 2026 +0200
fix(arrow/scalar): support same-width time casts (#1040)
## What changed
Extract temporal scalar values according to the source width before
converting them to a destination `Time32` or `Time64` unit.
## Why
The previous implementation chose the source value assertion from the
destination type. Same-width conversions therefore asserted `Time32`
values as `Time64`, or vice versa, and panicked.
The regression matrix covers Time32-to-Time32, Time32-to-Time64,
Time64-to-Time32, and Time64-to-Time64 conversions.
## Validation
`go test ./arrow/scalar`
---
arrow/scalar/scalar_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++
arrow/scalar/temporal.go | 12 ++++++++++--
2 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/arrow/scalar/scalar_test.go b/arrow/scalar/scalar_test.go
index 78ec0c9a..8aa7aebe 100644
--- a/arrow/scalar/scalar_test.go
+++ b/arrow/scalar/scalar_test.go
@@ -366,6 +366,48 @@ func TestTimeScalarsMakeScalar(t *testing.T) {
assert.Equal(t, "10:10:10.123456789",
scalar.NewTime64Scalar(arrow.Time64(tententen), typ4).String())
}
+func TestTimeScalarUnitConversions(t *testing.T) {
+ tests := []struct {
+ name string
+ from scalar.TimeScalar
+ to arrow.DataType
+ want scalar.Scalar
+ }{
+ {
+ name: "time32 to time32",
+ from: scalar.NewTime32Scalar(2,
arrow.FixedWidthTypes.Time32s),
+ to: arrow.FixedWidthTypes.Time32ms,
+ want: scalar.NewTime32Scalar(2000,
arrow.FixedWidthTypes.Time32ms),
+ },
+ {
+ name: "time32 to time64",
+ from: scalar.NewTime32Scalar(2,
arrow.FixedWidthTypes.Time32s),
+ to: arrow.FixedWidthTypes.Time64us,
+ want: scalar.NewTime64Scalar(2_000_000,
arrow.FixedWidthTypes.Time64us),
+ },
+ {
+ name: "time64 to time32",
+ from: scalar.NewTime64Scalar(2_000_000,
arrow.FixedWidthTypes.Time64us),
+ to: arrow.FixedWidthTypes.Time32ms,
+ want: scalar.NewTime32Scalar(2000,
arrow.FixedWidthTypes.Time32ms),
+ },
+ {
+ name: "time64 to time64",
+ from: scalar.NewTime64Scalar(2,
arrow.FixedWidthTypes.Time64us),
+ to: arrow.FixedWidthTypes.Time64ns,
+ want: scalar.NewTime64Scalar(2000,
arrow.FixedWidthTypes.Time64ns),
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := tt.from.CastTo(tt.to)
+ require.NoError(t, err)
+ assert.True(t, scalar.Equals(tt.want, got))
+ })
+ }
+}
+
func TestTimestampScalarBasics(t *testing.T) {
typ1 := arrow.FixedWidthTypes.Timestamp_ms
typ2 := arrow.FixedWidthTypes.Timestamp_s
diff --git a/arrow/scalar/temporal.go b/arrow/scalar/temporal.go
index 42a46738..26a6aeae 100644
--- a/arrow/scalar/temporal.go
+++ b/arrow/scalar/temporal.go
@@ -155,11 +155,19 @@ func castTemporal(from TemporalScalar, to arrow.DataType)
(Scalar, error) {
return NewDate64Scalar(arrow.Date64(millis -
millis%int64(millisecondsInDay))), nil
}
case TimeScalar:
+ var value int64
+ switch s := s.(type) {
+ case *Time32:
+ value = int64(s.Value)
+ case *Time64:
+ value = int64(s.Value)
+ }
+
switch to := to.(type) {
case *arrow.Time32Type:
- return
NewTime32Scalar(arrow.Time32(arrow.ConvertTimestampValue(s.Unit(), to.Unit,
int64(s.value().(arrow.Time64)))), to), nil
+ return
NewTime32Scalar(arrow.Time32(arrow.ConvertTimestampValue(s.Unit(), to.Unit,
value)), to), nil
case *arrow.Time64Type:
- return
NewTime64Scalar(arrow.Time64(arrow.ConvertTimestampValue(s.Unit(), to.Unit,
int64(s.value().(arrow.Time32)))), to), nil
+ return
NewTime64Scalar(arrow.Time64(arrow.ConvertTimestampValue(s.Unit(), to.Unit,
value)), to), nil
}
case *Duration: