@@ -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
@@ -47,13 +48,11 @@ type datetime struct {
47
48
// Nanoseconds, fractional part of seconds. Tarantool uses int32_t, see
48
49
// a definition in src/lib/core/datetime.h.
49
50
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.
51
+ // Timezone offset in minutes from UTC. Tarantool uses a int16_t type,
52
+ // see a structure definition in src/lib/core/datetime.h.
53
53
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.
54
+ // Olson timezone id. Tarantool uses a int16_t type, see a structure
55
+ // definition in src/lib/core/datetime.h.
57
56
tzIndex int16
58
57
}
59
58
@@ -79,16 +78,39 @@ type Datetime struct {
79
78
time time.Time
80
79
}
81
80
81
+ const (
82
+ // NoTimezone allows to create a datetime without UTC timezone for
83
+ // Tarantool. The problem is that Golang by default creates a time value
84
+ // with UTC timezone. So it is a way to create a datetime without timezone.
85
+ NoTimezone = ""
86
+ )
87
+
88
+ var noTimezoneLoc = time .FixedZone (NoTimezone , 0 )
89
+
82
90
// 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]
91
+ // specified time.Time. It may return an error if the Time value is out of
92
+ // supported range: [-5879610-06-22T00:00Z .. 5879611-07-11T00:00Z] or
93
+ // an invalid timezone or offset value is out of supported range:
94
+ // [math.MinInt16, math.MaxInt16]
85
95
func NewDatetime (t time.Time ) (* Datetime , error ) {
86
96
seconds := t .Unix ()
87
97
88
98
if seconds < minSeconds || seconds > maxSeconds {
89
99
return nil , fmt .Errorf ("Time %s is out of supported range." , t )
90
100
}
91
101
102
+ zone , offset := t .Zone ()
103
+ if zone != NoTimezone {
104
+ if _ , ok := timezoneToIndex [zone ]; ! ok {
105
+ return nil , fmt .Errorf ("Unknown timezone %s with offset %d" ,
106
+ zone , offset )
107
+ }
108
+ }
109
+ offset /= 60
110
+ if offset < math .MinInt16 || offset > math .MaxInt16 {
111
+ return nil , fmt .Errorf ("Offset must be between %d and %d" , math .MinInt16 , math .MaxInt16 )
112
+ }
113
+
92
114
dt := new (Datetime )
93
115
dt .time = t
94
116
return dt , nil
@@ -105,8 +127,14 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
105
127
var dt datetime
106
128
dt .seconds = tm .Unix ()
107
129
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.
130
+
131
+ zone , offset := tm .Zone ()
132
+ if zone != NoTimezone {
133
+ // The zone value already checked in NewDatetime() or
134
+ // UnmarshalMsgpack() calls.
135
+ dt .tzIndex = int16 (timezoneToIndex [zone ])
136
+ }
137
+ dt .tzOffset = int16 (offset / 60 )
110
138
111
139
var bytesSize = secondsSize
112
140
if dt .nsec != 0 || dt .tzOffset != 0 || dt .tzIndex != 0 {
@@ -127,7 +155,7 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
127
155
func (tm * Datetime ) UnmarshalMsgpack (b []byte ) error {
128
156
l := len (b )
129
157
if l != maxSize && l != secondsSize {
130
- return fmt .Errorf ("invalid data length: got %d, wanted %d or %d" , len (b ), secondsSize , maxSize )
158
+ return fmt .Errorf ("Invalid data length: got %d, wanted %d or %d" , len (b ), secondsSize , maxSize )
131
159
}
132
160
133
161
var dt datetime
@@ -140,7 +168,23 @@ func (tm *Datetime) UnmarshalMsgpack(b []byte) error {
140
168
dt .tzIndex = int16 (binary .LittleEndian .Uint16 (b [secondsSize + nsecSize + tzOffsetSize :]))
141
169
}
142
170
143
- tt := time .Unix (dt .seconds , int64 (dt .nsec )).UTC ()
171
+ tt := time .Unix (dt .seconds , int64 (dt .nsec ))
172
+
173
+ loc := noTimezoneLoc
174
+ if dt .tzIndex != 0 || dt .tzOffset != 0 {
175
+ zone := NoTimezone
176
+ offset := int (dt .tzOffset ) * 60
177
+
178
+ if dt .tzIndex != 0 {
179
+ if _ , ok := indexToTimezone [int (dt .tzIndex )]; ! ok {
180
+ return fmt .Errorf ("Unknown timezone index %d" , dt .tzIndex )
181
+ }
182
+ zone = indexToTimezone [int (dt .tzIndex )]
183
+ }
184
+ loc = time .FixedZone (zone , offset )
185
+ }
186
+ tt = tt .In (loc )
187
+
144
188
dtp , err := NewDatetime (tt )
145
189
if dtp != nil {
146
190
* tm = * dtp
0 commit comments