@@ -47,13 +47,11 @@ type datetime struct {
4747 // Nanoseconds, fractional part of seconds. Tarantool uses int32_t, see
4848 // a definition in src/lib/core/datetime.h.
4949 nsec int32
50- // Timezone offset in minutes from UTC (not implemented in Tarantool,
51- // see gh-163). Tarantool uses a int16_t type, see a structure
52- // definition in src/lib/core/datetime.h.
50+ // Timezone offset in minutes from UTC. Tarantool uses a int16_t type,
51+ // see a structure definition in src/lib/core/datetime.h.
5352 tzOffset int16
54- // Olson timezone id (not implemented in Tarantool, see gh-163).
55- // Tarantool uses a int16_t type, see a structure definition in
56- // src/lib/core/datetime.h.
53+ // Olson timezone id. Tarantool uses a int16_t type, see a structure
54+ // definition in src/lib/core/datetime.h.
5755 tzIndex int16
5856}
5957
@@ -79,16 +77,44 @@ type Datetime struct {
7977 time time.Time
8078}
8179
80+ const (
81+ // NoTimezone allows to create a datetime without UTC timezone for
82+ // Tarantool. The problem is that Golang by default creates a time value
83+ // with UTC timezone. So it is a way to create a datetime without timezone.
84+ NoTimezone = ""
85+ )
86+
87+ var noTimezoneLoc = time .FixedZone (NoTimezone , 0 )
88+
89+ const (
90+ offsetMin = - 12 * 60 * 60
91+ offsetMax = 14 * 60 * 60
92+ )
93+
8294// NewDatetime returns a pointer to a new datetime.Datetime that contains a
83- // specified time.Time. It may returns an error if the Time value is out of
84- // supported range: [-5879610-06-22T00:00Z .. 5879611-07-11T00:00Z]
95+ // specified time.Time. It may return an error if the Time value is out of
96+ // supported range: [-5879610-06-22T00:00Z .. 5879611-07-11T00:00Z] or
97+ // an invalid timezone or offset value is out of supported range:
98+ // [-12 * 60 * 60, 14 * 60 * 60].
8599func NewDatetime (t time.Time ) (* Datetime , error ) {
86100 seconds := t .Unix ()
87101
88102 if seconds < minSeconds || seconds > maxSeconds {
89103 return nil , fmt .Errorf ("time %s is out of supported range" , t )
90104 }
91105
106+ zone , offset := t .Zone ()
107+ if zone != NoTimezone {
108+ if _ , ok := timezoneToIndex [zone ]; ! ok {
109+ return nil , fmt .Errorf ("unknown timezone %s with offset %d" ,
110+ zone , offset )
111+ }
112+ }
113+ if offset < offsetMin || offset > offsetMax {
114+ return nil , fmt .Errorf ("offset must be between %d and %d hours" ,
115+ offsetMin , offsetMax )
116+ }
117+
92118 dt := new (Datetime )
93119 dt .time = t
94120 return dt , nil
@@ -105,8 +131,14 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
105131 var dt datetime
106132 dt .seconds = tm .Unix ()
107133 dt .nsec = int32 (tm .Nanosecond ())
108- dt .tzIndex = 0 // It is not implemented, see gh-163.
109- dt .tzOffset = 0 // It is not implemented, see gh-163.
134+
135+ zone , offset := tm .Zone ()
136+ if zone != NoTimezone {
137+ // The zone value already checked in NewDatetime() or
138+ // UnmarshalMsgpack() calls.
139+ dt .tzIndex = int16 (timezoneToIndex [zone ])
140+ }
141+ dt .tzOffset = int16 (offset / 60 )
110142
111143 var bytesSize = secondsSize
112144 if dt .nsec != 0 || dt .tzOffset != 0 || dt .tzIndex != 0 {
@@ -140,7 +172,23 @@ func (tm *Datetime) UnmarshalMsgpack(b []byte) error {
140172 dt .tzIndex = int16 (binary .LittleEndian .Uint16 (b [secondsSize + nsecSize + tzOffsetSize :]))
141173 }
142174
143- tt := time .Unix (dt .seconds , int64 (dt .nsec )).UTC ()
175+ tt := time .Unix (dt .seconds , int64 (dt .nsec ))
176+
177+ loc := noTimezoneLoc
178+ if dt .tzIndex != 0 || dt .tzOffset != 0 {
179+ zone := NoTimezone
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+
144192 dtp , err := NewDatetime (tt )
145193 if dtp != nil {
146194 * tm = * dtp
0 commit comments