diff --git a/utils.go b/utils.go index b0c6e9ca3..d6545f5be 100644 --- a/utils.go +++ b/utils.go @@ -277,15 +277,9 @@ func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Va } func appendDateTime(buf []byte, t time.Time) ([]byte, error) { - nsec := t.Nanosecond() - // to round under microsecond - if nsec%1000 >= 500 { // save half of time.Time.Add calls - t = t.Add(500 * time.Nanosecond) - nsec = t.Nanosecond() - } year, month, day := t.Date() hour, min, sec := t.Clock() - micro := nsec / 1000 + nsec := t.Nanosecond() if year < 1 || year > 9999 { return buf, errors.New("year is not in the range [1, 9999]: " + strconv.Itoa(year)) // use errors.New instead of fmt.Errorf to avoid year escape to heap @@ -293,14 +287,14 @@ func appendDateTime(buf []byte, t time.Time) ([]byte, error) { year100 := year / 100 year1 := year % 100 - var localBuf [26]byte // does not escape + var localBuf [len("2006-01-02T15:04:05.999999999")]byte // does not escape localBuf[0], localBuf[1], localBuf[2], localBuf[3] = digits10[year100], digits01[year100], digits10[year1], digits01[year1] localBuf[4] = '-' localBuf[5], localBuf[6] = digits10[month], digits01[month] localBuf[7] = '-' localBuf[8], localBuf[9] = digits10[day], digits01[day] - if hour == 0 && min == 0 && sec == 0 && micro == 0 { + if hour == 0 && min == 0 && sec == 0 && nsec == 0 { return append(buf, localBuf[:10]...), nil } @@ -311,18 +305,33 @@ func appendDateTime(buf []byte, t time.Time) ([]byte, error) { localBuf[16] = ':' localBuf[17], localBuf[18] = digits10[sec], digits01[sec] - if micro == 0 { + if nsec == 0 { return append(buf, localBuf[:19]...), nil } - - micro10000 := micro / 10000 - micro100 := (micro / 100) % 100 - micro1 := micro % 100 + nsec100000000 := nsec / 100000000 + nsec1000000 := (nsec / 1000000) % 100 + nsec10000 := (nsec / 10000) % 100 + nsec100 := (nsec / 100) % 100 + nsec1 := nsec % 100 localBuf[19] = '.' - localBuf[20], localBuf[21], localBuf[22], localBuf[23], localBuf[24], localBuf[25] = - digits10[micro10000], digits01[micro10000], digits10[micro100], digits01[micro100], digits10[micro1], digits01[micro1] - return append(buf, localBuf[:]...), nil + // milli second + localBuf[20], localBuf[21], localBuf[22] = + digits01[nsec100000000], digits10[nsec1000000], digits01[nsec1000000] + // micro second + localBuf[23], localBuf[24], localBuf[25] = + digits10[nsec10000], digits01[nsec10000], digits10[nsec100] + // nano second + localBuf[26], localBuf[27], localBuf[28] = + digits01[nsec100], digits10[nsec1], digits01[nsec1] + + // trim trailing zeros + n := len(localBuf) + for n > 0 && localBuf[n-1] == '0' { + n-- + } + + return append(buf, localBuf[:n]...), nil } // zeroDateTime is used in formatBinaryDateTime to avoid an allocation diff --git a/utils_test.go b/utils_test.go index e3619e7a7..67b132d2b 100644 --- a/utils_test.go +++ b/utils_test.go @@ -299,40 +299,40 @@ func TestAppendDateTime(t *testing.T) { str string }{ { - t: time.Date(2020, 05, 30, 0, 0, 0, 0, time.UTC), - str: "2020-05-30", + t: time.Date(1234, 5, 6, 0, 0, 0, 0, time.UTC), + str: "1234-05-06", }, { - t: time.Date(2020, 05, 30, 22, 0, 0, 0, time.UTC), - str: "2020-05-30 22:00:00", + t: time.Date(4567, 12, 31, 12, 0, 0, 0, time.UTC), + str: "4567-12-31 12:00:00", }, { - t: time.Date(2020, 05, 30, 22, 33, 0, 0, time.UTC), - str: "2020-05-30 22:33:00", + t: time.Date(2020, 5, 30, 12, 34, 0, 0, time.UTC), + str: "2020-05-30 12:34:00", }, { - t: time.Date(2020, 05, 30, 22, 33, 44, 0, time.UTC), - str: "2020-05-30 22:33:44", + t: time.Date(2020, 5, 30, 12, 34, 56, 0, time.UTC), + str: "2020-05-30 12:34:56", }, { - t: time.Date(2020, 05, 30, 22, 33, 44, 550000000, time.UTC), - str: "2020-05-30 22:33:44.550000", + t: time.Date(2020, 5, 30, 22, 33, 44, 123000000, time.UTC), + str: "2020-05-30 22:33:44.123", }, { - t: time.Date(2020, 05, 30, 22, 33, 44, 550000499, time.UTC), - str: "2020-05-30 22:33:44.550000", + t: time.Date(2020, 5, 30, 22, 33, 44, 123456000, time.UTC), + str: "2020-05-30 22:33:44.123456", }, { - t: time.Date(2020, 05, 30, 22, 33, 44, 550000500, time.UTC), - str: "2020-05-30 22:33:44.550001", + t: time.Date(2020, 5, 30, 22, 33, 44, 123456789, time.UTC), + str: "2020-05-30 22:33:44.123456789", }, { - t: time.Date(2020, 05, 30, 22, 33, 44, 550000567, time.UTC), - str: "2020-05-30 22:33:44.550001", + t: time.Date(9999, 12, 31, 23, 59, 59, 999999999, time.UTC), + str: "9999-12-31 23:59:59.999999999", }, { - t: time.Date(2020, 05, 30, 22, 33, 44, 999999567, time.UTC), - str: "2020-05-30 22:33:45", + t: time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC), + str: "0001-01-01", }, } for _, v := range tests { @@ -340,7 +340,6 @@ func TestAppendDateTime(t *testing.T) { buf, _ = appendDateTime(buf, v.t) if str := string(buf); str != v.str { t.Errorf("appendDateTime(%v), have: %s, want: %s", v.t, str, v.str) - return } }