Skip to content

Commit 87d5977

Browse files
authored
feat: support timestamp_tz, (#198)
1 parent ef30899 commit 87d5977

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

columntype.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,31 @@ func (c timestampColumnType) Desc() *TypeDesc {
134134
return &TypeDesc{Name: "Timestamp", Nullable: bool(c.isNullable)}
135135
}
136136

137+
type timestampTzColumnType struct {
138+
columnTypeDefault
139+
isNullable
140+
}
141+
142+
func (c timestampTzColumnType) Parse(s string) (driver.Value, error) {
143+
if c.checkNull(s) {
144+
return nil, nil
145+
}
146+
println(s)
147+
return time.Parse("2006-01-02 15:04:05.999999 -0700", s)
148+
}
149+
150+
func (c timestampTzColumnType) ScanType() reflect.Type {
151+
return reflectTypeTime
152+
}
153+
154+
func (c timestampTzColumnType) DatabaseTypeName() string {
155+
return c.wrapName("Timestamp_Tz")
156+
}
157+
158+
func (c timestampTzColumnType) Desc() *TypeDesc {
159+
return &TypeDesc{Name: "Timestamp_Tz", Nullable: bool(c.isNullable)}
160+
}
161+
137162
type dateColumnType struct {
138163
columnTypeDefault
139164
isNullable
@@ -224,6 +249,8 @@ func NewColumnType(dbType string, opts *ColumnTypeOptions) (ColumnType, error) {
224249
return &simpleColumnType{dbType: nullable.wrapName(desc.Name), scanType: reflectTypeFloat64, nullable: desc.Nullable, parseNull: parseNull}, nil
225250
case "Timestamp":
226251
return &timestampColumnType{isNullable: nullable, tz: opts.timezone}, nil
252+
case "Timestamp_Tz":
253+
return &timestampTzColumnType{isNullable: nullable}, nil
227254
case "Date":
228255
return &dateColumnType{isNullable: nullable}, nil
229256
case "Decimal":

tests/type_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,61 @@ func (s *DatabendTestSuite) TestTimestamp() {
137137
})
138138
}
139139
}
140+
141+
func (s *DatabendTestSuite) TestTimestampTz() {
142+
if semver.Compare(driverVersion, "v0.9.0") <= 0 || semver.Compare(serverVersion, "1.2.844") < 0 {
143+
return
144+
}
145+
146+
type testCase struct {
147+
name string
148+
// scan should get Time with location in settings
149+
setting *time.Location
150+
data *time.Location
151+
}
152+
locShanghai, _ := time.LoadLocation("Asia/Shanghai")
153+
locLos, _ := time.LoadLocation("America/Los_Angeles")
154+
input := time.Date(2025, 1, 16, 2, 1, 26, 739219000, locLos)
155+
156+
testCases := []testCase{
157+
{name: "1", setting: time.UTC},
158+
{name: "2", setting: locShanghai},
159+
}
160+
161+
for i, tc := range testCases {
162+
s.Run(tc.name, func() {
163+
db := sql.OpenDB(s.cfg)
164+
defer db.Close()
165+
166+
tableName := fmt.Sprintf("test_tz_%s_%d", s.table, i)
167+
result, err := db.Exec(fmt.Sprintf("create or replace table %s (t Timestamp_TZ)", tableName))
168+
s.r.NoError(err)
169+
170+
result, err = db.Exec(fmt.Sprintf("set timezone='%v'", tc.setting.String()))
171+
s.r.NoError(err)
172+
173+
insertSQL := fmt.Sprintf("INSERT INTO %s VALUES (?)", tableName)
174+
result, err = db.Exec(insertSQL, input)
175+
s.r.NoError(err)
176+
n, err := result.RowsAffected()
177+
s.r.NoError(err)
178+
s.r.Equal(int64(1), n)
179+
180+
selectSQL := fmt.Sprintf("select * from %s", tableName)
181+
rows, err := db.Query(selectSQL)
182+
s.r.NoError(err)
183+
s.r.True(rows.Next())
184+
s.r.NoError(err)
185+
186+
var output time.Time
187+
err = rows.Scan(&output)
188+
s.r.NoError(err)
189+
190+
s.r.Equal(input.UnixMicro(), output.UnixMicro())
191+
name, offset := output.Zone()
192+
s.r.Equal(-8*3600, offset)
193+
s.r.Equal("", name)
194+
s.r.NoError(rows.Close())
195+
})
196+
}
197+
}

0 commit comments

Comments
 (0)