Skip to content

Commit fbf09db

Browse files
committed
bugfix: unequal timezone after decoding
The patch fixes unequal timezones after Datetime encoding/decoding. It does two things for the fix: 1. After the patch, a location name is picked from Time.Location().String() instead of Time.Zone(). It allows us to encode a timezone from the original name. 2. A decoder function tries to load a location from system location. It allows to handle unfixed timezones properly. Closes #217
1 parent d4905f5 commit fbf09db

File tree

4 files changed

+151
-63
lines changed

4 files changed

+151
-63
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
2727
- A connection is still opened after ConnectionPool.Close() (#208)
2828
- Future.GetTyped() after Future.Get() does not decode response
2929
correctly (#213)
30+
- Datetime location after encode + decode is not equal (#217)
3031

3132
## [1.8.0] - 2022-08-17
3233

datetime/datetime.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,20 +96,25 @@ const (
9696
// supported range: [-5879610-06-22T00:00Z .. 5879611-07-11T00:00Z] or
9797
// an invalid timezone or offset value is out of supported range:
9898
// [-12 * 60 * 60, 14 * 60 * 60].
99+
//
100+
// NOTE: Tarantool's datetime.tz value is picked from t.Location().String().
101+
// "Local" location is unsupported, see ExampleNewDatetime_localUnsupported.
99102
func NewDatetime(t time.Time) (*Datetime, error) {
100103
seconds := t.Unix()
101104

102105
if seconds < minSeconds || seconds > maxSeconds {
103106
return nil, fmt.Errorf("time %s is out of supported range", t)
104107
}
105108

106-
zone, offset := t.Zone()
109+
zone := t.Location().String()
110+
_, offset := t.Zone()
107111
if zone != NoTimezone {
108112
if _, ok := timezoneToIndex[zone]; !ok {
109113
return nil, fmt.Errorf("unknown timezone %s with offset %d",
110114
zone, offset)
111115
}
112116
}
117+
113118
if offset < offsetMin || offset > offsetMax {
114119
return nil, fmt.Errorf("offset must be between %d and %d hours",
115120
offsetMin, offsetMax)
@@ -230,7 +235,8 @@ func (dtime *Datetime) MarshalMsgpack() ([]byte, error) {
230235
dt.seconds = tm.Unix()
231236
dt.nsec = int32(tm.Nanosecond())
232237

233-
zone, offset := tm.Zone()
238+
zone := tm.Location().String()
239+
_, offset := tm.Zone()
234240
if zone != NoTimezone {
235241
// The zone value already checked in NewDatetime() or
236242
// UnmarshalMsgpack() calls.
@@ -283,7 +289,17 @@ func (tm *Datetime) UnmarshalMsgpack(b []byte) error {
283289
}
284290
zone = indexToTimezone[int(dt.tzIndex)]
285291
}
286-
loc = time.FixedZone(zone, offset)
292+
if zone != NoTimezone {
293+
if loadLoc, err := time.LoadLocation(zone); err == nil {
294+
loc = loadLoc
295+
} else {
296+
// Unable to load location.
297+
loc = time.FixedZone(zone, offset)
298+
}
299+
} else {
300+
// Only offset.
301+
loc = time.FixedZone(zone, offset)
302+
}
287303
}
288304
tt = tt.In(loc)
289305

datetime/datetime_test.go

Lines changed: 80 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -527,11 +527,12 @@ func TestCustomTimezone(t *testing.T) {
527527

528528
tpl := resp.Data[0].([]interface{})
529529
if respDt, ok := toDatetime(tpl[0]); ok {
530-
zone, offset := respDt.ToTime().Zone()
530+
zone := respDt.ToTime().Location().String()
531+
_, offset := respDt.ToTime().Zone()
531532
if zone != customZone {
532533
t.Fatalf("Expected zone %s instead of %s", customZone, zone)
533534
}
534-
if offset != customOffset {
535+
if offset != 240*60 {
535536
t.Fatalf("Expected offset %d instead of %d", customOffset, offset)
536537
}
537538

@@ -586,62 +587,63 @@ var datetimeSample = []struct {
586587
fmt string
587588
dt string
588589
mpBuf string // MessagePack buffer.
590+
zone string
589591
}{
590592
/* Cases for base encoding without a timezone. */
591-
{time.RFC3339, "2012-01-31T23:59:59.000000010Z", "d8047f80284f000000000a00000000000000"},
592-
{time.RFC3339, "1970-01-01T00:00:00.000000010Z", "d80400000000000000000a00000000000000"},
593-
{time.RFC3339, "2010-08-12T11:39:14Z", "d70462dd634c00000000"},
594-
{time.RFC3339, "1984-03-24T18:04:05Z", "d7041530c31a00000000"},
595-
{time.RFC3339, "2010-01-12T00:00:00Z", "d70480bb4b4b00000000"},
596-
{time.RFC3339, "1970-01-01T00:00:00Z", "d7040000000000000000"},
597-
{time.RFC3339, "1970-01-01T00:00:00.123456789Z", "d804000000000000000015cd5b0700000000"},
598-
{time.RFC3339, "1970-01-01T00:00:00.12345678Z", "d80400000000000000000ccd5b0700000000"},
599-
{time.RFC3339, "1970-01-01T00:00:00.1234567Z", "d8040000000000000000bccc5b0700000000"},
600-
{time.RFC3339, "1970-01-01T00:00:00.123456Z", "d804000000000000000000ca5b0700000000"},
601-
{time.RFC3339, "1970-01-01T00:00:00.12345Z", "d804000000000000000090b25b0700000000"},
602-
{time.RFC3339, "1970-01-01T00:00:00.1234Z", "d804000000000000000040ef5a0700000000"},
603-
{time.RFC3339, "1970-01-01T00:00:00.123Z", "d8040000000000000000c0d4540700000000"},
604-
{time.RFC3339, "1970-01-01T00:00:00.12Z", "d8040000000000000000000e270700000000"},
605-
{time.RFC3339, "1970-01-01T00:00:00.1Z", "d804000000000000000000e1f50500000000"},
606-
{time.RFC3339, "1970-01-01T00:00:00.01Z", "d80400000000000000008096980000000000"},
607-
{time.RFC3339, "1970-01-01T00:00:00.001Z", "d804000000000000000040420f0000000000"},
608-
{time.RFC3339, "1970-01-01T00:00:00.0001Z", "d8040000000000000000a086010000000000"},
609-
{time.RFC3339, "1970-01-01T00:00:00.00001Z", "d80400000000000000001027000000000000"},
610-
{time.RFC3339, "1970-01-01T00:00:00.000001Z", "d8040000000000000000e803000000000000"},
611-
{time.RFC3339, "1970-01-01T00:00:00.0000001Z", "d80400000000000000006400000000000000"},
612-
{time.RFC3339, "1970-01-01T00:00:00.00000001Z", "d80400000000000000000a00000000000000"},
613-
{time.RFC3339, "1970-01-01T00:00:00.000000001Z", "d80400000000000000000100000000000000"},
614-
{time.RFC3339, "1970-01-01T00:00:00.000000009Z", "d80400000000000000000900000000000000"},
615-
{time.RFC3339, "1970-01-01T00:00:00.00000009Z", "d80400000000000000005a00000000000000"},
616-
{time.RFC3339, "1970-01-01T00:00:00.0000009Z", "d80400000000000000008403000000000000"},
617-
{time.RFC3339, "1970-01-01T00:00:00.000009Z", "d80400000000000000002823000000000000"},
618-
{time.RFC3339, "1970-01-01T00:00:00.00009Z", "d8040000000000000000905f010000000000"},
619-
{time.RFC3339, "1970-01-01T00:00:00.0009Z", "d8040000000000000000a0bb0d0000000000"},
620-
{time.RFC3339, "1970-01-01T00:00:00.009Z", "d80400000000000000004054890000000000"},
621-
{time.RFC3339, "1970-01-01T00:00:00.09Z", "d8040000000000000000804a5d0500000000"},
622-
{time.RFC3339, "1970-01-01T00:00:00.9Z", "d804000000000000000000e9a43500000000"},
623-
{time.RFC3339, "1970-01-01T00:00:00.99Z", "d80400000000000000008033023b00000000"},
624-
{time.RFC3339, "1970-01-01T00:00:00.999Z", "d8040000000000000000c0878b3b00000000"},
625-
{time.RFC3339, "1970-01-01T00:00:00.9999Z", "d80400000000000000006043993b00000000"},
626-
{time.RFC3339, "1970-01-01T00:00:00.99999Z", "d8040000000000000000f0a29a3b00000000"},
627-
{time.RFC3339, "1970-01-01T00:00:00.999999Z", "d804000000000000000018c69a3b00000000"},
628-
{time.RFC3339, "1970-01-01T00:00:00.9999999Z", "d80400000000000000009cc99a3b00000000"},
629-
{time.RFC3339, "1970-01-01T00:00:00.99999999Z", "d8040000000000000000f6c99a3b00000000"},
630-
{time.RFC3339, "1970-01-01T00:00:00.999999999Z", "d8040000000000000000ffc99a3b00000000"},
631-
{time.RFC3339, "1970-01-01T00:00:00.0Z", "d7040000000000000000"},
632-
{time.RFC3339, "1970-01-01T00:00:00.00Z", "d7040000000000000000"},
633-
{time.RFC3339, "1970-01-01T00:00:00.000Z", "d7040000000000000000"},
634-
{time.RFC3339, "1970-01-01T00:00:00.0000Z", "d7040000000000000000"},
635-
{time.RFC3339, "1970-01-01T00:00:00.00000Z", "d7040000000000000000"},
636-
{time.RFC3339, "1970-01-01T00:00:00.000000Z", "d7040000000000000000"},
637-
{time.RFC3339, "1970-01-01T00:00:00.0000000Z", "d7040000000000000000"},
638-
{time.RFC3339, "1970-01-01T00:00:00.00000000Z", "d7040000000000000000"},
639-
{time.RFC3339, "1970-01-01T00:00:00.000000000Z", "d7040000000000000000"},
640-
{time.RFC3339, "1973-11-29T21:33:09Z", "d70415cd5b0700000000"},
641-
{time.RFC3339, "2013-10-28T17:51:56Z", "d7043ca46e5200000000"},
642-
{time.RFC3339, "9999-12-31T23:59:59Z", "d7047f41f4ff3a000000"},
593+
{time.RFC3339, "2012-01-31T23:59:59.000000010Z", "d8047f80284f000000000a00000000000000", ""},
594+
{time.RFC3339, "1970-01-01T00:00:00.000000010Z", "d80400000000000000000a00000000000000", ""},
595+
{time.RFC3339, "2010-08-12T11:39:14Z", "d70462dd634c00000000", ""},
596+
{time.RFC3339, "1984-03-24T18:04:05Z", "d7041530c31a00000000", ""},
597+
{time.RFC3339, "2010-01-12T00:00:00Z", "d70480bb4b4b00000000", ""},
598+
{time.RFC3339, "1970-01-01T00:00:00Z", "d7040000000000000000", ""},
599+
{time.RFC3339, "1970-01-01T00:00:00.123456789Z", "d804000000000000000015cd5b0700000000", ""},
600+
{time.RFC3339, "1970-01-01T00:00:00.12345678Z", "d80400000000000000000ccd5b0700000000", ""},
601+
{time.RFC3339, "1970-01-01T00:00:00.1234567Z", "d8040000000000000000bccc5b0700000000", ""},
602+
{time.RFC3339, "1970-01-01T00:00:00.123456Z", "d804000000000000000000ca5b0700000000", ""},
603+
{time.RFC3339, "1970-01-01T00:00:00.12345Z", "d804000000000000000090b25b0700000000", ""},
604+
{time.RFC3339, "1970-01-01T00:00:00.1234Z", "d804000000000000000040ef5a0700000000", ""},
605+
{time.RFC3339, "1970-01-01T00:00:00.123Z", "d8040000000000000000c0d4540700000000", ""},
606+
{time.RFC3339, "1970-01-01T00:00:00.12Z", "d8040000000000000000000e270700000000", ""},
607+
{time.RFC3339, "1970-01-01T00:00:00.1Z", "d804000000000000000000e1f50500000000", ""},
608+
{time.RFC3339, "1970-01-01T00:00:00.01Z", "d80400000000000000008096980000000000", ""},
609+
{time.RFC3339, "1970-01-01T00:00:00.001Z", "d804000000000000000040420f0000000000", ""},
610+
{time.RFC3339, "1970-01-01T00:00:00.0001Z", "d8040000000000000000a086010000000000", ""},
611+
{time.RFC3339, "1970-01-01T00:00:00.00001Z", "d80400000000000000001027000000000000", ""},
612+
{time.RFC3339, "1970-01-01T00:00:00.000001Z", "d8040000000000000000e803000000000000", ""},
613+
{time.RFC3339, "1970-01-01T00:00:00.0000001Z", "d80400000000000000006400000000000000", ""},
614+
{time.RFC3339, "1970-01-01T00:00:00.00000001Z", "d80400000000000000000a00000000000000", ""},
615+
{time.RFC3339, "1970-01-01T00:00:00.000000001Z", "d80400000000000000000100000000000000", ""},
616+
{time.RFC3339, "1970-01-01T00:00:00.000000009Z", "d80400000000000000000900000000000000", ""},
617+
{time.RFC3339, "1970-01-01T00:00:00.00000009Z", "d80400000000000000005a00000000000000", ""},
618+
{time.RFC3339, "1970-01-01T00:00:00.0000009Z", "d80400000000000000008403000000000000", ""},
619+
{time.RFC3339, "1970-01-01T00:00:00.000009Z", "d80400000000000000002823000000000000", ""},
620+
{time.RFC3339, "1970-01-01T00:00:00.00009Z", "d8040000000000000000905f010000000000", ""},
621+
{time.RFC3339, "1970-01-01T00:00:00.0009Z", "d8040000000000000000a0bb0d0000000000", ""},
622+
{time.RFC3339, "1970-01-01T00:00:00.009Z", "d80400000000000000004054890000000000", ""},
623+
{time.RFC3339, "1970-01-01T00:00:00.09Z", "d8040000000000000000804a5d0500000000", ""},
624+
{time.RFC3339, "1970-01-01T00:00:00.9Z", "d804000000000000000000e9a43500000000", ""},
625+
{time.RFC3339, "1970-01-01T00:00:00.99Z", "d80400000000000000008033023b00000000", ""},
626+
{time.RFC3339, "1970-01-01T00:00:00.999Z", "d8040000000000000000c0878b3b00000000", ""},
627+
{time.RFC3339, "1970-01-01T00:00:00.9999Z", "d80400000000000000006043993b00000000", ""},
628+
{time.RFC3339, "1970-01-01T00:00:00.99999Z", "d8040000000000000000f0a29a3b00000000", ""},
629+
{time.RFC3339, "1970-01-01T00:00:00.999999Z", "d804000000000000000018c69a3b00000000", ""},
630+
{time.RFC3339, "1970-01-01T00:00:00.9999999Z", "d80400000000000000009cc99a3b00000000", ""},
631+
{time.RFC3339, "1970-01-01T00:00:00.99999999Z", "d8040000000000000000f6c99a3b00000000", ""},
632+
{time.RFC3339, "1970-01-01T00:00:00.999999999Z", "d8040000000000000000ffc99a3b00000000", ""},
633+
{time.RFC3339, "1970-01-01T00:00:00.0Z", "d7040000000000000000", ""},
634+
{time.RFC3339, "1970-01-01T00:00:00.00Z", "d7040000000000000000", ""},
635+
{time.RFC3339, "1970-01-01T00:00:00.000Z", "d7040000000000000000", ""},
636+
{time.RFC3339, "1970-01-01T00:00:00.0000Z", "d7040000000000000000", ""},
637+
{time.RFC3339, "1970-01-01T00:00:00.00000Z", "d7040000000000000000", ""},
638+
{time.RFC3339, "1970-01-01T00:00:00.000000Z", "d7040000000000000000", ""},
639+
{time.RFC3339, "1970-01-01T00:00:00.0000000Z", "d7040000000000000000", ""},
640+
{time.RFC3339, "1970-01-01T00:00:00.00000000Z", "d7040000000000000000", ""},
641+
{time.RFC3339, "1970-01-01T00:00:00.000000000Z", "d7040000000000000000", ""},
642+
{time.RFC3339, "1973-11-29T21:33:09Z", "d70415cd5b0700000000", ""},
643+
{time.RFC3339, "2013-10-28T17:51:56Z", "d7043ca46e5200000000", ""},
644+
{time.RFC3339, "9999-12-31T23:59:59Z", "d7047f41f4ff3a000000", ""},
643645
/* Cases for encoding with a timezone. */
644-
{time.RFC3339 + " MST", "2006-01-02T15:04:00+03:00 MSK", "d804b016b9430000000000000000b400ee00"},
646+
{time.RFC3339, "2006-01-02T15:04:00Z", "d804e040b9430000000000000000b400b303", "Europe/Moscow"},
645647
}
646648

647649
func TestDatetimeInsertSelectDelete(t *testing.T) {
@@ -653,8 +655,14 @@ func TestDatetimeInsertSelectDelete(t *testing.T) {
653655
for _, testcase := range datetimeSample {
654656
t.Run(testcase.dt, func(t *testing.T) {
655657
tm, err := time.Parse(testcase.fmt, testcase.dt)
656-
if testcase.fmt == time.RFC3339 {
658+
if testcase.zone == "" {
657659
tm = tm.In(noTimezoneLoc)
660+
} else {
661+
loc, err := time.LoadLocation(testcase.zone)
662+
if err != nil {
663+
t.Fatalf("Unable to load location: %s", err)
664+
}
665+
tm = tm.In(loc)
658666
}
659667
if err != nil {
660668
t.Fatalf("Time (%s) parse failed: %s", testcase.dt, err)
@@ -966,7 +974,7 @@ func TestCustomEncodeDecodeTuple5(t *testing.T) {
966974
conn := test_helpers.ConnectWithValidation(t, server, opts)
967975
defer conn.Close()
968976

969-
tm := time.Unix(500, 1000)
977+
tm := time.Unix(500, 1000).In(time.FixedZone(NoTimezone, 0))
970978
dt, err := NewDatetime(tm)
971979
if err != nil {
972980
t.Fatalf("Unable to create Datetime from %s: %s", tm, err)
@@ -999,8 +1007,14 @@ func TestMPEncode(t *testing.T) {
9991007
for _, testcase := range datetimeSample {
10001008
t.Run(testcase.dt, func(t *testing.T) {
10011009
tm, err := time.Parse(testcase.fmt, testcase.dt)
1002-
if testcase.fmt == time.RFC3339 {
1010+
if testcase.zone == "" {
10031011
tm = tm.In(noTimezoneLoc)
1012+
} else {
1013+
loc, err := time.LoadLocation(testcase.zone)
1014+
if err != nil {
1015+
t.Fatalf("Unable to load location: %s", err)
1016+
}
1017+
tm = tm.In(loc)
10041018
}
10051019
if err != nil {
10061020
t.Fatalf("Time (%s) parse failed: %s", testcase.dt, err)
@@ -1016,7 +1030,7 @@ func TestMPEncode(t *testing.T) {
10161030
refBuf, _ := hex.DecodeString(testcase.mpBuf)
10171031
if reflect.DeepEqual(buf, refBuf) != true {
10181032
t.Fatalf("Failed to encode datetime '%s', actual %x, expected %x",
1019-
testcase.dt,
1033+
tm,
10201034
buf,
10211035
refBuf)
10221036
}
@@ -1028,8 +1042,14 @@ func TestMPDecode(t *testing.T) {
10281042
for _, testcase := range datetimeSample {
10291043
t.Run(testcase.dt, func(t *testing.T) {
10301044
tm, err := time.Parse(testcase.fmt, testcase.dt)
1031-
if testcase.fmt == time.RFC3339 {
1045+
if testcase.zone == "" {
10321046
tm = tm.In(noTimezoneLoc)
1047+
} else {
1048+
loc, err := time.LoadLocation(testcase.zone)
1049+
if err != nil {
1050+
t.Fatalf("Unable to load location: %s", err)
1051+
}
1052+
tm = tm.In(loc)
10331053
}
10341054
if err != nil {
10351055
t.Fatalf("Time (%s) parse failed: %s", testcase.dt, err)

datetime/example_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,21 @@ func Example() {
8080
fmt.Printf("Data: %v\n", respDt.ToTime())
8181
}
8282

83+
// ExampleNewDatetime_localUnsupported demonstrates that "Local" location is
84+
// unsupported.
85+
func ExampleNewDatetime_localUnsupported() {
86+
tm := time.Now().Local()
87+
fmt.Println("Location:", tm.Location().String())
88+
if _, err := NewDatetime(tm); err != nil {
89+
fmt.Println("Could not create a Datetime with Local location.")
90+
} else {
91+
fmt.Println("A Datetime with Local location created.")
92+
}
93+
// Output:
94+
// Location: Local
95+
// Could not create a Datetime with Local location.
96+
}
97+
8398
// Example demonstrates how to create a datetime for Tarantool without UTC
8499
// timezone in datetime.
85100
func ExampleNewDatetime_noTimezone() {
@@ -165,6 +180,42 @@ func ExampleDatetime_Add() {
165180
// New time: 2014-02-28 17:57:29.000000009 +0000 UTC
166181
}
167182

183+
// ExampleDatetime_Add_dst demonstrates how to add an Interval to a
184+
// Datetime value with a DST location.
185+
func ExampleDatetime_Add_dst() {
186+
loc, err := time.LoadLocation("Europe/Moscow")
187+
if err != nil {
188+
fmt.Printf("Unable to load location: %s", err)
189+
return
190+
}
191+
tm := time.Date(2008, 1, 1, 1, 1, 1, 1, loc)
192+
dt, err := NewDatetime(tm)
193+
if err != nil {
194+
fmt.Printf("Unable to create Datetime: %s", err)
195+
return
196+
}
197+
198+
fmt.Printf("Datime time:\n")
199+
fmt.Printf("%s\n", dt.ToTime())
200+
fmt.Printf("Datime time + 6 month:\n")
201+
fmt.Printf("%s\n", dt.ToTime().AddDate(0, 6, 0))
202+
dt, err = dt.Add(Interval{Month: 6})
203+
if err != nil {
204+
fmt.Printf("Unable to add 6 month: %s", err)
205+
return
206+
}
207+
fmt.Printf("Datetime + 6 month time:\n")
208+
fmt.Printf("%s\n", dt.ToTime())
209+
210+
// Output:
211+
// Datime time:
212+
// 2008-01-01 01:01:01.000000001 +0300 MSK
213+
// Datime time + 6 month:
214+
// 2008-07-01 01:01:01.000000001 +0400 MSD
215+
// Datetime + 6 month time:
216+
// 2008-07-01 01:01:01.000000001 +0400 MSD
217+
}
218+
168219
// ExampleDatetime_Sub demonstrates how to subtract an Interval from a
169220
// Datetime value.
170221
func ExampleDatetime_Sub() {

0 commit comments

Comments
 (0)