Skip to content

Commit f87f8bc

Browse files
inothlance6716
andauthored
feat: Added time.Time support for datetime format (#957)
* feat: Added support for datetime format * fix: handler "Error return value of `binary.Write` is not checked" * fix: add toBinaryDateTime test * fix: update TestToBinaryDateTime * fix: allow zero time * fix: code format --------- Co-authored-by: lance6716 <[email protected]>
1 parent 2f42179 commit f87f8bc

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

mysql/resultset_helper.go

+48-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package mysql
22

33
import (
4+
"bytes"
5+
"encoding/binary"
46
"math"
57
"strconv"
8+
"time"
69

710
"github.com/pingcap/errors"
811

@@ -39,13 +42,53 @@ func FormatTextValue(value interface{}) ([]byte, error) {
3942
return v, nil
4043
case string:
4144
return utils.StringToByteSlice(v), nil
45+
case time.Time:
46+
return utils.StringToByteSlice(v.Format(time.DateTime)), nil
4247
case nil:
4348
return nil, nil
4449
default:
4550
return nil, errors.Errorf("invalid type %T", value)
4651
}
4752
}
4853

54+
func toBinaryDateTime(t time.Time) ([]byte, error) {
55+
var buf bytes.Buffer
56+
57+
if t.IsZero() {
58+
return nil, nil
59+
}
60+
61+
year, month, day := t.Year(), t.Month(), t.Day()
62+
hour, min, sec := t.Hour(), t.Minute(), t.Second()
63+
nanosec := t.Nanosecond()
64+
65+
if nanosec > 0 {
66+
buf.WriteByte(byte(11))
67+
_ = binary.Write(&buf, binary.LittleEndian, uint16(year))
68+
buf.WriteByte(byte(month))
69+
buf.WriteByte(byte(day))
70+
buf.WriteByte(byte(hour))
71+
buf.WriteByte(byte(min))
72+
buf.WriteByte(byte(sec))
73+
_ = binary.Write(&buf, binary.LittleEndian, uint32(nanosec/1000))
74+
} else if hour > 0 || min > 0 || sec > 0 {
75+
buf.WriteByte(byte(7))
76+
_ = binary.Write(&buf, binary.LittleEndian, uint16(year))
77+
buf.WriteByte(byte(month))
78+
buf.WriteByte(byte(day))
79+
buf.WriteByte(byte(hour))
80+
buf.WriteByte(byte(min))
81+
buf.WriteByte(byte(sec))
82+
} else {
83+
buf.WriteByte(byte(4))
84+
_ = binary.Write(&buf, binary.LittleEndian, uint16(year))
85+
buf.WriteByte(byte(month))
86+
buf.WriteByte(byte(day))
87+
}
88+
89+
return buf.Bytes(), nil
90+
}
91+
4992
func formatBinaryValue(value interface{}) ([]byte, error) {
5093
switch v := value.(type) {
5194
case int8:
@@ -76,6 +119,8 @@ func formatBinaryValue(value interface{}) ([]byte, error) {
76119
return v, nil
77120
case string:
78121
return utils.StringToByteSlice(v), nil
122+
case time.Time:
123+
return toBinaryDateTime(v)
79124
default:
80125
return nil, errors.Errorf("invalid type %T", value)
81126
}
@@ -91,6 +136,8 @@ func fieldType(value interface{}) (typ uint8, err error) {
91136
typ = MYSQL_TYPE_DOUBLE
92137
case string, []byte:
93138
typ = MYSQL_TYPE_VAR_STRING
139+
case time.Time:
140+
typ = MYSQL_TYPE_DATETIME
94141
case nil:
95142
typ = MYSQL_TYPE_NULL
96143
default:
@@ -110,7 +157,7 @@ func formatField(field *Field, value interface{}) error {
110157
case float32, float64:
111158
field.Charset = 63
112159
field.Flag = BINARY_FLAG | NOT_NULL_FLAG
113-
case string, []byte:
160+
case string, []byte, time.Time:
114161
field.Charset = 33
115162
case nil:
116163
field.Charset = 33

mysql/util_test.go

+62
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package mysql
22

33
import (
44
"testing"
5+
"time"
56

67
"github.com/stretchr/testify/require"
78
)
@@ -51,3 +52,64 @@ func TestFormatBinaryTime(t *testing.T) {
5152
require.Equal(t, test.Expect, string(got), "test case %v", test.Data)
5253
}
5354
}
55+
56+
func TestToBinaryDateTime(t *testing.T) {
57+
var (
58+
DateTimeNano = "2006-01-02 15:04:05.000000"
59+
formatBinaryDateTime = func(n int, data []byte) string {
60+
date, err := FormatBinaryDateTime(n, data)
61+
if err != nil {
62+
return ""
63+
}
64+
return string(date)
65+
}
66+
)
67+
68+
tests := []struct {
69+
Name string
70+
Data time.Time
71+
Expect func(n int, data []byte) string
72+
Error bool
73+
}{
74+
{
75+
Name: "Zero time",
76+
Data: time.Time{},
77+
Expect: nil,
78+
},
79+
{
80+
Name: "Date with nanoseconds",
81+
Data: time.Date(2023, 10, 10, 10, 10, 10, 123456000, time.UTC),
82+
Expect: formatBinaryDateTime,
83+
},
84+
{
85+
Name: "Date with time",
86+
Data: time.Date(2023, 10, 10, 10, 10, 10, 0, time.UTC),
87+
Expect: formatBinaryDateTime,
88+
},
89+
{
90+
Name: "Date only",
91+
Data: time.Date(2023, 10, 10, 0, 0, 0, 0, time.UTC),
92+
Expect: formatBinaryDateTime,
93+
},
94+
}
95+
96+
for _, test := range tests {
97+
t.Run(test.Name, func(t *testing.T) {
98+
got, err := toBinaryDateTime(test.Data)
99+
if test.Error {
100+
require.Error(t, err)
101+
} else {
102+
require.NoError(t, err)
103+
}
104+
if len(got) == 0 {
105+
return
106+
}
107+
tmp := test.Expect(int(got[0]), got[1:])
108+
if int(got[0]) < 11 {
109+
require.Equal(t, tmp, test.Data.Format(time.DateTime), "test case %v", test.Data.String())
110+
} else {
111+
require.Equal(t, tmp, test.Data.Format(DateTimeNano), "test case %v", test.Data.String())
112+
}
113+
})
114+
}
115+
}

0 commit comments

Comments
 (0)