]>
Commit | Line | Data |
---|---|---|
107c1cdb ND |
1 | package msgpack |
2 | ||
3 | import ( | |
4 | "encoding/binary" | |
5 | "fmt" | |
6 | "reflect" | |
7 | "time" | |
8 | ||
9 | "github.com/vmihailenco/msgpack/codes" | |
10 | ) | |
11 | ||
12 | var timeExtId int8 = -1 | |
13 | ||
14 | func init() { | |
15 | timeType := reflect.TypeOf((*time.Time)(nil)).Elem() | |
16 | registerExt(timeExtId, timeType, encodeTimeValue, decodeTimeValue) | |
17 | } | |
18 | ||
19 | func (e *Encoder) EncodeTime(tm time.Time) error { | |
20 | b := e.encodeTime(tm) | |
21 | if err := e.encodeExtLen(len(b)); err != nil { | |
22 | return err | |
23 | } | |
24 | if err := e.w.WriteByte(byte(timeExtId)); err != nil { | |
25 | return err | |
26 | } | |
27 | return e.write(b) | |
28 | } | |
29 | ||
30 | func (e *Encoder) encodeTime(tm time.Time) []byte { | |
31 | secs := uint64(tm.Unix()) | |
32 | if secs>>34 == 0 { | |
33 | data := uint64(tm.Nanosecond())<<34 | secs | |
34 | if data&0xffffffff00000000 == 0 { | |
35 | b := make([]byte, 4) | |
36 | binary.BigEndian.PutUint32(b, uint32(data)) | |
37 | return b | |
38 | } else { | |
39 | b := make([]byte, 8) | |
40 | binary.BigEndian.PutUint64(b, data) | |
41 | return b | |
42 | } | |
43 | } | |
44 | ||
45 | b := make([]byte, 12) | |
46 | binary.BigEndian.PutUint32(b, uint32(tm.Nanosecond())) | |
47 | binary.BigEndian.PutUint64(b[4:], uint64(secs)) | |
48 | return b | |
49 | } | |
50 | ||
51 | func (d *Decoder) DecodeTime() (time.Time, error) { | |
52 | tm, err := d.decodeTime() | |
53 | if err != nil { | |
54 | return tm, err | |
55 | } | |
56 | ||
57 | if tm.IsZero() { | |
58 | // Assume that zero time does not have timezone information. | |
59 | return tm.UTC(), nil | |
60 | } | |
61 | return tm, nil | |
62 | } | |
63 | ||
64 | func (d *Decoder) decodeTime() (time.Time, error) { | |
65 | extLen := d.extLen | |
66 | d.extLen = 0 | |
67 | if extLen == 0 { | |
68 | c, err := d.readCode() | |
69 | if err != nil { | |
70 | return time.Time{}, err | |
71 | } | |
72 | ||
73 | // Legacy format. | |
74 | if c == codes.FixedArrayLow|2 { | |
75 | sec, err := d.DecodeInt64() | |
76 | if err != nil { | |
77 | return time.Time{}, err | |
78 | } | |
79 | ||
80 | nsec, err := d.DecodeInt64() | |
81 | if err != nil { | |
82 | return time.Time{}, err | |
83 | } | |
84 | ||
85 | return time.Unix(sec, nsec), nil | |
86 | } | |
87 | ||
88 | if codes.IsString(c) { | |
89 | s, err := d.string(c) | |
90 | if err != nil { | |
91 | return time.Time{}, err | |
92 | } | |
93 | return time.Parse(time.RFC3339Nano, s) | |
94 | } | |
95 | ||
96 | extLen, err = d.parseExtLen(c) | |
97 | if err != nil { | |
98 | return time.Time{}, err | |
99 | } | |
100 | ||
101 | // Skip ext id. | |
102 | _, err = d.s.ReadByte() | |
103 | if err != nil { | |
104 | return time.Time{}, nil | |
105 | } | |
106 | } | |
107 | ||
108 | b, err := d.readN(extLen) | |
109 | if err != nil { | |
110 | return time.Time{}, err | |
111 | } | |
112 | ||
113 | switch len(b) { | |
114 | case 4: | |
115 | sec := binary.BigEndian.Uint32(b) | |
116 | return time.Unix(int64(sec), 0), nil | |
117 | case 8: | |
118 | sec := binary.BigEndian.Uint64(b) | |
119 | nsec := int64(sec >> 34) | |
120 | sec &= 0x00000003ffffffff | |
121 | return time.Unix(int64(sec), nsec), nil | |
122 | case 12: | |
123 | nsec := binary.BigEndian.Uint32(b) | |
124 | sec := binary.BigEndian.Uint64(b[4:]) | |
125 | return time.Unix(int64(sec), int64(nsec)), nil | |
126 | default: | |
127 | err = fmt.Errorf("msgpack: invalid ext len=%d decoding time", extLen) | |
128 | return time.Time{}, err | |
129 | } | |
130 | } | |
131 | ||
132 | func encodeTimeValue(e *Encoder, v reflect.Value) error { | |
133 | tm := v.Interface().(time.Time) | |
134 | b := e.encodeTime(tm) | |
135 | return e.write(b) | |
136 | } | |
137 | ||
138 | func decodeTimeValue(d *Decoder, v reflect.Value) error { | |
139 | tm, err := d.DecodeTime() | |
140 | if err != nil { | |
141 | return err | |
142 | } | |
143 | v.Set(reflect.ValueOf(tm)) | |
144 | return nil | |
145 | } |