@@ -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,44 @@ 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
+
92
118
dt := new (Datetime )
93
119
dt .time = t
94
120
return dt , nil
@@ -105,8 +131,14 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
105
131
var dt datetime
106
132
dt .seconds = tm .Unix ()
107
133
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.
134
+
135
+ zone , offset := tm .Zone ()
136
+ if zone != NoTimezone {
137
+ // The zone value already checked in NewDatetime() or
138
+ // UnmarshalMsgpack() calls.
139
+ dt .tzIndex = int16 (timezoneToIndex [zone ])
140
+ }
141
+ dt .tzOffset = int16 (offset / 60 )
110
142
111
143
var bytesSize = secondsSize
112
144
if dt .nsec != 0 || dt .tzOffset != 0 || dt .tzIndex != 0 {
@@ -127,7 +159,7 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
127
159
func (tm * Datetime ) UnmarshalMsgpack (b []byte ) error {
128
160
l := len (b )
129
161
if l != maxSize && l != secondsSize {
130
- 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 )
131
163
}
132
164
133
165
var dt datetime
@@ -140,7 +172,23 @@ func (tm *Datetime) UnmarshalMsgpack(b []byte) error {
140
172
dt .tzIndex = int16 (binary .LittleEndian .Uint16 (b [secondsSize + nsecSize + tzOffsetSize :]))
141
173
}
142
174
143
- tt := time .Unix (dt .seconds , int64 (dt .nsec )).UTC ()
175
+ tt := time .Unix (dt .seconds , int64 (dt .nsec ))
176
+
177
+ loc := noTimezoneLoc
178
+ if dt .tzIndex != 0 || dt .tzOffset != 0 {
179
+ zone := NoTimezone
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
+
144
192
dtp , err := NewDatetime (tt )
145
193
if dtp != nil {
146
194
* tm = * dtp
0 commit comments