社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  DATABASE

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

脚本之家 • 7 月前 • 192 次点击  
脚本之家 设为“星标
第一时间收到文章更新
图片
出处:凉凉的知识库(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