Py学习  »  DATABASE

在Go中读取MySQL Date类型,一不小心就踩坑

脚本之家 • 1 周前 • 32 次点击  
脚本之家 设为“星标
第一时间收到文章更新
图片
出处:凉凉的知识库(ID:luckyliangliangliang)

一不小心就踩坑

先举一个实际的🌰:

我们先创建一个表,并插入一行数据。注意表中两个字段一个是DATETIME类型,一个是 DATE类型的

CREATE TABLE`t_test` (
`id`intNOTNULL AUTO_INCREMENT,
`f_one` datetime DEFAULTNULL,
`f_two`dateDEFAULTNULL,
  PRIMARY KEY (`id`)
ENGINE=InnoDBDEFAULTCHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

INSERTINTO`test`.`t_test` (`f_one``f_two`VALUES ('2025-11-29 10:25:05''2025-11-29');

当我们查询数据可以看到和插入的数据一致

  • f_one datetime展示为2025-11-29 10:25:05
  • f_two date展示为2025-11-29



    
mysql> select * from t_test where id = 1;
+----+---------------------+------------+
| id | f_one               | f_two      |
+----+---------------------+------------+
|  1 | 2025-11-29 10:25:05 | 2025-11-29 |
+----+---------------------+------------+
1 row in set (0.003 sec)

现在使用Go来读取上面的数据

package main

import (
"testing"

"github.com/stretchr/testify/assert"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

type Test struct {
 ID   int    `gorm:"column:id;primaryKey"`
 FOne string`gorm:"column:f_one"`// 注意这里我们定义成了string类型
 FTwo string`gorm:"column:f_two"`// 注意这里我们定义成了string类型
}

func (t Test) TableName() string {
return"t_test"
}

func TestRead(t *testing.T) {
// 数据库连接配置
 dsn := "root:root@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"

 db, err := gorm.Open(mysql.Open(dsn))
 assert.Nil(t, err)

var result Test
 err = db.Where("id = ?"1).Find(&result).Error
 assert.Nil(t, err)

 assert.Equal(t, "2025-11-29 10:25:05", result.FOne)
 assert.Equal(t, "2025-11-29", result.FTwo)
}

可以在这里暂停思考下,上面的单元测试是否会通过。下面是实际执行的结果。

Error: Not equal: 
       expected: "2025-11-29 10:25:05"
       actual  : "2025-11-29T10:25:05+08:00"

Error: Not equal: 
       expected: "2025-11-29"
       actual  : "2025-11-29T00:00:00+08:00"

结果非常Amazing啊

f_one我们期望的是2025-11-29 10:25:05,实际Go读取到是 2025-11-29T10:25:05+08:00,额外添加了T分隔符和时区信息

f_two我们期望的是2025-11-29, 实际Go读取到是 2025-11-29T00:00:00+08:00,不仅额外添加了T分隔符和时区信息,还添加了时分秒

你看,如果你对Go读取到的字符串按MySQL时间格式处理,可能一不注意就踩坑里了。

问题出在哪里?

你可能已经注意到了,Go结构体的定义使用了string类型,但不知道你有没有注意到数据库连接中的另外一个参数 parseTime=True

parseTime是什么?parseTime是 Go 的 MySQL 驱动 (github.com/go-sql-driver/mysql) 中的一个连接参数,控制是否将 MySQL 时间类型自动转换为 Go 的 time.Time对象:

  • parseTime=false 禁用自动转换(默认值)
  • parseTime=true 启用自动转换

当Go的定义为string类型时,问题就出在自动转换上

parseTime=false时, 转化方式为DATETIME/DATE(MySQL)->string(Go) ,不会出现上面的问题,Go中读取到的字符串和MySQL的格式一致:2025-11-29 10:25:052025-11-29

parseTime=true时,Go的MySQL驱动首先尝试将 MySQL的 DATETIME/DATE(MySQL)解析为 Go 的 time.Time对象,当发现目标字段是  string类型时,驱动会自动调用 time.Time的 String()方法进行转换,time.String()按照RFC3339格式输出,与MySQL中展示的格式不一致,就形成了上文的效果。也就是说

# 开启parseTime=true且Go类型定义为string时

`DATETIME(MySQL)` 
      |
      v
`time.Time`(golang) 
      |
      | ->这里的`time.time`到`string`这一步出现了转化差异
      v
`string(golang)`          

总结

下面总结不同parseTime和Go类型定义的情况下,Go程序读取到的值。


由此也引出了比较合理的使用姿势
  • 如果启用了 parseTime=true,我建议你在Go中定义成time.Time类型
type Test struct {
 ID   int       `gorm:"column:id;primaryKey"`
 FOne time.Time `gorm:"column:f_one"`
 FTwo time.Time `gorm:"column:f_two"`
}
  • 如果没有启用,即parseTime=false,在Go只能定义成string
type Test struct {
 ID   int    `gorm:"column:id;primaryKey"`
 FOne string `gorm:"column:f_one"`
 FTwo string `gorm:"column:f_two"`
}


图片
  推荐阅读:
  1. 尬住了,Go HTTP/3 又又被搁置了!
  2. Go、Python、Rust:该学哪一门?
  3. Go 会成为“老生态”的新引擎吗?
  4. Python 正成为新的 PHP——承认这一点很痛苦
  5. Python、Go、Rust、TypeScript、Kotlin,谁能定义未来?

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/189959