Go读取MySQLDate类型的避坑指南

作者:

文章目录
  • 先举一个实际的: 我们先创建一个表,并插入一行数据。注意表中两个字段一个是DATETIME类型,一个是DATE类型的 CREATE TABLE `t_test` ( `id` int NOT NULL AUTO_INCREMENT, `f_one` datetime DEFAULT NULL, `f_two` date DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; INSERT INTO `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:05和2025-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程序读取到的值。 Go字段类型定义 parseTime=false parseTime=true string YYYY-MM-DD[ HH:MM:SS[.fraction]]即:DATE->YYYY-MM-DDDATETIME->YYYY-MM-DD HH:MM:SSDATETIME(3)->YYYY-MM-DD HH:MM:SS.sssDATETIME(6)->YYYY-MM-DD HH:MM:SS.ssssss零值->0000-00-00[ 00:00:00] 此时Go中时间字符串的格式和MySQL默认格式一致 RFC3339YYYY-MM-DDTHH:MM:SS±HH:MM 此时Go中时间字符串的格式和MySQL默认格式不一致!! time.Time 报错 (类型不兼容) Gotime.Time实例如果零值也是零值time.Time实例 由此也引出了比较合理的使用姿势 如果启用了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”` } 到此这篇关于Go读取MySQL Date类型的避坑指南的文章就介绍到这了,更多相关Go读取MySQL Date类型内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: 详解Go语言如何使用xorm实现读取mysql 如何利用Golang解析读取Mysql备份文件 Django读取Mysql数据并显示在前端的实例
  • 目录
    • 一不小心就踩坑
    • 问题出在哪里?
    • 总结

    先举一个实际的:

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

    CREATE TABLE `t_test` (
      `id` int NOT NULL AUTO_INCREMENT,
      `f_one` datetime DEFAULT NULL,
      `f_two` date DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
    
    INSERT INTO `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.TimeString()方法进行转换,time.String()按照RFC3339格式输出,与MySQL中展示的格式不一致,就形成了上文的效果。也就是说

    # 开启parseTime=true且Go类型定义为string时
    
    `DATETIME(MySQL)` 
          |
          v
    `time.Time`(golang) 
          |
          | ->这里的`time.time`到`string`这一步出现了转化差异
          v
    `string(golang)`          
    

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

    Go字段类型定义 parseTime=false parseTime=true
    string YYYY-MM-DD[ HH:MM:SS[.fraction]]
    即:
    DATE->YYYY-MM-DD
    DATETIME->YYYY-MM-DD HH:MM:SS
    DATETIME(3)->YYYY-MM-DD HH:MM:SS.sss
    DATETIME(6)->YYYY-MM-DD HH:MM:SS.ssssss
    零值->0000-00-00[ 00:00:00]

    此时Go中时间字符串的格式和MySQL默认格式一致

    RFC3339
    YYYY-MM-DDTHH:MM:SS±HH:MM

    此时Go中时间字符串的格式和MySQL默认格式不一致!!

    time.Time 报错 (类型不兼容) Gotime.Time实例
    如果零值也是零值time.Time实例

    由此也引出了比较合理的使用姿势

    • 如果启用了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"`
    }
    

    到此这篇关于Go读取MySQL Date类型的避坑指南的文章就介绍到这了,更多相关Go读取MySQL Date类型内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

    您可能感兴趣的文章:

    • 详解Go语言如何使用xorm实现读取mysql
    • 如何利用Golang解析读取Mysql备份文件
    • Django读取Mysql数据并显示在前端的实例

    站内搜索