gotosocial/vendor/github.com/vmihailenco/msgpack/v5/time.go

152 lines
3 KiB
Go

package msgpack
import (
"encoding/binary"
"fmt"
"reflect"
"time"
"github.com/vmihailenco/msgpack/v5/msgpcode"
)
var timeExtID int8 = -1
func init() {
RegisterExtEncoder(timeExtID, time.Time{}, timeEncoder)
RegisterExtDecoder(timeExtID, time.Time{}, timeDecoder)
}
func timeEncoder(e *Encoder, v reflect.Value) ([]byte, error) {
return e.encodeTime(v.Interface().(time.Time)), nil
}
func timeDecoder(d *Decoder, v reflect.Value, extLen int) error {
tm, err := d.decodeTime(extLen)
if err != nil {
return err
}
if tm.IsZero() {
// Zero time does not have timezone information.
tm = tm.UTC()
}
ptr := v.Addr().Interface().(*time.Time)
*ptr = tm
return nil
}
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
}
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:], secs)
return b
}
func (d *Decoder) DecodeTime() (time.Time, error) {
c, err := d.readCode()
if err != nil {
return time.Time{}, err
}
// Legacy format.
if c == msgpcode.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 msgpcode.IsString(c) {
s, err := d.string(c)
if err != nil {
return time.Time{}, err
}
return time.Parse(time.RFC3339Nano, s)
}
extID, extLen, err := d.extHeader(c)
if err != nil {
return time.Time{}, err
}
// NodeJS seems to use extID 13.
if extID != timeExtID && extID != 13 {
return time.Time{}, fmt.Errorf("msgpack: invalid time ext id=%d", extID)
}
tm, err := d.decodeTime(extLen)
if err != nil {
return tm, err
}
if tm.IsZero() {
// Zero time does not have timezone information.
return tm.UTC(), nil
}
return tm, nil
}
func (d *Decoder) decodeTime(extLen int) (time.Time, error) {
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
}
}