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