@@ -12,6 +12,7 @@ package datetime
12
12
import (
13
13
"encoding/binary"
14
14
"fmt"
15
+ "reflect"
15
16
"time"
16
17
17
18
"github.com/vmihailenco/msgpack/v5"
@@ -35,7 +36,7 @@ import (
35
36
36
37
// Datetime external type. Supported since Tarantool 2.10. See more details in
37
38
// issue https://github.com/tarantool/tarantool/issues/5946.
38
- const datetime_extId = 4
39
+ const datetimeExtID = 4
39
40
40
41
// datetime structure keeps a number of seconds and nanoseconds since Unix Epoch.
41
42
// Time is normalized by UTC, so time-zone offset is informative only.
@@ -93,41 +94,41 @@ const (
93
94
offsetMax = 14 * 60 * 60
94
95
)
95
96
96
- // NewDatetime returns a pointer to a new datetime.Datetime that contains a
97
+ // MakeDatetime returns a datetime.Datetime object that contains a
97
98
// specified time.Time. It may return an error if the Time value is out of
98
99
// supported range: [-5879610-06-22T00:00Z .. 5879611-07-11T00:00Z] or
99
100
// an invalid timezone or offset value is out of supported range:
100
101
// [-12 * 60 * 60, 14 * 60 * 60].
101
102
//
102
103
// NOTE: Tarantool's datetime.tz value is picked from t.Location().String().
103
104
// "Local" location is unsupported, see ExampleNewDatetime_localUnsupported.
104
- func NewDatetime (t time.Time ) (* Datetime , error ) {
105
+ func MakeDatetime (t time.Time ) (Datetime , error ) {
106
+ dt := Datetime {}
105
107
seconds := t .Unix ()
106
108
107
109
if seconds < minSeconds || seconds > maxSeconds {
108
- return nil , fmt .Errorf ("time %s is out of supported range" , t )
110
+ return dt , fmt .Errorf ("time %s is out of supported range" , t )
109
111
}
110
112
111
113
zone := t .Location ().String ()
112
114
_ , offset := t .Zone ()
113
115
if zone != NoTimezone {
114
116
if _ , ok := timezoneToIndex [zone ]; ! ok {
115
- return nil , fmt .Errorf ("unknown timezone %s with offset %d" ,
117
+ return dt , fmt .Errorf ("unknown timezone %s with offset %d" ,
116
118
zone , offset )
117
119
}
118
120
}
119
121
120
122
if offset < offsetMin || offset > offsetMax {
121
- return nil , fmt .Errorf ("offset must be between %d and %d hours" ,
123
+ return dt , fmt .Errorf ("offset must be between %d and %d hours" ,
122
124
offsetMin , offsetMax )
123
125
}
124
126
125
- dt := new (Datetime )
126
127
dt .time = t
127
128
return dt , nil
128
129
}
129
130
130
- func intervalFromDatetime (dtime * Datetime ) (ival Interval ) {
131
+ func intervalFromDatetime (dtime Datetime ) (ival Interval ) {
131
132
ival .Year = int64 (dtime .time .Year ())
132
133
ival .Month = int64 (dtime .time .Month ())
133
134
ival .Day = int64 (dtime .time .Day ())
@@ -158,7 +159,7 @@ func daysInMonth(year int64, month int64) int64 {
158
159
159
160
// C implementation:
160
161
// https://github.com/tarantool/c-dt/blob/cec6acebb54d9e73ea0b99c63898732abd7683a6/dt_arithmetic.c#L74-L98
161
- func addMonth (ival * Interval , delta int64 , adjust Adjust ) {
162
+ func addMonth (ival Interval , delta int64 , adjust Adjust ) Interval {
162
163
oldYear := ival .Year
163
164
oldMonth := ival .Month
164
165
@@ -172,16 +173,17 @@ func addMonth(ival *Interval, delta int64, adjust Adjust) {
172
173
}
173
174
}
174
175
if adjust == ExcessAdjust || ival .Day < 28 {
175
- return
176
+ return ival
176
177
}
177
178
178
179
dim := daysInMonth (ival .Year , ival .Month )
179
180
if ival .Day > dim || (adjust == LastAdjust && ival .Day == daysInMonth (oldYear , oldMonth )) {
180
181
ival .Day = dim
181
182
}
183
+ return ival
182
184
}
183
185
184
- func (d * Datetime ) add (ival Interval , positive bool ) (* Datetime , error ) {
186
+ func (d Datetime ) add (ival Interval , positive bool ) (Datetime , error ) {
185
187
newVal := intervalFromDatetime (d )
186
188
187
189
var direction int64
@@ -191,7 +193,7 @@ func (d *Datetime) add(ival Interval, positive bool) (*Datetime, error) {
191
193
direction = - 1
192
194
}
193
195
194
- addMonth (& newVal , direction * ival .Year * 12 + direction * ival .Month , ival .Adjust )
196
+ newVal = addMonth (newVal , direction * ival .Year * 12 + direction * ival .Month , ival .Adjust )
195
197
newVal .Day += direction * 7 * ival .Week
196
198
newVal .Day += direction * ival .Day
197
199
newVal .Hour += direction * ival .Hour
@@ -203,23 +205,23 @@ func (d *Datetime) add(ival Interval, positive bool) (*Datetime, error) {
203
205
int (newVal .Day ), int (newVal .Hour ), int (newVal .Min ),
204
206
int (newVal .Sec ), int (newVal .Nsec ), d .time .Location ())
205
207
206
- return NewDatetime (tm )
208
+ return MakeDatetime (tm )
207
209
}
208
210
209
211
// Add creates a new Datetime as addition of the Datetime and Interval. It may
210
212
// return an error if a new Datetime is out of supported range.
211
- func (d * Datetime ) Add (ival Interval ) (* Datetime , error ) {
213
+ func (d Datetime ) Add (ival Interval ) (Datetime , error ) {
212
214
return d .add (ival , true )
213
215
}
214
216
215
217
// Sub creates a new Datetime as subtraction of the Datetime and Interval. It
216
218
// may return an error if a new Datetime is out of supported range.
217
- func (d * Datetime ) Sub (ival Interval ) (* Datetime , error ) {
219
+ func (d Datetime ) Sub (ival Interval ) (Datetime , error ) {
218
220
return d .add (ival , false )
219
221
}
220
222
221
223
// Interval returns an Interval value to a next Datetime value.
222
- func (d * Datetime ) Interval (next * Datetime ) Interval {
224
+ func (d Datetime ) Interval (next Datetime ) Interval {
223
225
curIval := intervalFromDatetime (d )
224
226
nextIval := intervalFromDatetime (next )
225
227
_ , curOffset := d .time .Zone ()
@@ -240,8 +242,9 @@ func (d *Datetime) ToTime() time.Time {
240
242
return d .time
241
243
}
242
244
243
- func (d * Datetime ) MarshalMsgpack () ([]byte , error ) {
244
- tm := d .ToTime ()
245
+ func datetimeEncoder (e * msgpack.Encoder , v reflect.Value ) ([]byte , error ) {
246
+ dtime := v .Interface ().(Datetime )
247
+ tm := dtime .ToTime ()
245
248
246
249
var dt datetime
247
250
dt .seconds = tm .Unix ()
@@ -272,18 +275,26 @@ func (d *Datetime) MarshalMsgpack() ([]byte, error) {
272
275
return buf , nil
273
276
}
274
277
275
- func (d * Datetime ) UnmarshalMsgpack (b []byte ) error {
276
- l := len (b )
277
- if l != maxSize && l != secondsSize {
278
+ func datetimeDecoder (d * msgpack.Decoder , v reflect.Value , extLen int ) error {
279
+ if extLen != maxSize && extLen != secondsSize {
278
280
return fmt .Errorf ("invalid data length: got %d, wanted %d or %d" ,
279
- len (b ), secondsSize , maxSize )
281
+ extLen , secondsSize , maxSize )
282
+ }
283
+
284
+ b := make ([]byte , extLen )
285
+ n , err := d .Buffered ().Read (b )
286
+ if err != nil {
287
+ return err
288
+ }
289
+ if n < extLen {
290
+ return fmt .Errorf ("msgpack: unexpected end of stream after %d datetime bytes" , n )
280
291
}
281
292
282
293
var dt datetime
283
294
sec := binary .LittleEndian .Uint64 (b )
284
295
dt .seconds = int64 (sec )
285
296
dt .nsec = 0
286
- if l == maxSize {
297
+ if extLen == maxSize {
287
298
dt .nsec = int32 (binary .LittleEndian .Uint32 (b [secondsSize :]))
288
299
dt .tzOffset = int16 (binary .LittleEndian .Uint16 (b [secondsSize + nsecSize :]))
289
300
dt .tzIndex = int16 (binary .LittleEndian .Uint16 (b [secondsSize + nsecSize + tzOffsetSize :]))
@@ -316,13 +327,12 @@ func (d *Datetime) UnmarshalMsgpack(b []byte) error {
316
327
}
317
328
tt = tt .In (loc )
318
329
319
- dtp , err := NewDatetime (tt )
320
- if dtp != nil {
321
- * d = * dtp
322
- }
330
+ ptr := v .Addr ().Interface ().(* Datetime )
331
+ * ptr , err = MakeDatetime (tt )
323
332
return err
324
333
}
325
334
326
335
func init () {
327
- msgpack .RegisterExt (datetime_extId , (* Datetime )(nil ))
336
+ msgpack .RegisterExtDecoder (datetimeExtID , Datetime {}, datetimeDecoder )
337
+ msgpack .RegisterExtEncoder (datetimeExtID , Datetime {}, datetimeEncoder )
328
338
}
0 commit comments