Skip to content

Commit 7719f53

Browse files
committed
With native time type [TO SQUASH]
1 parent c4d60c6 commit 7719f53

File tree

3 files changed

+175
-86
lines changed

3 files changed

+175
-86
lines changed

datetime/config.lua

-2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ s:truncate()
3434
box.schema.user.grant('test', 'read,write', 'space', 'testDatetime', { if_not_exists = true })
3535
box.schema.user.grant('guest', 'read,write', 'space', 'testDatetime', { if_not_exists = true })
3636

37-
s:insert({ datetime.new() })
38-
3937
-- Set listen only when every other thing is configured.
4038
box.cfg{
4139
listen = os.getenv("TEST_TNT_LISTEN"),

datetime/datetime.go

+79-25
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package datetime
22

33
import (
44
"fmt"
5+
"math"
56
"reflect"
7+
"time"
68

79
"encoding/binary"
810

@@ -31,29 +33,37 @@ const (
3133
type EventTime struct {
3234
// Seconds since Epoch
3335
Seconds int64
34-
// Nanoseconds, if any
36+
// Nanoseconds
3537
Nsec int32
36-
// Offset in minutes from UTC
38+
// Timezone offset (in minutes) from UTC
3739
TZOffset int16
3840
// Olson timezone id
3941
TZIndex int16
4042
}
4143

4244
func encodeDatetime(e *msgpack.Encoder, v reflect.Value) error {
43-
tm := v.Interface().(EventTime)
45+
tm := v.Interface().(time.Time)
46+
47+
seconds := tm.Unix()
48+
nanosec := tm.UnixNano() - int64(seconds*int64(math.Pow(10, 9)))
49+
50+
// tzindex and tzoffset are not implemented
51+
// See https://github.com/tarantool/tarantool/issues/6751
52+
tzindex := 0
53+
tzoffset := 0
4454

4555
var payloadLen = 8
46-
if tm.Nsec != 0 || tm.TZOffset != 0 || tm.TZIndex != 0 {
56+
if nanosec != 0 || tzoffset != 0 || tzindex != 0 {
4757
payloadLen = 16
4858
}
4959

5060
b := make([]byte, payloadLen)
51-
binary.LittleEndian.PutUint64(b, uint64(tm.Seconds))
61+
binary.LittleEndian.PutUint64(b, uint64(seconds))
5262

5363
if payloadLen == 16 {
54-
binary.LittleEndian.PutUint32(b[NSEC_LEN:], uint32(tm.Nsec))
55-
binary.LittleEndian.PutUint16(b[TZ_OFFSET_LEN:], uint16(tm.TZOffset))
56-
binary.LittleEndian.PutUint16(b[TZ_INDEX_LEN:], uint16(tm.TZIndex))
64+
binary.LittleEndian.PutUint32(b[NSEC_LEN:], uint32(nanosec))
65+
binary.LittleEndian.PutUint16(b[TZ_OFFSET_LEN:], uint16(tzoffset))
66+
binary.LittleEndian.PutUint16(b[TZ_INDEX_LEN:], uint16(tzindex))
5767
}
5868

5969
_, err := e.Writer().Write(b)
@@ -65,32 +75,76 @@ func encodeDatetime(e *msgpack.Encoder, v reflect.Value) error {
6575
}
6676

6777
func decodeDatetime(d *msgpack.Decoder, v reflect.Value) error {
68-
var tm EventTime
69-
var err error
78+
/*
79+
var (
80+
seconds int64
81+
nanosec int32
82+
err error
83+
)
84+
85+
seconds, err = d.DecodeInt64()
86+
if err != nil {
87+
return fmt.Errorf("msgpack: can't read bytes on datetime seconds decode: %w", err)
88+
}
7089
71-
tm.Seconds, err = d.DecodeInt64()
90+
nanosec, err = d.DecodeInt32()
91+
if err == nil {
92+
if err != nil {
93+
return fmt.Errorf("msgpack: can't read bytes on datetime nanoseconds decode: %w", err)
94+
}
95+
_, err = d.DecodeInt16()
96+
if err != nil {
97+
return fmt.Errorf("msgpack: can't read bytes on datetime timezone offset decode: %w", err)
98+
}
99+
_, err = d.DecodeInt16()
100+
if err != nil {
101+
return fmt.Errorf("msgpack: can't read bytes on datetime timezone index decode: %w", err)
102+
}
103+
}
104+
105+
nsec := nanosec - int32(seconds*int64(math.Pow(10, 9)))
106+
nsec = 0 // FIXME
107+
t := time.Unix(seconds, int64(nsec)).UTC()
108+
v.Set(reflect.ValueOf(t))
109+
110+
return nil
111+
*/
112+
113+
var bytesCount int = 8
114+
b := make([]byte, bytesCount)
115+
116+
n, err := d.Buffered().Read(b)
72117
if err != nil {
73-
return fmt.Errorf("msgpack: can't read bytes on datetime seconds decode: %w", err)
118+
return fmt.Errorf("msgpack: can't read bytes on datetime decode: %w", err)
74119
}
75120

76-
tm.Nsec, err = d.DecodeInt32()
77-
if err == nil {
78-
tm.TZOffset, err = d.DecodeInt16()
79-
if err != nil {
80-
return fmt.Errorf("msgpack: can't read bytes on datetime tzoffset decode: %w", err)
81-
}
82-
tm.TZIndex, err = d.DecodeInt16()
83-
if err != nil {
84-
return fmt.Errorf("msgpack: can't read bytes on datetime tzindex decode: %w", err)
85-
}
121+
if n < bytesCount {
122+
return fmt.Errorf("msgpack: unexpected end of stream after %d datetime bytes", n)
123+
}
124+
125+
payload_len := len(b)
126+
if payload_len != 8 && payload_len != 16 {
127+
return fmt.Errorf("Invalid data length: got %d, wanted 8", len(b))
128+
}
129+
130+
seconds := int64(binary.LittleEndian.Uint64(b[:SEC_LEN]))
131+
132+
var nanosec int32
133+
opt_buf_len := NSEC_LEN + TZ_OFFSET_LEN + TZ_INDEX_LEN
134+
if payload_len == 16 {
135+
opt_buf := b[opt_buf_len:]
136+
nanosec = int32(binary.LittleEndian.Uint32(opt_buf[:NSEC_LEN]))
86137
}
87138

88-
v.Set(reflect.ValueOf(tm))
139+
nsec := nanosec + int32(seconds*int64(math.Pow(10, 9)))
140+
nsec = 0 // FIXME
141+
t := time.Unix(seconds, int64(nsec)).UTC()
142+
v.Set(reflect.ValueOf(t))
89143

90144
return nil
91145
}
92146

93147
func init() {
94-
msgpack.Register(reflect.TypeOf((*EventTime)(nil)).Elem(), encodeDatetime, decodeDatetime)
95-
msgpack.RegisterExt(Datetime_extId, (*EventTime)(nil))
148+
msgpack.Register(reflect.TypeOf((*time.Time)(nil)).Elem(), encodeDatetime, decodeDatetime)
149+
msgpack.RegisterExt(Datetime_extId, (*time.Time)(nil))
96150
}

datetime/datetime_test.go

+96-59
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
package datetime_test
22

33
import (
4-
_ "bytes"
4+
"bufio"
5+
"bytes"
56
"fmt"
67
"log"
78
"os"
9+
"reflect"
810
"testing"
911
"time"
1012

1113
. "github.com/tarantool/go-tarantool"
12-
"github.com/tarantool/go-tarantool/datetime"
1314
"github.com/tarantool/go-tarantool/test_helpers"
1415
"gopkg.in/vmihailenco/msgpack.v2"
1516
)
@@ -30,7 +31,7 @@ var space = "testDatetime"
3031
var index = "primary"
3132

3233
type TupleDatetime struct {
33-
tm datetime.EventTime
34+
tm time.Time
3435
}
3536

3637
func (t *TupleDatetime) DecodeMsgpack(d *msgpack.Decoder) error {
@@ -47,7 +48,7 @@ func (t *TupleDatetime) DecodeMsgpack(d *msgpack.Decoder) error {
4748
if err != nil {
4849
return err
4950
}
50-
t.tm = res.(datetime.EventTime)
51+
t.tm = res.(time.Time)
5152

5253
return nil
5354
}
@@ -63,79 +64,106 @@ func connectWithValidation(t *testing.T) *Connection {
6364
return conn
6465
}
6566

66-
func tupleValueIsDatetime(t *testing.T, tuples []interface{}, tm datetime.EventTime) {
67+
func tupleValueIsDatetime(t *testing.T, tuples []interface{}, tm time.Time) {
6768
if tpl, ok := tuples[0].([]interface{}); !ok {
6869
t.Errorf("Unexpected return value body")
6970
} else {
70-
if len(tpl) != 1 {
71-
t.Errorf("Unexpected return value body (tuple len)")
72-
}
73-
if val, ok := tpl[0].(datetime.EventTime); !ok || val != tm {
71+
/*
72+
if len(tpl) != 1 {
73+
t.Errorf("Unexpected return value body (tuple len = %d)", len(tpl))
74+
}
75+
*/
76+
if val, ok := tpl[0].(time.Time); !ok || val != tm {
77+
fmt.Println("Tuple: ", tpl[0])
78+
fmt.Println("Expected:", val)
7479
t.Errorf("Unexpected return value body (tuple 0 field)")
7580
}
7681
}
7782
}
7883

7984
func TestEncodeDecode(t *testing.T) {
80-
t.Skip("Not imeplemented")
85+
t.Skip("not implemented")
8186
if isDatetimeSupported == false {
8287
t.Skip("Skipping test for Tarantool without datetime support in msgpack")
8388
}
8489

85-
//e := msgpack.NewEncoder(w io.Writer)
86-
/*
87-
r := bytes.NewReader(byteData)
88-
d := msgpack.NewDecoder(r)
89-
var tm datetime.EventTime
90-
//func (t *TupleDatetime) DecodeMsgpack(d *msgpack.Decoder) error {
91-
var err error
92-
var l int
93-
if l, err = d.DecodeSliceLen(); err != nil {
94-
t.Errorf("Datetime decode failed")
95-
}
96-
if l != 1 {
97-
t.Errorf("array len doesn't match: %d", l)
98-
}
90+
tm, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
91+
if err != nil {
92+
t.Errorf("Parse is failed %s", err)
93+
}
9994

100-
res, err := d.DecodeInterface()
101-
if err != nil {
102-
return err
103-
}
104-
t.tm = res.(datetime.EventTime)
105-
*/
106-
}
95+
// Encode
96+
var buf bytes.Buffer
97+
bufferWriter := bufio.NewWriter(&buf)
98+
e := msgpack.NewEncoder(bufferWriter)
10799

108-
func TestSelect(t *testing.T) {
109-
if isDatetimeSupported == false {
110-
t.Skip("Skipping test for Tarantool without datetime support in msgpack")
100+
err = e.EncodeValue(reflect.ValueOf(tm))
101+
if err != nil {
102+
t.Errorf("Datetime encode failed %s", err)
111103
}
104+
fmt.Printf("%x\n", buf)
112105

113-
conn := connectWithValidation(t)
114-
defer conn.Close()
106+
// Decode
107+
r := bufio.NewReader(&buf)
108+
d := msgpack.NewDecoder(r)
109+
res, err := d.DecodeInterface()
110+
if err != nil {
111+
t.Errorf("Datetime encode failed %s", err)
112+
}
113+
dt := res.(time.Time)
114+
if tm != dt {
115+
fmt.Println("not equal")
116+
}
117+
}
118+
119+
func tupleRoundtrip(t *testing.T, conn *Connection, datetime string) {
120+
tm, err := time.Parse(time.RFC3339, datetime)
121+
if err != nil {
122+
t.Errorf("Time (%s) parse failed: %s", datetime, err)
123+
}
115124

116-
tm := datetime.EventTime{0, 0, 0, 0}
125+
// Insert tuple with datetime.
126+
resp, err := conn.Insert(space, []interface{}{tm})
127+
if err != nil {
128+
t.Errorf("Datetime insert failed: %s", err.Error())
129+
}
130+
fmt.Println(resp)
117131

132+
// Select tuple with datetime.
118133
var offset uint32 = 0
119134
var limit uint32 = 1
120-
resp, errSel := conn.Select(space, index, offset, limit, IterEq, []interface{}{tm})
121-
if errSel != nil {
122-
t.Errorf("Datetime select failed: %s", errSel.Error())
135+
resp, err = conn.Select(space, index, offset, limit, IterEq, []interface{}{tm})
136+
if err != nil {
137+
t.Errorf("Datetime select failed: %s", err.Error())
123138
}
124139
if resp == nil {
125140
t.Errorf("Response is nil after Select")
126141
}
127142
tupleValueIsDatetime(t, resp.Data, tm)
128143

129-
var tuples []TupleDatetime
130-
errTyp := conn.SelectTyped(space, index, 0, 1, IterEq, []interface{}{tm}, &tuples)
131-
if errTyp != nil {
132-
t.Errorf("Failed to SelectTyped: %s", errTyp.Error())
144+
// Delete tuple with datetime.
145+
resp, err = conn.Delete(space, index, []interface{}{tm})
146+
if err != nil {
147+
t.Errorf("Datetime detele failed: %s", err.Error())
133148
}
134-
if len(tuples) != 1 {
135-
t.Errorf("Result len of SelectTyped != 1")
149+
fmt.Println(resp)
150+
}
151+
152+
var datetimes = []string{
153+
// "2012-01-31T23:59:59.000000010Z",
154+
"2006-01-02T15:04:05Z",
155+
}
156+
157+
func TestSelect(t *testing.T) {
158+
if isDatetimeSupported == false {
159+
t.Skip("Skipping test for Tarantool without datetime support in msgpack")
136160
}
137-
if tuples[0].tm != tm {
138-
t.Errorf("Bad value loaded from SelectTyped: %d", tuples[0].tm.Seconds)
161+
162+
conn := connectWithValidation(t)
163+
defer conn.Close()
164+
165+
for _, dt := range datetimes {
166+
tupleRoundtrip(t, conn, dt)
139167
}
140168
}
141169

@@ -147,25 +175,34 @@ func TestReplace(t *testing.T) {
147175
conn := connectWithValidation(t)
148176
defer conn.Close()
149177

150-
tm := datetime.EventTime{15, 0, 0, 0}
178+
tm, err := time.Parse(time.RFC3339, "2007-01-02T15:04:05Z")
179+
if err != nil {
180+
t.Errorf("Time parse failed: %s", err)
181+
}
151182

152-
respRep, errRep := conn.Replace(space, []interface{}{tm})
153-
if errRep != nil {
154-
t.Errorf("Datetime replace failed: %s", errRep)
183+
resp, err := conn.Replace(space, []interface{}{tm})
184+
if err != nil {
185+
t.Errorf("Datetime replace failed: %s", err)
155186
}
156-
if respRep == nil {
187+
if resp == nil {
157188
t.Errorf("Response is nil after Replace")
158189
}
159-
tupleValueIsDatetime(t, respRep.Data, tm)
190+
tupleValueIsDatetime(t, resp.Data, tm)
160191

161-
respSel, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{tm})
162-
if errSel != nil {
163-
t.Errorf("Datetime select failed: %s", errSel)
192+
resp, err = conn.Select(space, index, 0, 1, IterEq, []interface{}{tm})
193+
if err != nil {
194+
t.Errorf("Datetime select failed: %s", err)
164195
}
165-
if respSel == nil {
196+
if resp == nil {
166197
t.Errorf("Response is nil after Select")
167198
}
168-
tupleValueIsDatetime(t, respSel.Data, tm)
199+
tupleValueIsDatetime(t, resp.Data, tm)
200+
201+
// Delete tuple with datetime.
202+
resp, err = conn.Delete(space, index, []interface{}{tm})
203+
if err != nil {
204+
t.Errorf("Datetime detele failed: %s", err.Error())
205+
}
169206
}
170207

171208
// runTestMain is a body of TestMain function

0 commit comments

Comments
 (0)