如何在Go应用中从PostgreSQL获取无时区的timestamp值?
解决Go应用中PostgreSQL TIMESTAMP(无时区)的类型映射问题
嘿,我刚踩过类似的坑,这事儿核心是Go的database/sql和PostgreSQL的类型映射没对齐,咱们一步步来搞定:
问题根源
PostgreSQL的TIMESTAMP(不带时区)类型,在Go标准库database/sql里对应的原生类型是time.Time,而不是你用的string。直接用string接收驱动返回的timestamp值,驱动没法正确解析二进制格式的timestamp,自然测试失败。另外你代码里还有个小语法错误:Go里字符串得用双引号,单引号是用来定义单个字符(rune)的,'2018-12-31 00:00:00'这种写法本身就会编译报错。
修正方案
1. 调整结构体类型
把Timestamp字段改成time.Time(如果你的timestamp列允许为空,就用sql.NullTime):
import "time" import "database/sql" type Datapoint struct { Timestamp time.Time // 完美对应PostgreSQL的TIMESTAMP类型 Data sql.NullString // 保持不变,适配允许为空的TEXT列 }
2. 修正测试数据的写法
Go里字符串用双引号,sql.NullString需要显式标记Valid字段:
// 推荐用time.Date构造时间,避免字符串解析隐患 var testData = Datapoint{ Timestamp: time.Date(2018, 12, 31, 0, 0, 0, 0, time.UTC), Data: sql.NullString{String: "test", Valid: true}, } // 如果一定要用字符串解析,记得用time.Parse // ts, _ := time.Parse("2006-01-02 15:04:05", "2018-12-31 00:00:00") // var testData = Datapoint{Timestamp: ts, Data: sql.NullString{String: "test", Valid: true}}
3. 正确执行插入操作
别用字符串拼接SQL,用参数化查询——驱动会自动处理类型映射,还能避免SQL注入:
// 创建表(测试用,生产环境别重复执行) _, err := db.Exec("CREATE TABLE IF NOT EXISTS mytable (id SERIAL, timestamp TIMESTAMP, data TEXT);") if err != nil { log.Fatal(err) } // PostgreSQL用$1、$2作为参数占位符,不是? _, err = db.Exec("INSERT INTO mytable(timestamp, data) VALUES ($1, $2)", testData.Timestamp, testData.Data) if err != nil { log.Fatal(err) }
4. 查询数据时的字符串转换
如果需要把数据库返回的time.Time转成字符串格式,直接用Format方法:
var dp Datapoint err := db.QueryRow("SELECT timestamp, data FROM mytable WHERE id = $1", 1).Scan(&dp.Timestamp, &dp.Data) if err != nil { log.Fatal(err) } // 转成你需要的字符串格式 tsStr := dp.Timestamp.Format("2006-01-02 15:04:05") fmt.Println(tsStr)
额外提醒
如果你确实需要在结构体里用string存储timestamp,那得在查询时用PostgreSQL的TO_CHAR函数把timestamp转成字符串:
SELECT TO_CHAR(timestamp, 'YYYY-MM-DD HH24:MI:SS') AS timestamp_str, data FROM mytable
然后扫描到string类型的字段里,但这种方式不如直接用time.Time灵活,毕竟time.Time可以方便地做时间计算、时区转换等操作。
内容的提问来源于stack exchange,提问作者Escher
相关产品推荐
相关产品推荐

