@@ -12,6 +12,7 @@ package datetime
1212import (
1313 "encoding/binary"
1414 "fmt"
15+ "math"
1516 "time"
1617
1718 "gopkg.in/vmihailenco/msgpack.v2"
@@ -49,13 +50,11 @@ type datetime struct {
4950 // Nanoseconds, fractional part of seconds. Tarantool uses int32_t, see
5051 // a definition in src/lib/core/datetime.h.
5152 nsec int32
52- // Timezone offset in minutes from UTC (not implemented in Tarantool,
53- // see gh-163). Tarantool uses a int16_t type, see a structure
54- // definition in src/lib/core/datetime.h.
53+ // Timezone offset in minutes from UTC. Tarantool uses a int16_t type,
54+ // see a structure definition in src/lib/core/datetime.h.
5555 tzOffset int16
56- // Olson timezone id (not implemented in Tarantool, see gh-163).
57- // Tarantool uses a int16_t type, see a structure definition in
58- // src/lib/core/datetime.h.
56+ // Olson timezone id. Tarantool uses a int16_t type, see a structure
57+ // definition in src/lib/core/datetime.h.
5958 tzIndex int16
6059}
6160
@@ -81,16 +80,40 @@ type Datetime struct {
8180 time time.Time
8281}
8382
83+ const (
84+ noTimezoneZone = ""
85+ noTimezoneOffset = 0
86+ )
87+
88+ // NoTimezone allows to create a datetime without UTC timezone for
89+ // Tarantool. The problem is that Golang by default creates a time value with
90+ // UTC timezone. So it is a way to create a datetime without timezone.
91+ var NoTimezone = time .FixedZone (noTimezoneZone , noTimezoneOffset )
92+
8493// NewDatetime returns a pointer to a new datetime.Datetime that contains a
85- // specified time.Time. It may returns an error if the Time value is out of
86- // supported range: [-5879610-06-22T00:00Z .. 5879611-07-11T00:00Z]
94+ // specified time.Time. It may return an error if the Time value is out of
95+ // supported range: [-5879610-06-22T00:00Z .. 5879611-07-11T00:00Z] or
96+ // an invalid timezone or offset value is out of supported range:
97+ // [math.MinInt16, math.MaxInt16]
8798func NewDatetime (t time.Time ) (* Datetime , error ) {
8899 seconds := t .Unix ()
89100
90101 if seconds < minSeconds || seconds > maxSeconds {
91102 return nil , fmt .Errorf ("Time %s is out of supported range." , t )
92103 }
93104
105+ zone , offset := t .Zone ()
106+ if zone != noTimezoneZone {
107+ if _ , ok := timezoneToIndex [zone ]; ! ok {
108+ return nil , fmt .Errorf ("Unknown timezone %s with offset %d" ,
109+ zone , offset )
110+ }
111+ }
112+ offset /= 60
113+ if offset < math .MinInt16 || offset > math .MaxInt16 {
114+ return nil , fmt .Errorf ("Offset must be between %d and %d" , math .MinInt16 , math .MaxInt16 )
115+ }
116+
94117 dt := new (Datetime )
95118 dt .time = t
96119 return dt , nil
@@ -110,8 +133,12 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
110133 var dt datetime
111134 dt .seconds = tm .Unix ()
112135 dt .nsec = int32 (tm .Nanosecond ())
113- dt .tzIndex = 0 // It is not implemented, see gh-163.
114- dt .tzOffset = 0 // It is not implemented, see gh-163.
136+
137+ zone , offset := tm .Zone ()
138+ if zone != noTimezoneZone {
139+ dt .tzIndex = int16 (timezoneToIndex [zone ])
140+ }
141+ dt .tzOffset = int16 (offset / 60 )
115142
116143 var bytesSize = secondsSize
117144 if dt .nsec != 0 || dt .tzOffset != 0 || dt .tzIndex != 0 {
@@ -132,7 +159,7 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
132159func (tm * Datetime ) UnmarshalMsgpack (b []byte ) error {
133160 l := len (b )
134161 if l != maxSize && l != secondsSize {
135- return fmt .Errorf ("invalid data length: got %d, wanted %d or %d" , len (b ), secondsSize , maxSize )
162+ return fmt .Errorf ("Invalid data length: got %d, wanted %d or %d" , len (b ), secondsSize , maxSize )
136163 }
137164
138165 var dt datetime
@@ -145,7 +172,23 @@ func (tm *Datetime) UnmarshalMsgpack(b []byte) error {
145172 dt .tzIndex = int16 (binary .LittleEndian .Uint16 (b [secondsSize + nsecSize + tzOffsetSize :]))
146173 }
147174
148- tt := time .Unix (dt .seconds , int64 (dt .nsec )).UTC ()
175+ tt := time .Unix (dt .seconds , int64 (dt .nsec ))
176+
177+ loc := NoTimezone
178+ if dt .tzIndex != 0 || dt .tzOffset != 0 {
179+ zone := noTimezoneZone
180+ offset := int (dt .tzOffset ) * 60
181+
182+ if dt .tzIndex != 0 {
183+ if _ , ok := indexToTimezone [int (dt .tzIndex )]; ! ok {
184+ return fmt .Errorf ("Unknown timezone index %d" , dt .tzIndex )
185+ }
186+ zone = indexToTimezone [int (dt .tzIndex )]
187+ }
188+ loc = time .FixedZone (zone , offset )
189+ }
190+ tt = tt .In (loc )
191+
149192 dtp , err := NewDatetime (tt )
150193 if dtp != nil {
151194 * tm = * dtp
0 commit comments