@@ -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,40 @@ type Datetime struct {
79
78
time time.Time
80
79
}
81
80
81
+ const (
82
+ noTimezoneZone = ""
83
+ noTimezoneOffset = 0
84
+ )
85
+
86
+ // NoTimezone allows to create a datetime without UTC timezone for
87
+ // Tarantool. The problem is that Golang by default creates a time value with
88
+ // UTC timezone. So it is a way to create a datetime without timezone.
89
+ var NoTimezone = time .FixedZone (noTimezoneZone , noTimezoneOffset )
90
+
82
91
// 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]
92
+ // specified time.Time. It may return an error if the Time value is out of
93
+ // supported range: [-5879610-06-22T00:00Z .. 5879611-07-11T00:00Z] or
94
+ // an invalid timezone or offset value is out of supported range:
95
+ // [math.MinInt16, math.MaxInt16]
85
96
func NewDatetime (t time.Time ) (* Datetime , error ) {
86
97
seconds := t .Unix ()
87
98
88
99
if seconds < minSeconds || seconds > maxSeconds {
89
100
return nil , fmt .Errorf ("Time %s is out of supported range." , t )
90
101
}
91
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
+
92
115
dt := new (Datetime )
93
116
dt .time = t
94
117
return dt , nil
@@ -105,8 +128,12 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
105
128
var dt datetime
106
129
dt .seconds = tm .Unix ()
107
130
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.
131
+
132
+ zone , offset := tm .Zone ()
133
+ if zone != noTimezoneZone {
134
+ dt .tzIndex = int16 (timezoneToIndex [zone ])
135
+ }
136
+ dt .tzOffset = int16 (offset / 60 )
110
137
111
138
var bytesSize = secondsSize
112
139
if dt .nsec != 0 || dt .tzOffset != 0 || dt .tzIndex != 0 {
@@ -127,7 +154,7 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
127
154
func (tm * Datetime ) UnmarshalMsgpack (b []byte ) error {
128
155
l := len (b )
129
156
if l != maxSize && l != secondsSize {
130
- return fmt .Errorf ("invalid data length: got %d, wanted %d or %d" , len (b ), secondsSize , maxSize )
157
+ return fmt .Errorf ("Invalid data length: got %d, wanted %d or %d" , len (b ), secondsSize , maxSize )
131
158
}
132
159
133
160
var dt datetime
@@ -140,7 +167,23 @@ func (tm *Datetime) UnmarshalMsgpack(b []byte) error {
140
167
dt .tzIndex = int16 (binary .LittleEndian .Uint16 (b [secondsSize + nsecSize + tzOffsetSize :]))
141
168
}
142
169
143
- tt := time .Unix (dt .seconds , int64 (dt .nsec )).UTC ()
170
+ tt := time .Unix (dt .seconds , int64 (dt .nsec ))
171
+
172
+ loc := NoTimezone
173
+ if dt .tzIndex != 0 || dt .tzOffset != 0 {
174
+ zone := noTimezoneZone
175
+ offset := int (dt .tzOffset ) * 60
176
+
177
+ if dt .tzIndex != 0 {
178
+ if _ , ok := indexToTimezone [int (dt .tzIndex )]; ! ok {
179
+ return fmt .Errorf ("Unknown timezone index %d" , dt .tzIndex )
180
+ }
181
+ zone = indexToTimezone [int (dt .tzIndex )]
182
+ }
183
+ loc = time .FixedZone (zone , offset )
184
+ }
185
+ tt = tt .In (loc )
186
+
144
187
dtp , err := NewDatetime (tt )
145
188
if dtp != nil {
146
189
* tm = * dtp
0 commit comments