@@ -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"
@@ -49,13 +50,11 @@ type datetime struct {
49
50
// Nanoseconds, fractional part of seconds. Tarantool uses int32_t, see
50
51
// a definition in src/lib/core/datetime.h.
51
52
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.
55
55
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.
59
58
tzIndex int16
60
59
}
61
60
@@ -81,6 +80,16 @@ type Datetime struct {
81
80
time time.Time
82
81
}
83
82
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
+
84
93
// NewDatetime returns a pointer to a new datetime.Datetime that contains a
85
94
// specified time.Time. It may returns an error if the Time value is out of
86
95
// supported range: [-5879610-06-22T00:00Z .. 5879611-07-11T00:00Z]
@@ -91,6 +100,18 @@ func NewDatetime(t time.Time) (*Datetime, error) {
91
100
return nil , fmt .Errorf ("Time %s is out of supported range." , t )
92
101
}
93
102
103
+ zone , offset := t .Zone ()
104
+ if zone != noTimezoneZone {
105
+ if _ , ok := timezoneToIndex [zone ]; ! ok {
106
+ return nil , fmt .Errorf ("Unknown timezone %s with offset %d" ,
107
+ zone , offset )
108
+ }
109
+ }
110
+ offset /= 60
111
+ if offset < math .MinInt16 || offset > math .MaxInt16 {
112
+ return nil , fmt .Errorf ("Offset must be between %d and %d" , math .MinInt16 , math .MaxInt16 )
113
+ }
114
+
94
115
dt := new (Datetime )
95
116
dt .time = t
96
117
return dt , nil
@@ -110,8 +131,12 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
110
131
var dt datetime
111
132
dt .seconds = tm .Unix ()
112
133
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.
134
+
135
+ zone , offset := tm .Zone ()
136
+ if zone != noTimezoneZone {
137
+ dt .tzIndex = int16 (timezoneToIndex [zone ])
138
+ }
139
+ dt .tzOffset = int16 (offset / 60 )
115
140
116
141
var bytesSize = secondsSize
117
142
if dt .nsec != 0 || dt .tzOffset != 0 || dt .tzIndex != 0 {
@@ -132,7 +157,7 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
132
157
func (tm * Datetime ) UnmarshalMsgpack (b []byte ) error {
133
158
l := len (b )
134
159
if l != maxSize && l != secondsSize {
135
- return fmt .Errorf ("invalid data length: got %d, wanted %d or %d" , len (b ), secondsSize , maxSize )
160
+ return fmt .Errorf ("Invalid data length: got %d, wanted %d or %d" , len (b ), secondsSize , maxSize )
136
161
}
137
162
138
163
var dt datetime
@@ -145,7 +170,23 @@ func (tm *Datetime) UnmarshalMsgpack(b []byte) error {
145
170
dt .tzIndex = int16 (binary .LittleEndian .Uint16 (b [secondsSize + nsecSize + tzOffsetSize :]))
146
171
}
147
172
148
- tt := time .Unix (dt .seconds , int64 (dt .nsec )).UTC ()
173
+ tt := time .Unix (dt .seconds , int64 (dt .nsec ))
174
+
175
+ loc := NoTimezone
176
+ if dt .tzIndex != 0 || dt .tzOffset != 0 {
177
+ zone := noTimezoneZone
178
+ offset := int (dt .tzOffset ) * 60
179
+
180
+ if dt .tzIndex != 0 {
181
+ if _ , ok := indexToTimezone [int (dt .tzIndex )]; ! ok {
182
+ return fmt .Errorf ("Unknown timezone index %d" , dt .tzIndex )
183
+ }
184
+ zone = indexToTimezone [int (dt .tzIndex )]
185
+ }
186
+ loc = time .FixedZone (zone , offset )
187
+ }
188
+ tt = tt .In (loc )
189
+
149
190
dtp , err := NewDatetime (tt )
150
191
if dtp != nil {
151
192
* tm = * dtp
0 commit comments