parse unix timestamps
This commit is contained in:
parent
f3e17035cd
commit
4591a086be
56
scan.go
56
scan.go
@ -3,6 +3,7 @@ package bind
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -53,27 +54,71 @@ func parseUint(s string, byteSize int) (uint64, error) {
|
|||||||
return intParse(strconv.ParseUint, s, byteSize)
|
return intParse(strconv.ParseUint, s, byteSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func floatToTime(f float64) time.Time {
|
||||||
|
s := int64(f)
|
||||||
|
ns := int64((f - float64(s)) * 1_000_000_000)
|
||||||
|
return time.Unix(s, ns)
|
||||||
|
}
|
||||||
|
|
||||||
func scanConvert(t reflect.Type, v any) (any, bool) {
|
func scanConvert(t reflect.Type, v any) (any, bool) {
|
||||||
if isAny(t) {
|
if isAny(t) {
|
||||||
return v, true
|
return v, true
|
||||||
}
|
}
|
||||||
|
|
||||||
r := reflect.ValueOf(v)
|
r := reflect.ValueOf(v)
|
||||||
if !r.CanConvert(t) {
|
if r.CanConvert(t) {
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.Convert(t).Interface(), true
|
return r.Convert(t).Interface(), true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t != reflect.TypeFor[time.Time]() {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
fi := reflect.TypeFor[float64]()
|
||||||
|
if !r.CanConvert(fi) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
f := r.Convert(fi).Interface().(float64)
|
||||||
|
return floatToTime(f), true
|
||||||
|
}
|
||||||
|
|
||||||
func parseTime(s string) (any, error) {
|
func parseTime(s string) (any, error) {
|
||||||
|
const errMsg = "failed to parse time"
|
||||||
for _, l := range timeLayouts {
|
for _, l := range timeLayouts {
|
||||||
if t, err := time.Parse(l, s); err == nil {
|
if t, err := time.Parse(l, s); err == nil {
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Time{}, errors.New("failed to parse time")
|
ss := strings.Split(s, ".")
|
||||||
|
if len(ss) > 2 {
|
||||||
|
return nil, errors.New(errMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ss) == 1 {
|
||||||
|
i, err := parseInt(ss[0], 8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(errMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Unix(i, 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sec, err := parseInt(ss[0], 8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(errMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
dec := strings.TrimLeft(ss[1], "0")
|
||||||
|
i, err := parseInt(dec, 8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(errMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
zeros := len(ss[1]) - len(dec)
|
||||||
|
nanosec := int64(float64(i) * math.Pow10(9-zeros-len(dec)))
|
||||||
|
return time.Unix(sec, nanosec), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func scanString(t reflect.Type, s string) (any, bool) {
|
func scanString(t reflect.Type, s string) (any, bool) {
|
||||||
@ -117,7 +162,6 @@ func scanString(t reflect.Type, s string) (any, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func scan(t reflect.Type, v any) (any, bool) {
|
func scan(t reflect.Type, v any) (any, bool) {
|
||||||
// time and duration support
|
|
||||||
if vv, ok := scanConvert(t, v); ok {
|
if vv, ok := scanConvert(t, v); ok {
|
||||||
return vv, true
|
return vv, true
|
||||||
}
|
}
|
||||||
|
|||||||
66
scan_test.go
66
scan_test.go
@ -2,6 +2,7 @@ package bind_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"code.squareroundforest.org/arpio/bind"
|
"code.squareroundforest.org/arpio/bind"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -145,6 +146,71 @@ func TestScan(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("time from int", func(t *testing.T) {
|
||||||
|
const timestamp = 1757097011
|
||||||
|
var tim time.Time
|
||||||
|
if !bind.BindScalar(&tim, timestamp) || !tim.Equal(time.Unix(timestamp, 0)) {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("time from float", func(t *testing.T) {
|
||||||
|
const (
|
||||||
|
seconds = 1757097011
|
||||||
|
nanoseconds = 33000
|
||||||
|
timestamp = float64(1757097011.000033)
|
||||||
|
)
|
||||||
|
|
||||||
|
var tim time.Time
|
||||||
|
if !bind.BindScalar(&tim, timestamp) || tim.Unix() != seconds {
|
||||||
|
t.Fatal(tim, tim.Unix(), tim.UnixNano())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("time from int string", func(t *testing.T) {
|
||||||
|
const seconds = 1757097011
|
||||||
|
var tim time.Time
|
||||||
|
if !bind.BindScalar(&tim, fmt.Sprint(seconds)) || tim.Unix() != seconds {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("time from float string", func(t *testing.T) {
|
||||||
|
const (
|
||||||
|
seconds = 1757097011
|
||||||
|
nanoseconds = 33000
|
||||||
|
s = "1757097011.000033"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tim time.Time
|
||||||
|
if !bind.BindScalar(&tim, s) || tim.Unix() != seconds || tim.Nanosecond() != nanoseconds {
|
||||||
|
t.Fatal(tim.Unix(), tim.Nanosecond())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("time from invalid float string", func(t *testing.T) {
|
||||||
|
t.Run("multiple decimal points", func(t *testing.T) {
|
||||||
|
var tim time.Time
|
||||||
|
if bind.BindScalar(&tim, "24.235.23") {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid round part", func(t *testing.T) {
|
||||||
|
var tim time.Time
|
||||||
|
if bind.BindScalar(&tim, "89s.01") {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid fractional part", func(t *testing.T) {
|
||||||
|
var tim time.Time
|
||||||
|
if bind.BindScalar(&tim, "89.01s") {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("time invalid", func(t *testing.T) {
|
t.Run("time invalid", func(t *testing.T) {
|
||||||
var tim time.Time
|
var tim time.Time
|
||||||
if bind.BindScalar(&tim, "foo") {
|
if bind.BindScalar(&tim, "foo") {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user