@@ -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,16 +80,40 @@ 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
- // 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]
87
98
func NewDatetime (t time.Time ) (* Datetime , error ) {
88
99
seconds := t .Unix ()
89
100
90
101
if seconds < minSeconds || seconds > maxSeconds {
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