@@ -12,6 +12,7 @@ package datetime
12
12
import (
13
13
"encoding/binary"
14
14
"fmt"
15
+ "math"
15
16
"time"
16
17
17
18
"gopkg.in/vmihailenco/msgpack.v2"
@@ -81,6 +82,16 @@ type Datetime struct {
81
82
time time.Time
82
83
}
83
84
85
+ const (
86
+ noTimezoneZone = ""
87
+ noTimezoneOffset = 0
88
+ )
89
+
90
+ // NoTimezone allows to create a datetime without UTC timezone for
91
+ // Tarantool. The problem is that Golang by default creates a time value with
92
+ // UTC timezone. So it is a way to create a datetime without timezone.
93
+ var NoTimezone = time .FixedZone (noTimezoneZone , noTimezoneOffset )
94
+
84
95
// NewDatetime returns a pointer to a new datetime.Datetime that contains a
85
96
// specified time.Time. It may returns an error if the Time value is out of
86
97
// supported range: [-5879610-06-22T00:00Z .. 5879611-07-11T00:00Z]
@@ -91,6 +102,18 @@ func NewDatetime(t time.Time) (*Datetime, error) {
91
102
return nil , fmt .Errorf ("Time %s is out of supported range." , t )
92
103
}
93
104
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
+
94
117
dt := new (Datetime )
95
118
dt .time = t
96
119
return dt , nil
@@ -110,8 +133,12 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
110
133
var dt datetime
111
134
dt .seconds = tm .Unix ()
112
135
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 )
115
142
116
143
var bytesSize = secondsSize
117
144
if dt .nsec != 0 || dt .tzOffset != 0 || dt .tzIndex != 0 {
@@ -132,7 +159,7 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
132
159
func (tm * Datetime ) UnmarshalMsgpack (b []byte ) error {
133
160
l := len (b )
134
161
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 )
136
163
}
137
164
138
165
var dt datetime
@@ -145,7 +172,23 @@ func (tm *Datetime) UnmarshalMsgpack(b []byte) error {
145
172
dt .tzIndex = int16 (binary .LittleEndian .Uint16 (b [secondsSize + nsecSize + tzOffsetSize :]))
146
173
}
147
174
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
+
149
192
dtp , err := NewDatetime (tt )
150
193
if dtp != nil {
151
194
* tm = * dtp
0 commit comments