Przeglądaj źródła

codec: treat time.Time{} as the zero value for time.Time.

Encode a zero value of time.Time as nil,
and decode nil as a zero value of time.Time.

Also, ensure isEmptyValue handles time.Time zero value as empty
and set a time.Time to the zero value during a setZero call.
Ugorji Nwoke 8 lat temu
rodzic
commit
679a71d0bd
8 zmienionych plików z 55 dodań i 12 usunięć
  1. 13 5
      codec/binc.go
  2. 7 1
      codec/cbor.go
  3. 6 0
      codec/codec_test.go
  4. 2 0
      codec/decode.go
  5. 5 0
      codec/helper_internal.go
  6. 11 4
      codec/json.go
  7. 9 1
      codec/msgpack.go
  8. 2 1
      codec/simple.go

+ 13 - 5
codec/binc.go

@@ -83,9 +83,13 @@ func (e *bincEncDriver) EncodeNil() {
 // }
 
 func (e *bincEncDriver) EncodeTime(t time.Time) {
-	bs := encodeTime(t)
-	e.w.writen1(bincVdTimestamp<<4 | uint8(len(bs)))
-	e.w.writeb(bs)
+	if t.IsZero() {
+		e.EncodeNil()
+	} else {
+		bs := encodeTime(t)
+		e.w.writen1(bincVdTimestamp<<4 | uint8(len(bs)))
+		e.w.writeb(bs)
+	}
 }
 
 func (e *bincEncDriver) EncodeBool(b bool) {
@@ -397,15 +401,19 @@ func (d *bincDecDriver) TryDecodeAsNil() bool {
 // 	return rt == timeTypId
 // }
 
-func (d *bincDecDriver) DecodeTime() (tt time.Time) {
+func (d *bincDecDriver) DecodeTime() (t time.Time) {
 	if !d.bdRead {
 		d.readNextBd()
 	}
+	if d.bd == bincVdSpecial<<4|bincSpNil {
+		d.bdRead = false
+		return
+	}
 	if d.vd != bincVdTimestamp {
 		d.d.errorf("Invalid d.vd. Expecting 0x%x. Received: 0x%x", bincVdTimestamp, d.vd)
 		return
 	}
-	tt, err := decodeTime(d.r.readx(int(d.vs)))
+	t, err := decodeTime(d.r.readx(int(d.vs)))
 	if err != nil {
 		panic(err)
 	}

+ 7 - 1
codec/cbor.go

@@ -128,7 +128,9 @@ func (e *cborEncDriver) encLen(bd byte, length int) {
 }
 
 func (e *cborEncDriver) EncodeTime(t time.Time) {
-	if e.h.TimeRFC3339 {
+	if t.IsZero() {
+		e.EncodeNil()
+	} else if e.h.TimeRFC3339 {
 		e.encUint(0, cborBaseTag)
 		e.EncodeString(cUTF8, t.Format(time.RFC3339Nano))
 	} else {
@@ -525,6 +527,10 @@ func (d *cborDecDriver) DecodeTime() (t time.Time) {
 	if !d.bdRead {
 		d.readNextBd()
 	}
+	if d.bd == cborBdNil || d.bd == cborBdUndefined {
+		d.bdRead = false
+		return
+	}
 	xtag := d.decUint()
 	d.bdRead = false
 	return d.decodeTime(xtag)

+ 6 - 0
codec/codec_test.go

@@ -906,6 +906,12 @@ func testCodecMiscOne(t *testing.T, h Handle) {
 		testUnmarshalErr(&ya, []byte{0x91, 0x90}, h, t, "ya")
 	}
 
+	var tt1, tt2 time.Time
+	tt2 = time.Now()
+	bs = testMarshalErr(tt1, h, t, "zero-time-enc")
+	testUnmarshalErr(&tt2, bs, h, t, "zero-time-dec")
+	testDeepEqualErr(tt1, tt2, t, "zero-time-eq")
+
 	// test encoding a slice of byte (but not []byte) and decoding into a []byte
 	var sw = []wrapUint8{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'}
 	var bw []byte // ("ABCDEFGHIJ")

+ 2 - 0
codec/decode.go

@@ -1997,6 +1997,8 @@ func setZero(iv interface{}) {
 		*v = nil
 	case *Raw:
 		*v = nil
+	case *time.Time:
+		*v = time.Time{}
 	case reflect.Value:
 		if v, canDecode = isDecodeable(v); canDecode && v.CanSet() {
 			v.Set(reflect.Zero(v.Type()))

+ 5 - 0
codec/helper_internal.go

@@ -10,6 +10,7 @@ import (
 	"errors"
 	"fmt"
 	"reflect"
+	"time"
 )
 
 func panicValToErr(panicVal interface{}, err *error) {
@@ -51,6 +52,10 @@ func hIsEmptyValue(v reflect.Value, deref, checkStruct bool) bool {
 		}
 		return v.IsNil()
 	case reflect.Struct:
+		// check for time.Time, and return true if IsZero
+		if rv2rtid(v) == timeTypId {
+			return rv2i(v).(time.Time).IsZero()
+		}
 		if !checkStruct {
 			return false
 		}

+ 11 - 4
codec/json.go

@@ -276,10 +276,14 @@ func (e *jsonEncDriver) EncodeNil() {
 func (e *jsonEncDriver) EncodeTime(t time.Time) {
 	// Do NOT use MarshalJSON, as it allocates internally.
 	// instead, we call AppendFormat directly, using our scratch buffer (e.b)
-	e.b[0] = '"'
-	b := t.AppendFormat(e.b[1:1], time.RFC3339Nano)
-	e.b[len(b)+1] = '"'
-	e.w.writeb(e.b[:len(b)+2])
+	if t.IsZero() {
+		e.EncodeNil()
+	} else {
+		e.b[0] = '"'
+		b := t.AppendFormat(e.b[1:1], time.RFC3339Nano)
+		e.b[len(b)+1] = '"'
+		e.w.writeb(e.b[:len(b)+2])
+	}
 	// fmt.Printf(">>>> time as a string: '%s'\n", e.b[:len(b)+2])
 	// v, err := t.MarshalJSON(); if err != nil { e.e.error(err) } e.w.writeb(v)
 }
@@ -687,6 +691,9 @@ func (d *jsonDecDriver) DecodeBool() (v bool) {
 func (d *jsonDecDriver) DecodeTime() (t time.Time) {
 	// read string, and pass the string into json.unmarshal
 	d.appendStringAsBytes()
+	if d.fnull {
+		return
+	}
 	t, err := time.Parse(time.RFC3339, stringView(d.bs))
 	if err != nil {
 		d.d.error(err)

+ 9 - 1
codec/msgpack.go

@@ -195,6 +195,10 @@ func (e *msgpackEncDriver) EncodeFloat64(f float64) {
 }
 
 func (e *msgpackEncDriver) EncodeTime(t time.Time) {
+	if t.IsZero() {
+		e.EncodeNil()
+		return
+	}
 	t = t.UTC()
 	sec, nsec := t.Unix(), uint64(t.Nanosecond())
 	var data64 uint64
@@ -702,7 +706,7 @@ func (d *msgpackDecDriver) TryDecodeAsNil() (v bool) {
 	}
 	if d.bd == mpNil {
 		d.bdRead = false
-		v = true
+		return true
 	}
 	return
 }
@@ -773,6 +777,10 @@ func (d *msgpackDecDriver) DecodeTime() (t time.Time) {
 	if !d.bdRead {
 		d.readNextBd()
 	}
+	if d.bd == mpNil {
+		d.bdRead = false
+		return
+	}
 	var clen int
 	switch d.ContainerType() {
 	case valueTypeBytes, valueTypeString:

+ 2 - 1
codec/simple.go

@@ -199,7 +199,8 @@ func (e *simpleEncDriver) EncodeStringBytes(c charEncoding, v []byte) {
 }
 
 func (e *simpleEncDriver) EncodeTime(t time.Time) {
-	if e.h.EncZeroValuesAsNil && e.c != containerMapKey && t.IsZero() {
+	// if e.h.EncZeroValuesAsNil && e.c != containerMapKey && t.IsZero() {
+	if t.IsZero() {
 		e.EncodeNil()
 		return
 	}