Skip to content

Commit fa0ec5b

Browse files
committed
datetime: always return time with fixed zone
Before the patch there was a double behavior: 1. If a user create Datetime from time.Time then Datetime.ToTime() return an original time value with an original timezone. 2. If an Tarantool returns Datetime then we use a timezone with a fixed offset. We need it to avoid changes of the timezone if a user create a new Datetime from a Datetime.ToTime() value. As a result, it was was possible to get different behaviors when a user use time.Time.AddDate() or time.Time.Add() for DST timezones. One possible solution is to always return a time with a fixed offset from Datetime.ToTime() and recommend to use Datetime.Add() or Datetime.Sub() for change time values.
1 parent 8b58928 commit fa0ec5b

File tree

3 files changed

+94
-2
lines changed

3 files changed

+94
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1616

1717
### Changed
1818

19+
- Datetime.ToTime() always returns a time with a fixed timezone (#217)
20+
1921
### Fixed
2022

2123
- Mode type description in the connection_pool subpackage (#208)

datetime/datetime.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ func NewDatetime(t time.Time) (*Datetime, error) {
109109
return nil, fmt.Errorf("unknown timezone %s with offset %d",
110110
zone, offset)
111111
}
112+
t = t.In(time.FixedZone(zone, offset))
112113
}
113114
if offset < offsetMin || offset > offsetMax {
114115
return nil, fmt.Errorf("offset must be between %d and %d hours",
@@ -192,11 +193,32 @@ func (dtime *Datetime) add(ival Interval, positive bool) (*Datetime, error) {
192193
newVal.Sec += direction * ival.Sec
193194
newVal.Nsec += direction * ival.Nsec
194195

196+
newLoc := dtime.time.Location()
197+
zone, _ := dtime.time.Zone()
198+
locChanged := false
199+
200+
if loc, err := time.LoadLocation(zone); err == nil {
201+
newLoc = loc
202+
locChanged = true
203+
}
204+
195205
tm := time.Date(int(newVal.Year), time.Month(newVal.Month),
196206
int(newVal.Day), int(newVal.Hour), int(newVal.Min),
197-
int(newVal.Sec), int(newVal.Nsec), dtime.time.Location())
207+
int(newVal.Sec), int(newVal.Nsec), newLoc)
208+
209+
seconds := tm.Unix()
210+
if seconds < minSeconds || seconds > maxSeconds {
211+
return nil, fmt.Errorf("time %s is out of supported range", tm)
212+
}
213+
214+
if locChanged {
215+
_, offset := tm.Zone()
216+
tm = tm.In(time.FixedZone(zone, offset))
217+
}
198218

199-
return NewDatetime(tm)
219+
dt := new(Datetime)
220+
dt.time = tm
221+
return dt, nil
200222
}
201223

202224
// Add creates a new Datetime as addition of the Datetime and Interval. It may

datetime/example_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,35 @@ func ExampleDatetime_Add() {
165165
// New time: 2014-02-28 17:57:29.000000009 +0000 UTC
166166
}
167167

168+
// ExampleDatetime_Add_dst demonstrates how to add an Interval to a Datetime value.
169+
func ExampleDatetime_Add_dst() {
170+
loc := time.FixedZone("Europe/Moscow", 3*60*60)
171+
tm := time.Date(2008, 1, 1, 1, 1, 1, 1, loc)
172+
dt, err := NewDatetime(tm)
173+
if err != nil {
174+
fmt.Printf("Unable to create Datetime: %s", err)
175+
}
176+
177+
fmt.Printf("Datime time:\n")
178+
fmt.Printf("%s\n", dt.ToTime())
179+
fmt.Printf("Datime time + 6 month:\n")
180+
fmt.Printf("%s\n", dt.ToTime().AddDate(0, 6, 0))
181+
dt, err = dt.Add(Interval{Month: 6})
182+
if err != nil {
183+
fmt.Printf("Unable to add 6 month: %s", err)
184+
}
185+
fmt.Printf("Datetime + 6 month time:\n")
186+
fmt.Printf("%s\n", dt.ToTime())
187+
188+
// Output:
189+
// Datime time:
190+
// 2008-01-01 01:01:01.000000001 +0300 Europe/Moscow
191+
// Datime time + 6 month:
192+
// 2008-07-01 01:01:01.000000001 +0300 Europe/Moscow
193+
// Datetime + 6 month time:
194+
// 2008-07-01 01:01:01.000000001 +0400 Europe/Moscow
195+
}
196+
168197
// ExampleDatetime_Sub demonstrates how to subtract an Interval from a
169198
// Datetime value.
170199
func ExampleDatetime_Sub() {
@@ -235,3 +264,42 @@ func ExampleInterval_Sub() {
235264
// Output:
236265
// {-9 2 3 0 0 -30 10 0 1}
237266
}
267+
268+
// Example_ToTime_fixedZone demonstrates that produced time by Datetime
269+
// has fixed timezone. The zone is selected from the original value.
270+
func ExampleDatetime_ToTime_fixedZone() {
271+
loc, err := time.LoadLocation("Europe/Moscow")
272+
if err != nil {
273+
fmt.Printf("Unable to load Europe/Moscow: %s", err)
274+
return
275+
}
276+
277+
tm := time.Date(2008, 1, 1, 1, 1, 1, 1, loc)
278+
dt, err := NewDatetime(tm)
279+
if err != nil {
280+
fmt.Printf("Unable to create datetime with custom location: %s", err)
281+
}
282+
283+
fmt.Printf("Original time:\n")
284+
fmt.Printf("%s\n", tm)
285+
fmt.Printf("Produced time:\n")
286+
fmt.Printf("%s\n", dt.ToTime())
287+
fmt.Printf("Original time + 6 month:\n")
288+
fmt.Printf("%s\n", tm.AddDate(0, 6, 0))
289+
fmt.Printf("Produced time + 6 month:\n")
290+
fmt.Printf("%s\n", dt.ToTime().AddDate(0, 6, 0))
291+
fmt.Printf("Datetime + 6 month:\n")
292+
dt.Add(Interval{Month: 6})
293+
fmt.Printf("%s\n", dt.ToTime())
294+
// Output:
295+
// Original time:
296+
// 2008-01-01 01:01:01.000000001 +0300 MSK
297+
// Produced time:
298+
// 2008-01-01 01:01:01.000000001 +0300 MSK
299+
// Original time + 6 month:
300+
// 2008-07-01 01:01:01.000000001 +0400 MSD
301+
// Produced time + 6 month:
302+
// 2008-07-01 01:01:01.000000001 +0300 MSK
303+
// Datetime + 6 month:
304+
// 2008-01-01 01:01:01.000000001 +0300 MSK
305+
}

0 commit comments

Comments
 (0)