From e2bd7d5b4a8cd4015145d5538f0bf270341fe865 Mon Sep 17 00:00:00 2001 From: philhassey Date: Thu, 5 Dec 2024 13:08:38 -0700 Subject: [PATCH 1/3] types: add go native conversion methods to several types Addresses IDX-416 Signed-off-by: philhassey --- internal/error.go | 1 + types/datetime.go | 5 +++++ types/datetime_test.go | 8 ++++++++ types/decimal.go | 6 ++++++ types/decimal_test.go | 9 +++++++++ types/duration.go | 12 ++++++++++++ types/duration_test.go | 24 ++++++++++++++++++++++++ 7 files changed, 65 insertions(+) diff --git a/internal/error.go b/internal/error.go index 893bfa6..f85eccd 100644 --- a/internal/error.go +++ b/internal/error.go @@ -11,3 +11,4 @@ var ErrDecimal = fmt.Errorf("error parsing decimal value") var ErrDuration = fmt.Errorf("error parsing duration value") var ErrIP = fmt.Errorf("error parsing ip value") var ErrNotComparable = fmt.Errorf("incompatible types in comparison") +var ErrDurationRange = fmt.Errorf("duration out of range") diff --git a/types/datetime.go b/types/datetime.go index fed405b..dda905d 100644 --- a/types/datetime.go +++ b/types/datetime.go @@ -274,6 +274,11 @@ func (a Datetime) Milliseconds() int64 { return a.value } +// Time returns the time.Time representation of a Datetime. +func (a Datetime) Time() time.Time { + return time.UnixMilli(a.value) +} + func (v Datetime) hash() uint64 { return uint64(v.value) } diff --git a/types/datetime_test.go b/types/datetime_test.go index 5735c86..1d0e198 100644 --- a/types/datetime_test.go +++ b/types/datetime_test.go @@ -119,6 +119,14 @@ func TestDatetime(t *testing.T) { testutil.Equals(t, one.Milliseconds(), two.Milliseconds()) }) + t.Run("Time", func(t *testing.T) { + t.Parallel() + in := types.NewDatetime(time.UnixMilli(42)) + got := in.Time() + want := time.UnixMilli(42) + testutil.Equals(t, got, want) + }) + t.Run("Equal", func(t *testing.T) { t.Parallel() one := types.NewDatetimeFromMillis(1) diff --git a/types/decimal.go b/types/decimal.go index 600cfdf..7bb341d 100644 --- a/types/decimal.go +++ b/types/decimal.go @@ -186,6 +186,12 @@ func (d Decimal) MarshalJSON() ([]byte, error) { }) } +// Float returns a float64 representation of a Decimal. Warning: some precision +// may be lost during this conversion. +func (d Decimal) Float() float64 { + return float64(d.value)/decimalPrecision +} + func (d Decimal) hash() uint64 { return uint64(d.value) } diff --git a/types/decimal_test.go b/types/decimal_test.go index e59bfe7..3b0900b 100644 --- a/types/decimal_test.go +++ b/types/decimal_test.go @@ -281,6 +281,15 @@ func TestDecimal(t *testing.T) { } }) + t.Run("Float", func(t *testing.T) { + t.Parallel() + in, err := types.NewDecimalFromFloat(42.42) + testutil.OK(t, err) + got := in.Float() + want := 42.42 + testutil.Equals(t, got, want) + }) + t.Run("MarshalCedar", func(t *testing.T) { t.Parallel() testutil.Equals( diff --git a/types/duration.go b/types/duration.go index 985d478..f8ab13b 100644 --- a/types/duration.go +++ b/types/duration.go @@ -267,6 +267,18 @@ func (v Duration) ToMilliseconds() int64 { return v.value } +// Duration returns a time.Duration representation of a Duration. An error +// is returned if the duration cannot be converted to a time.Duration. +func (v Duration) Duration() (time.Duration, error) { + if v.value > math.MaxInt64/1000 { + return 0, internal.ErrDurationRange + } + if v.value < math.MinInt64/1000 { + return 0, internal.ErrDurationRange + } + return time.Millisecond * time.Duration(v.value), nil +} + func (v Duration) hash() uint64 { return uint64(v.value) } diff --git a/types/duration_test.go b/types/duration_test.go index ff17bd8..bbd525a 100644 --- a/types/duration_test.go +++ b/types/duration_test.go @@ -3,6 +3,7 @@ package types_test import ( "encoding/json" "fmt" + "math" "testing" "time" @@ -87,6 +88,29 @@ func TestDuration(t *testing.T) { testutil.Equals(t, one.ToMilliseconds(), two.ToMilliseconds()) }) + t.Run("Duration", func(t *testing.T) { + t.Parallel() + tests := []struct { + name string + in types.Duration + out time.Duration + err func(testutil.TB, error) + }{ + {"ok", types.NewDuration(time.Millisecond * 42), time.Millisecond * 42, testutil.OK}, + {"maxPlusOne", types.NewDurationFromMillis(math.MaxInt64/1000 + 1), 0, testutil.Error}, + {"minMinusOne", types.NewDurationFromMillis(math.MinInt64/1000 - 1), 0, testutil.Error}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + out, err := tt.in.Duration() + testutil.Equals(t, out, tt.out) + tt.err(t, err) + }) + } + }) + t.Run("Equal", func(t *testing.T) { t.Parallel() one := types.NewDurationFromMillis(1) From fb0f992be97d90d1bfd76b79e20532598301c4bf Mon Sep 17 00:00:00 2001 From: philhassey Date: Thu, 5 Dec 2024 13:12:00 -0700 Subject: [PATCH 2/3] types: appease linter by running gofmt Addresses IDX-416 Signed-off-by: philhassey --- types/decimal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/decimal.go b/types/decimal.go index 7bb341d..7b09c5e 100644 --- a/types/decimal.go +++ b/types/decimal.go @@ -189,7 +189,7 @@ func (d Decimal) MarshalJSON() ([]byte, error) { // Float returns a float64 representation of a Decimal. Warning: some precision // may be lost during this conversion. func (d Decimal) Float() float64 { - return float64(d.value)/decimalPrecision + return float64(d.value) / decimalPrecision } func (d Decimal) hash() uint64 { From 84ee8af828f87acf88f71c551b009e490f42df25 Mon Sep 17 00:00:00 2001 From: philhassey Date: Thu, 5 Dec 2024 14:12:46 -0700 Subject: [PATCH 3/3] types: return UTC time from Datetime.Time() Addresses IDX-479 Signed-off-by: philhassey --- types/datetime.go | 4 ++-- types/datetime_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/types/datetime.go b/types/datetime.go index dda905d..fd3afdd 100644 --- a/types/datetime.go +++ b/types/datetime.go @@ -274,9 +274,9 @@ func (a Datetime) Milliseconds() int64 { return a.value } -// Time returns the time.Time representation of a Datetime. +// Time returns the UTC time.Time representation of a Datetime. func (a Datetime) Time() time.Time { - return time.UnixMilli(a.value) + return time.UnixMilli(a.value).UTC() } func (v Datetime) hash() uint64 { diff --git a/types/datetime_test.go b/types/datetime_test.go index 1d0e198..dda16a0 100644 --- a/types/datetime_test.go +++ b/types/datetime_test.go @@ -123,7 +123,7 @@ func TestDatetime(t *testing.T) { t.Parallel() in := types.NewDatetime(time.UnixMilli(42)) got := in.Time() - want := time.UnixMilli(42) + want := time.UnixMilli(42).UTC() testutil.Equals(t, got, want) })