150 lines
2.9 KiB
Go
150 lines
2.9 KiB
Go
package msgpack
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"reflect"
|
|
"time"
|
|
|
|
"github.com/vmihailenco/msgpack/codes"
|
|
)
|
|
|
|
var timeExtId int8 = -1
|
|
|
|
func init() {
|
|
timeType := reflect.TypeOf((*time.Time)(nil)).Elem()
|
|
registerExt(timeExtId, timeType, encodeTimeValue, decodeTimeValue)
|
|
}
|
|
|
|
func (e *Encoder) EncodeTime(tm time.Time) error {
|
|
b := e.encodeTime(tm)
|
|
if err := e.encodeExtLen(len(b)); err != nil {
|
|
return err
|
|
}
|
|
if err := e.w.WriteByte(byte(timeExtId)); err != nil {
|
|
return err
|
|
}
|
|
return e.write(b)
|
|
}
|
|
|
|
func (e *Encoder) encodeTime(tm time.Time) []byte {
|
|
if e.timeBuf == nil {
|
|
e.timeBuf = make([]byte, 12)
|
|
}
|
|
|
|
secs := uint64(tm.Unix())
|
|
if secs>>34 == 0 {
|
|
data := uint64(tm.Nanosecond())<<34 | secs
|
|
if data&0xffffffff00000000 == 0 {
|
|
b := e.timeBuf[:4]
|
|
binary.BigEndian.PutUint32(b, uint32(data))
|
|
return b
|
|
} else {
|
|
b := e.timeBuf[:8]
|
|
binary.BigEndian.PutUint64(b, data)
|
|
return b
|
|
}
|
|
}
|
|
|
|
b := e.timeBuf[:12]
|
|
binary.BigEndian.PutUint32(b, uint32(tm.Nanosecond()))
|
|
binary.BigEndian.PutUint64(b[4:], uint64(secs))
|
|
return b
|
|
}
|
|
|
|
func (d *Decoder) DecodeTime() (time.Time, error) {
|
|
tm, err := d.decodeTime()
|
|
if err != nil {
|
|
return tm, err
|
|
}
|
|
|
|
if tm.IsZero() {
|
|
// Assume that zero time does not have timezone information.
|
|
return tm.UTC(), nil
|
|
}
|
|
return tm, nil
|
|
}
|
|
|
|
func (d *Decoder) decodeTime() (time.Time, error) {
|
|
extLen := d.extLen
|
|
d.extLen = 0
|
|
if extLen == 0 {
|
|
c, err := d.readCode()
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
// Legacy format.
|
|
if c == codes.FixedArrayLow|2 {
|
|
sec, err := d.DecodeInt64()
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
nsec, err := d.DecodeInt64()
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
return time.Unix(sec, nsec), nil
|
|
}
|
|
|
|
if codes.IsString(c) {
|
|
s, err := d.string(c)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
return time.Parse(time.RFC3339Nano, s)
|
|
}
|
|
|
|
extLen, err = d.parseExtLen(c)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
// Skip ext id.
|
|
_, err = d.s.ReadByte()
|
|
if err != nil {
|
|
return time.Time{}, nil
|
|
}
|
|
}
|
|
|
|
b, err := d.readN(extLen)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
|
|
switch len(b) {
|
|
case 4:
|
|
sec := binary.BigEndian.Uint32(b)
|
|
return time.Unix(int64(sec), 0), nil
|
|
case 8:
|
|
sec := binary.BigEndian.Uint64(b)
|
|
nsec := int64(sec >> 34)
|
|
sec &= 0x00000003ffffffff
|
|
return time.Unix(int64(sec), nsec), nil
|
|
case 12:
|
|
nsec := binary.BigEndian.Uint32(b)
|
|
sec := binary.BigEndian.Uint64(b[4:])
|
|
return time.Unix(int64(sec), int64(nsec)), nil
|
|
default:
|
|
err = fmt.Errorf("msgpack: invalid ext len=%d decoding time", extLen)
|
|
return time.Time{}, err
|
|
}
|
|
}
|
|
|
|
func encodeTimeValue(e *Encoder, v reflect.Value) error {
|
|
tm := v.Interface().(time.Time)
|
|
b := e.encodeTime(tm)
|
|
return e.write(b)
|
|
}
|
|
|
|
func decodeTimeValue(d *Decoder, v reflect.Value) error {
|
|
tm, err := d.DecodeTime()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v.Set(reflect.ValueOf(tm))
|
|
return nil
|
|
}
|