You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by sb...@apache.org on 2019/06/14 15:39:26 UTC

[arrow] branch master updated: ARROW-3671: [Go] implement MonthInterval and DayTimeInterval

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 634c8d2  ARROW-3671: [Go] implement MonthInterval and DayTimeInterval
634c8d2 is described below

commit 634c8d26eb60d261fe5096a9835bbc386860dcab
Author: Sebastien Binet <bi...@cern.ch>
AuthorDate: Fri Jun 14 17:39:06 2019 +0200

    ARROW-3671: [Go] implement MonthInterval and DayTimeInterval
    
    Author: Sebastien Binet <bi...@cern.ch>
    
    Closes #4562 from sbinet/issue-3671 and squashes the following commits:
    
    9a7d04da6 <Sebastien Binet> ARROW-3671:  implement MonthInterval and DayTimeInterval
---
 go/arrow/array/array.go          |   2 +-
 go/arrow/array/compare.go        |  12 ++
 go/arrow/array/interval.go       | 434 +++++++++++++++++++++++++++++++++++++++
 go/arrow/array/interval_test.go  | 276 +++++++++++++++++++++++++
 go/arrow/datatype_fixedwidth.go  |  71 +++++--
 go/arrow/type_traits_interval.go | 125 +++++++++++
 6 files changed, 901 insertions(+), 19 deletions(-)

diff --git a/go/arrow/array/array.go b/go/arrow/array/array.go
index 2f8be78..c13dd07 100644
--- a/go/arrow/array/array.go
+++ b/go/arrow/array/array.go
@@ -185,7 +185,7 @@ func init() {
 		arrow.TIMESTAMP:         func(data *Data) Interface { return NewTimestampData(data) },
 		arrow.TIME32:            func(data *Data) Interface { return NewTime32Data(data) },
 		arrow.TIME64:            func(data *Data) Interface { return NewTime64Data(data) },
-		arrow.INTERVAL:          unsupportedArrayType,
+		arrow.INTERVAL:          func(data *Data) Interface { return NewIntervalData(data) },
 		arrow.DECIMAL:           unsupportedArrayType,
 		arrow.LIST:              func(data *Data) Interface { return NewListData(data) },
 		arrow.STRUCT:            func(data *Data) Interface { return NewStructData(data) },
diff --git a/go/arrow/array/compare.go b/go/arrow/array/compare.go
index da8f5ab..0ea0b61 100644
--- a/go/arrow/array/compare.go
+++ b/go/arrow/array/compare.go
@@ -152,6 +152,12 @@ func ArrayEqual(left, right Interface) bool {
 	case *Struct:
 		r := right.(*Struct)
 		return arrayEqualStruct(l, r)
+	case *MonthInterval:
+		r := right.(*MonthInterval)
+		return arrayEqualMonthInterval(l, r)
+	case *DayTimeInterval:
+		r := right.(*DayTimeInterval)
+		return arrayEqualDayTimeInterval(l, r)
 
 	default:
 		panic(errors.Errorf("arrow/array: unknown array type %T", l))
@@ -329,6 +335,12 @@ func arrayApproxEqual(left, right Interface, opt equalOption) bool {
 	case *Struct:
 		r := right.(*Struct)
 		return arrayApproxEqualStruct(l, r, opt)
+	case *MonthInterval:
+		r := right.(*MonthInterval)
+		return arrayEqualMonthInterval(l, r)
+	case *DayTimeInterval:
+		r := right.(*DayTimeInterval)
+		return arrayEqualDayTimeInterval(l, r)
 
 	default:
 		panic(errors.Errorf("arrow/array: unknown array type %T", l))
diff --git a/go/arrow/array/interval.go b/go/arrow/array/interval.go
new file mode 100644
index 0000000..21efd6e
--- /dev/null
+++ b/go/arrow/array/interval.go
@@ -0,0 +1,434 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package array // import "github.com/apache/arrow/go/arrow/array"
+
+import (
+	"fmt"
+	"strings"
+	"sync/atomic"
+
+	"github.com/apache/arrow/go/arrow"
+	"github.com/apache/arrow/go/arrow/internal/bitutil"
+	"github.com/apache/arrow/go/arrow/internal/debug"
+	"github.com/apache/arrow/go/arrow/memory"
+	"github.com/pkg/errors"
+)
+
+func NewIntervalData(data *Data) Interface {
+	switch data.dtype.(type) {
+	case *arrow.MonthIntervalType:
+		return NewMonthIntervalData(data)
+	case *arrow.DayTimeIntervalType:
+		return NewDayTimeIntervalData(data)
+	default:
+		panic(errors.Errorf("arrow/array: unknown interval data type %T", data.dtype))
+	}
+}
+
+// A type which represents an immutable sequence of arrow.MonthInterval values.
+type MonthInterval struct {
+	array
+	values []arrow.MonthInterval
+}
+
+func NewMonthIntervalData(data *Data) *MonthInterval {
+	a := &MonthInterval{}
+	a.refCount = 1
+	a.setData(data)
+	return a
+}
+
+func (a *MonthInterval) Value(i int) arrow.MonthInterval            { return a.values[i] }
+func (a *MonthInterval) MonthIntervalValues() []arrow.MonthInterval { return a.values }
+
+func (a *MonthInterval) String() string {
+	o := new(strings.Builder)
+	o.WriteString("[")
+	for i, v := range a.values {
+		if i > 0 {
+			fmt.Fprintf(o, " ")
+		}
+		switch {
+		case a.IsNull(i):
+			o.WriteString("(null)")
+		default:
+			fmt.Fprintf(o, "%v", v)
+		}
+	}
+	o.WriteString("]")
+	return o.String()
+}
+
+func (a *MonthInterval) setData(data *Data) {
+	a.array.setData(data)
+	vals := data.buffers[1]
+	if vals != nil {
+		a.values = arrow.MonthIntervalTraits.CastFromBytes(vals.Bytes())
+		beg := a.array.data.offset
+		end := beg + a.array.data.length
+		a.values = a.values[beg:end]
+	}
+}
+
+func arrayEqualMonthInterval(left, right *MonthInterval) bool {
+	for i := 0; i < left.Len(); i++ {
+		if left.IsNull(i) {
+			continue
+		}
+		if left.Value(i) != right.Value(i) {
+			return false
+		}
+	}
+	return true
+}
+
+type MonthIntervalBuilder struct {
+	builder
+
+	data    *memory.Buffer
+	rawData []arrow.MonthInterval
+}
+
+func NewMonthIntervalBuilder(mem memory.Allocator) *MonthIntervalBuilder {
+	return &MonthIntervalBuilder{builder: builder{refCount: 1, mem: mem}}
+}
+
+// Release decreases the reference count by 1.
+// When the reference count goes to zero, the memory is freed.
+func (b *MonthIntervalBuilder) Release() {
+	debug.Assert(atomic.LoadInt64(&b.refCount) > 0, "too many releases")
+
+	if atomic.AddInt64(&b.refCount, -1) == 0 {
+		if b.nullBitmap != nil {
+			b.nullBitmap.Release()
+			b.nullBitmap = nil
+		}
+		if b.data != nil {
+			b.data.Release()
+			b.data = nil
+			b.rawData = nil
+		}
+	}
+}
+
+func (b *MonthIntervalBuilder) Append(v arrow.MonthInterval) {
+	b.Reserve(1)
+	b.UnsafeAppend(v)
+}
+
+func (b *MonthIntervalBuilder) AppendNull() {
+	b.Reserve(1)
+	b.UnsafeAppendBoolToBitmap(false)
+}
+
+func (b *MonthIntervalBuilder) UnsafeAppend(v arrow.MonthInterval) {
+	bitutil.SetBit(b.nullBitmap.Bytes(), b.length)
+	b.rawData[b.length] = v
+	b.length++
+}
+
+func (b *MonthIntervalBuilder) UnsafeAppendBoolToBitmap(isValid bool) {
+	if isValid {
+		bitutil.SetBit(b.nullBitmap.Bytes(), b.length)
+	} else {
+		b.nulls++
+	}
+	b.length++
+}
+
+// AppendValues will append the values in the v slice. The valid slice determines which values
+// in v are valid (not null). The valid slice must either be empty or be equal in length to v. If empty,
+// all values in v are appended and considered valid.
+func (b *MonthIntervalBuilder) AppendValues(v []arrow.MonthInterval, valid []bool) {
+	if len(v) != len(valid) && len(valid) != 0 {
+		panic("len(v) != len(valid) && len(valid) != 0")
+	}
+
+	if len(v) == 0 {
+		return
+	}
+
+	b.Reserve(len(v))
+	arrow.MonthIntervalTraits.Copy(b.rawData[b.length:], v)
+	b.builder.unsafeAppendBoolsToBitmap(valid, len(v))
+}
+
+func (b *MonthIntervalBuilder) init(capacity int) {
+	b.builder.init(capacity)
+
+	b.data = memory.NewResizableBuffer(b.mem)
+	bytesN := arrow.MonthIntervalTraits.BytesRequired(capacity)
+	b.data.Resize(bytesN)
+	b.rawData = arrow.MonthIntervalTraits.CastFromBytes(b.data.Bytes())
+}
+
+// Reserve ensures there is enough space for appending n elements
+// by checking the capacity and calling Resize if necessary.
+func (b *MonthIntervalBuilder) Reserve(n int) {
+	b.builder.reserve(n, b.Resize)
+}
+
+// Resize adjusts the space allocated by b to n elements. If n is greater than b.Cap(),
+// additional memory will be allocated. If n is smaller, the allocated memory may reduced.
+func (b *MonthIntervalBuilder) Resize(n int) {
+	nBuilder := n
+	if n < minBuilderCapacity {
+		n = minBuilderCapacity
+	}
+
+	if b.capacity == 0 {
+		b.init(n)
+	} else {
+		b.builder.resize(nBuilder, b.init)
+		b.data.Resize(arrow.MonthIntervalTraits.BytesRequired(n))
+		b.rawData = arrow.MonthIntervalTraits.CastFromBytes(b.data.Bytes())
+	}
+}
+
+// NewArray creates a MonthInterval array from the memory buffers used by the builder and resets the MonthIntervalBuilder
+// so it can be used to build a new array.
+func (b *MonthIntervalBuilder) NewArray() Interface {
+	return b.NewMonthIntervalArray()
+}
+
+// NewMonthIntervalArray creates a MonthInterval array from the memory buffers used by the builder and resets the MonthIntervalBuilder
+// so it can be used to build a new array.
+func (b *MonthIntervalBuilder) NewMonthIntervalArray() (a *MonthInterval) {
+	data := b.newData()
+	a = NewMonthIntervalData(data)
+	data.Release()
+	return
+}
+
+func (b *MonthIntervalBuilder) newData() (data *Data) {
+	bytesRequired := arrow.MonthIntervalTraits.BytesRequired(b.length)
+	if bytesRequired > 0 && bytesRequired < b.data.Len() {
+		// trim buffers
+		b.data.Resize(bytesRequired)
+	}
+	data = NewData(arrow.FixedWidthTypes.MonthInterval, b.length, []*memory.Buffer{b.nullBitmap, b.data}, nil, b.nulls, 0)
+	b.reset()
+
+	if b.data != nil {
+		b.data.Release()
+		b.data = nil
+		b.rawData = nil
+	}
+
+	return
+}
+
+// A type which represents an immutable sequence of arrow.DayTimeInterval values.
+type DayTimeInterval struct {
+	array
+	values []arrow.DayTimeInterval
+}
+
+func NewDayTimeIntervalData(data *Data) *DayTimeInterval {
+	a := &DayTimeInterval{}
+	a.refCount = 1
+	a.setData(data)
+	return a
+}
+
+func (a *DayTimeInterval) Value(i int) arrow.DayTimeInterval              { return a.values[i] }
+func (a *DayTimeInterval) DayTimeIntervalValues() []arrow.DayTimeInterval { return a.values }
+
+func (a *DayTimeInterval) String() string {
+	o := new(strings.Builder)
+	o.WriteString("[")
+	for i, v := range a.values {
+		if i > 0 {
+			fmt.Fprintf(o, " ")
+		}
+		switch {
+		case a.IsNull(i):
+			o.WriteString("(null)")
+		default:
+			fmt.Fprintf(o, "%v", v)
+		}
+	}
+	o.WriteString("]")
+	return o.String()
+}
+
+func (a *DayTimeInterval) setData(data *Data) {
+	a.array.setData(data)
+	vals := data.buffers[1]
+	if vals != nil {
+		a.values = arrow.DayTimeIntervalTraits.CastFromBytes(vals.Bytes())
+		beg := a.array.data.offset
+		end := beg + a.array.data.length
+		a.values = a.values[beg:end]
+	}
+}
+
+func arrayEqualDayTimeInterval(left, right *DayTimeInterval) bool {
+	for i := 0; i < left.Len(); i++ {
+		if left.IsNull(i) {
+			continue
+		}
+		if left.Value(i) != right.Value(i) {
+			return false
+		}
+	}
+	return true
+}
+
+type DayTimeIntervalBuilder struct {
+	builder
+
+	data    *memory.Buffer
+	rawData []arrow.DayTimeInterval
+}
+
+func NewDayTimeIntervalBuilder(mem memory.Allocator) *DayTimeIntervalBuilder {
+	return &DayTimeIntervalBuilder{builder: builder{refCount: 1, mem: mem}}
+}
+
+// Release decreases the reference count by 1.
+// When the reference count goes to zero, the memory is freed.
+func (b *DayTimeIntervalBuilder) Release() {
+	debug.Assert(atomic.LoadInt64(&b.refCount) > 0, "too many releases")
+
+	if atomic.AddInt64(&b.refCount, -1) == 0 {
+		if b.nullBitmap != nil {
+			b.nullBitmap.Release()
+			b.nullBitmap = nil
+		}
+		if b.data != nil {
+			b.data.Release()
+			b.data = nil
+			b.rawData = nil
+		}
+	}
+}
+
+func (b *DayTimeIntervalBuilder) Append(v arrow.DayTimeInterval) {
+	b.Reserve(1)
+	b.UnsafeAppend(v)
+}
+
+func (b *DayTimeIntervalBuilder) AppendNull() {
+	b.Reserve(1)
+	b.UnsafeAppendBoolToBitmap(false)
+}
+
+func (b *DayTimeIntervalBuilder) UnsafeAppend(v arrow.DayTimeInterval) {
+	bitutil.SetBit(b.nullBitmap.Bytes(), b.length)
+	b.rawData[b.length] = v
+	b.length++
+}
+
+func (b *DayTimeIntervalBuilder) UnsafeAppendBoolToBitmap(isValid bool) {
+	if isValid {
+		bitutil.SetBit(b.nullBitmap.Bytes(), b.length)
+	} else {
+		b.nulls++
+	}
+	b.length++
+}
+
+// AppendValues will append the values in the v slice. The valid slice determines which values
+// in v are valid (not null). The valid slice must either be empty or be equal in length to v. If empty,
+// all values in v are appended and considered valid.
+func (b *DayTimeIntervalBuilder) AppendValues(v []arrow.DayTimeInterval, valid []bool) {
+	if len(v) != len(valid) && len(valid) != 0 {
+		panic("len(v) != len(valid) && len(valid) != 0")
+	}
+
+	if len(v) == 0 {
+		return
+	}
+
+	b.Reserve(len(v))
+	arrow.DayTimeIntervalTraits.Copy(b.rawData[b.length:], v)
+	b.builder.unsafeAppendBoolsToBitmap(valid, len(v))
+}
+
+func (b *DayTimeIntervalBuilder) init(capacity int) {
+	b.builder.init(capacity)
+
+	b.data = memory.NewResizableBuffer(b.mem)
+	bytesN := arrow.DayTimeIntervalTraits.BytesRequired(capacity)
+	b.data.Resize(bytesN)
+	b.rawData = arrow.DayTimeIntervalTraits.CastFromBytes(b.data.Bytes())
+}
+
+// Reserve ensures there is enough space for appending n elements
+// by checking the capacity and calling Resize if necessary.
+func (b *DayTimeIntervalBuilder) Reserve(n int) {
+	b.builder.reserve(n, b.Resize)
+}
+
+// Resize adjusts the space allocated by b to n elements. If n is greater than b.Cap(),
+// additional memory will be allocated. If n is smaller, the allocated memory may reduced.
+func (b *DayTimeIntervalBuilder) Resize(n int) {
+	nBuilder := n
+	if n < minBuilderCapacity {
+		n = minBuilderCapacity
+	}
+
+	if b.capacity == 0 {
+		b.init(n)
+	} else {
+		b.builder.resize(nBuilder, b.init)
+		b.data.Resize(arrow.DayTimeIntervalTraits.BytesRequired(n))
+		b.rawData = arrow.DayTimeIntervalTraits.CastFromBytes(b.data.Bytes())
+	}
+}
+
+// NewArray creates a DayTimeInterval array from the memory buffers used by the builder and resets the DayTimeIntervalBuilder
+// so it can be used to build a new array.
+func (b *DayTimeIntervalBuilder) NewArray() Interface {
+	return b.NewDayTimeIntervalArray()
+}
+
+// NewDayTimeIntervalArray creates a DayTimeInterval array from the memory buffers used by the builder and resets the DayTimeIntervalBuilder
+// so it can be used to build a new array.
+func (b *DayTimeIntervalBuilder) NewDayTimeIntervalArray() (a *DayTimeInterval) {
+	data := b.newData()
+	a = NewDayTimeIntervalData(data)
+	data.Release()
+	return
+}
+
+func (b *DayTimeIntervalBuilder) newData() (data *Data) {
+	bytesRequired := arrow.DayTimeIntervalTraits.BytesRequired(b.length)
+	if bytesRequired > 0 && bytesRequired < b.data.Len() {
+		// trim buffers
+		b.data.Resize(bytesRequired)
+	}
+	data = NewData(arrow.FixedWidthTypes.DayTimeInterval, b.length, []*memory.Buffer{b.nullBitmap, b.data}, nil, b.nulls, 0)
+	b.reset()
+
+	if b.data != nil {
+		b.data.Release()
+		b.data = nil
+		b.rawData = nil
+	}
+
+	return
+}
+
+var (
+	_ Interface = (*MonthInterval)(nil)
+	_ Interface = (*DayTimeInterval)(nil)
+
+	_ Builder = (*MonthIntervalBuilder)(nil)
+	_ Builder = (*DayTimeIntervalBuilder)(nil)
+)
diff --git a/go/arrow/array/interval_test.go b/go/arrow/array/interval_test.go
new file mode 100644
index 0000000..bac0bb4
--- /dev/null
+++ b/go/arrow/array/interval_test.go
@@ -0,0 +1,276 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package array_test
+
+import (
+	"testing"
+
+	"github.com/apache/arrow/go/arrow"
+	"github.com/apache/arrow/go/arrow/array"
+	"github.com/apache/arrow/go/arrow/memory"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMonthIntervalArray(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+	defer mem.AssertSize(t, 0)
+
+	var (
+		want   = []arrow.MonthInterval{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	b := array.NewMonthIntervalBuilder(mem)
+	defer b.Release()
+
+	b.Retain()
+	b.Release()
+
+	b.AppendValues(want[:2], nil)
+	b.AppendNull()
+	b.Append(want[3])
+
+	if got, want := b.Len(), len(want); got != want {
+		t.Fatalf("invalid len: got=%d, want=%d", got, want)
+	}
+
+	if got, want := b.NullN(), 1; got != want {
+		t.Fatalf("invalid nulls: got=%d, want=%d", got, want)
+	}
+
+	arr := b.NewMonthIntervalArray()
+	defer arr.Release()
+
+	arr.Retain()
+	arr.Release()
+
+	if got, want := arr.Len(), len(want); got != want {
+		t.Fatalf("invalid len: got=%d, want=%d", got, want)
+	}
+
+	if got, want := arr.NullN(), 1; got != want {
+		t.Fatalf("invalid nulls: got=%d, want=%d", got, want)
+	}
+
+	for i := range want {
+		if arr.IsNull(i) != !valids[i] {
+			t.Fatalf("arr[%d]-validity: got=%v want=%v", i, !arr.IsNull(i), valids[i])
+		}
+		switch {
+		case arr.IsNull(i):
+		default:
+			got := arr.Value(i)
+			if got != want[i] {
+				t.Fatalf("arr[%d]: got=%q, want=%q", i, got, want[i])
+			}
+		}
+	}
+
+	sub := array.MakeFromData(arr.Data())
+	defer sub.Release()
+
+	if sub.DataType().ID() != arrow.INTERVAL {
+		t.Fatalf("invalid type: got=%q, want=interval", sub.DataType().Name())
+	}
+
+	if _, ok := sub.(*array.MonthInterval); !ok {
+		t.Fatalf("could not type-assert to array.MonthInterval")
+	}
+
+	if got, want := arr.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+	slice := array.NewSliceData(arr.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.MonthInterval)
+	if !ok {
+		t.Fatalf("could not type-assert to array.MonthInterval")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+}
+
+func TestMonthIntervalBuilder_Empty(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+	defer mem.AssertSize(t, 0)
+
+	want := []arrow.MonthInterval{1, 2, 3, 4}
+
+	b := array.NewMonthIntervalBuilder(mem)
+	defer b.Release()
+
+	miValues := func(a *array.MonthInterval) []arrow.MonthInterval {
+		vs := make([]arrow.MonthInterval, a.Len())
+		for i := range vs {
+			vs[i] = a.Value(i)
+		}
+		return vs
+	}
+
+	b.AppendValues([]arrow.MonthInterval{}, nil)
+	arr := b.NewMonthIntervalArray()
+	assert.Zero(t, arr.Len())
+	arr.Release()
+
+	b.AppendValues(nil, nil)
+	arr = b.NewMonthIntervalArray()
+	assert.Zero(t, arr.Len())
+	arr.Release()
+
+	b.AppendValues([]arrow.MonthInterval{}, nil)
+	b.AppendValues(want, nil)
+	arr = b.NewMonthIntervalArray()
+	assert.Equal(t, want, miValues(arr))
+	arr.Release()
+
+	b.AppendValues(want, nil)
+	b.AppendValues([]arrow.MonthInterval{}, nil)
+	arr = b.NewMonthIntervalArray()
+	assert.Equal(t, want, miValues(arr))
+	arr.Release()
+}
+
+func TestDayTimeArray(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+	defer mem.AssertSize(t, 0)
+
+	var (
+		want   = []arrow.DayTimeInterval{{1, 1}, {2, 2}, {3, 3}, {4, 4}}
+		valids = []bool{true, true, false, true}
+	)
+
+	b := array.NewDayTimeIntervalBuilder(mem)
+	defer b.Release()
+
+	b.Retain()
+	b.Release()
+
+	b.AppendValues(want[:2], nil)
+	b.AppendNull()
+	b.Append(want[3])
+
+	if got, want := b.Len(), len(want); got != want {
+		t.Fatalf("invalid len: got=%d, want=%d", got, want)
+	}
+
+	if got, want := b.NullN(), 1; got != want {
+		t.Fatalf("invalid nulls: got=%d, want=%d", got, want)
+	}
+
+	arr := b.NewDayTimeIntervalArray()
+	defer arr.Release()
+
+	arr.Retain()
+	arr.Release()
+
+	if got, want := arr.Len(), len(want); got != want {
+		t.Fatalf("invalid len: got=%d, want=%d", got, want)
+	}
+
+	if got, want := arr.NullN(), 1; got != want {
+		t.Fatalf("invalid nulls: got=%d, want=%d", got, want)
+	}
+
+	for i := range want {
+		if arr.IsNull(i) != !valids[i] {
+			t.Fatalf("arr[%d]-validity: got=%v want=%v", i, !arr.IsNull(i), valids[i])
+		}
+		switch {
+		case arr.IsNull(i):
+		default:
+			got := arr.Value(i)
+			if got != want[i] {
+				t.Fatalf("arr[%d]: got=%q, want=%q", i, got, want[i])
+			}
+		}
+	}
+
+	sub := array.MakeFromData(arr.Data())
+	defer sub.Release()
+
+	if sub.DataType().ID() != arrow.INTERVAL {
+		t.Fatalf("invalid type: got=%q, want=interval", sub.DataType().Name())
+	}
+
+	if _, ok := sub.(*array.DayTimeInterval); !ok {
+		t.Fatalf("could not type-assert to array.DayTimeInterval")
+	}
+
+	if got, want := arr.String(), `[{1 1} {2 2} (null) {4 4}]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+	slice := array.NewSliceData(arr.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.DayTimeInterval)
+	if !ok {
+		t.Fatalf("could not type-assert to array.DayInterval")
+	}
+
+	if got, want := v.String(), `[(null) {4 4}]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+}
+
+func TestDayTimeIntervalBuilder_Empty(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+	defer mem.AssertSize(t, 0)
+
+	want := []arrow.DayTimeInterval{{1, 1}, {2, 2}, {3, 3}, {4, 4}}
+
+	b := array.NewDayTimeIntervalBuilder(mem)
+	defer b.Release()
+
+	dtValues := func(a *array.DayTimeInterval) []arrow.DayTimeInterval {
+		vs := make([]arrow.DayTimeInterval, a.Len())
+		for i := range vs {
+			vs[i] = a.Value(i)
+		}
+		return vs
+	}
+
+	b.AppendValues([]arrow.DayTimeInterval{}, nil)
+	arr := b.NewDayTimeIntervalArray()
+	assert.Zero(t, arr.Len())
+	arr.Release()
+
+	b.AppendValues(nil, nil)
+	arr = b.NewDayTimeIntervalArray()
+	assert.Zero(t, arr.Len())
+	arr.Release()
+
+	b.AppendValues([]arrow.DayTimeInterval{}, nil)
+	b.AppendValues(want, nil)
+	arr = b.NewDayTimeIntervalArray()
+	assert.Equal(t, want, dtValues(arr))
+	arr.Release()
+
+	b.AppendValues(want, nil)
+	b.AppendValues([]arrow.DayTimeInterval{}, nil)
+	arr = b.NewDayTimeIntervalArray()
+	assert.Equal(t, want, dtValues(arr))
+	arr.Release()
+}
diff --git a/go/arrow/datatype_fixedwidth.go b/go/arrow/datatype_fixedwidth.go
index fa7ed3f..14766fa 100644
--- a/go/arrow/datatype_fixedwidth.go
+++ b/go/arrow/datatype_fixedwidth.go
@@ -94,25 +94,29 @@ func (t *Time64Type) String() string { return "time64[" + t.Unit.String() + "]"
 
 var (
 	FixedWidthTypes = struct {
-		Boolean   FixedWidthDataType
-		Date32    FixedWidthDataType
-		Date64    FixedWidthDataType
-		Float16   FixedWidthDataType
-		Time32s   FixedWidthDataType
-		Time32ms  FixedWidthDataType
-		Time64us  FixedWidthDataType
-		Time64ns  FixedWidthDataType
-		Timestamp FixedWidthDataType
+		Boolean         FixedWidthDataType
+		Date32          FixedWidthDataType
+		Date64          FixedWidthDataType
+		DayTimeInterval FixedWidthDataType
+		Float16         FixedWidthDataType
+		MonthInterval   FixedWidthDataType
+		Time32s         FixedWidthDataType
+		Time32ms        FixedWidthDataType
+		Time64us        FixedWidthDataType
+		Time64ns        FixedWidthDataType
+		Timestamp       FixedWidthDataType
 	}{
-		Boolean:   &BooleanType{},
-		Date32:    &Date32Type{},
-		Date64:    &Date64Type{},
-		Float16:   &Float16Type{},
-		Time32s:   &Time32Type{Unit: Second},
-		Time32ms:  &Time32Type{Unit: Millisecond},
-		Time64us:  &Time64Type{Unit: Microsecond},
-		Time64ns:  &Time64Type{Unit: Nanosecond},
-		Timestamp: &TimestampType{Unit: Nanosecond, TimeZone: "UTC"},
+		Boolean:         &BooleanType{},
+		Date32:          &Date32Type{},
+		Date64:          &Date64Type{},
+		DayTimeInterval: &DayTimeIntervalType{},
+		Float16:         &Float16Type{},
+		MonthInterval:   &MonthIntervalType{},
+		Time32s:         &Time32Type{Unit: Second},
+		Time32ms:        &Time32Type{Unit: Millisecond},
+		Time64us:        &Time64Type{Unit: Microsecond},
+		Time64ns:        &Time64Type{Unit: Nanosecond},
+		Timestamp:       &TimestampType{Unit: Nanosecond, TimeZone: "UTC"},
 	}
 
 	_ FixedWidthDataType = (*FixedSizeBinaryType)(nil)
@@ -126,3 +130,34 @@ func (t *Float16Type) String() string { return "float16" }
 
 // BitWidth returns the number of bits required to store a single element of this data type in memory.
 func (t *Float16Type) BitWidth() int { return 16 }
+
+// MonthInterval represents a number of months.
+type MonthInterval int32
+
+// MonthIntervalType is encoded as a 32-bit signed integer,
+// representing a number of months.
+type MonthIntervalType struct{}
+
+func (*MonthIntervalType) ID() Type       { return INTERVAL }
+func (*MonthIntervalType) Name() string   { return "month_interval" }
+func (*MonthIntervalType) String() string { return "month_interval" }
+
+// BitWidth returns the number of bits required to store a single element of this data type in memory.
+func (t *MonthIntervalType) BitWidth() int { return 32 }
+
+// DayTimeInterval represents a number of days and milliseconds (fraction of day).
+type DayTimeInterval struct {
+	Days         int32
+	Milliseconds int32
+}
+
+// DayTimeIntervalType is encoded as a pair of 32-bit signed integer,
+// representing a number of days and milliseconds (fraction of day).
+type DayTimeIntervalType struct{}
+
+func (*DayTimeIntervalType) ID() Type       { return INTERVAL }
+func (*DayTimeIntervalType) Name() string   { return "day_time_interval" }
+func (*DayTimeIntervalType) String() string { return "day_time_interval" }
+
+// BitWidth returns the number of bits required to store a single element of this data type in memory.
+func (t *DayTimeIntervalType) BitWidth() int { return 64 }
diff --git a/go/arrow/type_traits_interval.go b/go/arrow/type_traits_interval.go
new file mode 100644
index 0000000..8ddaa51
--- /dev/null
+++ b/go/arrow/type_traits_interval.go
@@ -0,0 +1,125 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package arrow
+
+import (
+	"encoding/binary"
+	"reflect"
+	"unsafe"
+)
+
+var (
+	MonthIntervalTraits   monthTraits
+	DayTimeIntervalTraits daytimeTraits
+)
+
+// MonthInterval traits
+
+const (
+	// MonthIntervalSizeBytes specifies the number of bytes required to store a single MonthInterval in memory
+	MonthIntervalSizeBytes = int(unsafe.Sizeof(MonthInterval(0)))
+)
+
+type monthTraits struct{}
+
+// BytesRequired returns the number of bytes required to store n elements in memory.
+func (monthTraits) BytesRequired(n int) int { return MonthIntervalSizeBytes * n }
+
+// PutValue
+func (monthTraits) PutValue(b []byte, v MonthInterval) {
+	binary.LittleEndian.PutUint32(b, uint32(v))
+}
+
+// CastFromBytes reinterprets the slice b to a slice of type MonthInterval.
+//
+// NOTE: len(b) must be a multiple of MonthIntervalSizeBytes.
+func (monthTraits) CastFromBytes(b []byte) []MonthInterval {
+	h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+
+	var res []MonthInterval
+	s := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+	s.Data = h.Data
+	s.Len = h.Len / MonthIntervalSizeBytes
+	s.Cap = h.Cap / MonthIntervalSizeBytes
+
+	return res
+}
+
+// CastToBytes reinterprets the slice b to a slice of bytes.
+func (monthTraits) CastToBytes(b []MonthInterval) []byte {
+	h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+
+	var res []byte
+	s := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+	s.Data = h.Data
+	s.Len = h.Len * MonthIntervalSizeBytes
+	s.Cap = h.Cap * MonthIntervalSizeBytes
+
+	return res
+}
+
+// Copy copies src to dst.
+func (monthTraits) Copy(dst, src []MonthInterval) { copy(dst, src) }
+
+// DayTimeInterval traits
+
+const (
+	// DayTimeIntervalSizeBytes specifies the number of bytes required to store a single DayTimeInterval in memory
+	DayTimeIntervalSizeBytes = int(unsafe.Sizeof(DayTimeInterval{}))
+)
+
+type daytimeTraits struct{}
+
+// BytesRequired returns the number of bytes required to store n elements in memory.
+func (daytimeTraits) BytesRequired(n int) int { return DayTimeIntervalSizeBytes * n }
+
+// PutValue
+func (daytimeTraits) PutValue(b []byte, v DayTimeInterval) {
+	binary.LittleEndian.PutUint32(b, uint32(v.Days))
+	binary.LittleEndian.PutUint32(b, uint32(v.Milliseconds))
+}
+
+// CastFromBytes reinterprets the slice b to a slice of type DayTimeInterval.
+//
+// NOTE: len(b) must be a multiple of DayTimeIntervalSizeBytes.
+func (daytimeTraits) CastFromBytes(b []byte) []DayTimeInterval {
+	h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+
+	var res []DayTimeInterval
+	s := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+	s.Data = h.Data
+	s.Len = h.Len / DayTimeIntervalSizeBytes
+	s.Cap = h.Cap / DayTimeIntervalSizeBytes
+
+	return res
+}
+
+// CastToBytes reinterprets the slice b to a slice of bytes.
+func (daytimeTraits) CastToBytes(b []DayTimeInterval) []byte {
+	h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+
+	var res []byte
+	s := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+	s.Data = h.Data
+	s.Len = h.Len * DayTimeIntervalSizeBytes
+	s.Cap = h.Cap * DayTimeIntervalSizeBytes
+
+	return res
+}
+
+// Copy copies src to dst.
+func (daytimeTraits) Copy(dst, src []DayTimeInterval) { copy(dst, src) }