@@ -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 (dtime * Datetime ) add (ival Interval , positive bool ) (* Datetime , error ) {
186
+ func (dtime Datetime ) add (ival Interval , positive bool ) (Datetime , error ) {
185
187
newVal := intervalFromDatetime (dtime )
186
188
187
189
var direction int64
@@ -191,7 +193,7 @@ func (dtime *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 (dtime *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 ), dtime .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 (dtime * Datetime ) Add (ival Interval ) (* Datetime , error ) {
213
+ func (dtime Datetime ) Add (ival Interval ) (Datetime , error ) {
212
214
return dtime .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 (dtime * Datetime ) Sub (ival Interval ) (* Datetime , error ) {
219
+ func (dtime Datetime ) Sub (ival Interval ) (Datetime , error ) {
218
220
return dtime .add (ival , false )
219
221
}
220
222
221
223
// Interval returns an Interval value to a next Datetime value.
222
- func (dtime * Datetime ) Interval (next * Datetime ) Interval {
224
+ func (dtime Datetime ) Interval (next Datetime ) Interval {
223
225
curIval := intervalFromDatetime (dtime )
224
226
nextIval := intervalFromDatetime (next )
225
227
_ , curOffset := dtime .time .Zone ()
@@ -236,11 +238,12 @@ func (dtime *Datetime) Interval(next *Datetime) Interval {
236
238
// If a Datetime created via unmarshaling Tarantool's datetime then we try to
237
239
// create a location with time.LoadLocation() first. In case of failure, we use
238
240
// a location created with time.FixedZone().
239
- func (dtime * Datetime ) ToTime () time.Time {
241
+ func (dtime Datetime ) ToTime () time.Time {
240
242
return dtime .time
241
243
}
242
244
243
- func (dtime * Datetime ) MarshalMsgpack () ([]byte , error ) {
245
+ func datetimeEncoder (e * msgpack.Encoder , v reflect.Value ) ([]byte , error ) {
246
+ dtime := v .Interface ().(Datetime )
244
247
tm := dtime .ToTime ()
245
248
246
249
var dt datetime
@@ -272,17 +275,25 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
272
275
return buf , nil
273
276
}
274
277
275
- func (tm * Datetime ) UnmarshalMsgpack (b []byte ) error {
276
- l := len (b )
277
- if l != maxSize && l != secondsSize {
278
- return fmt .Errorf ("invalid data length: got %d, wanted %d or %d" , len (b ), secondsSize , maxSize )
278
+ func datetimeDecoder (d * msgpack.Decoder , v reflect.Value , extLen int ) error {
279
+ if extLen != maxSize && extLen != secondsSize {
280
+ return fmt .Errorf ("invalid data length: got %d, wanted %d or %d" , extLen , secondsSize , maxSize )
281
+ }
282
+
283
+ b := make ([]byte , extLen )
284
+ n , err := d .Buffered ().Read (b )
285
+ if err != nil {
286
+ return err
287
+ }
288
+ if n < extLen {
289
+ return fmt .Errorf ("msgpack: unexpected end of stream after %d datetime bytes" , n )
279
290
}
280
291
281
292
var dt datetime
282
293
sec := binary .LittleEndian .Uint64 (b )
283
294
dt .seconds = int64 (sec )
284
295
dt .nsec = 0
285
- if l == maxSize {
296
+ if extLen == maxSize {
286
297
dt .nsec = int32 (binary .LittleEndian .Uint32 (b [secondsSize :]))
287
298
dt .tzOffset = int16 (binary .LittleEndian .Uint16 (b [secondsSize + nsecSize :]))
288
299
dt .tzIndex = int16 (binary .LittleEndian .Uint16 (b [secondsSize + nsecSize + tzOffsetSize :]))
@@ -315,13 +326,12 @@ func (tm *Datetime) UnmarshalMsgpack(b []byte) error {
315
326
}
316
327
tt = tt .In (loc )
317
328
318
- dtp , err := NewDatetime (tt )
319
- if dtp != nil {
320
- * tm = * dtp
321
- }
329
+ ptr := v .Addr ().Interface ().(* Datetime )
330
+ * ptr , err = MakeDatetime (tt )
322
331
return err
323
332
}
324
333
325
334
func init () {
326
- msgpack .RegisterExt (datetime_extId , (* Datetime )(nil ))
335
+ msgpack .RegisterExtDecoder (datetimeExtID , Datetime {}, datetimeDecoder )
336
+ msgpack .RegisterExtEncoder (datetimeExtID , Datetime {}, datetimeEncoder )
327
337
}
0 commit comments