Golang中如何处理从数据库获取的可空DateTime格式化问题
Golang中如何处理从数据库获取的可空DateTime格式化问题
我正在使用自定义扫描器来处理可为空的DATETIME字段,但有一个小的待改进之处。如果从数据库获取的日期为空,如何让扫描器返回 "" 或 0000-00-00 00:00:00,而不是 0001-01-01 00:00:00?
谢谢
当前终端输出
{0001-01-01 00:00:00 +0000 UTC false}
当前JSON输出
{
"deleted_at": {
"Time": "0001-01-01T00:00:00Z",
"Valid": false
}
}
期望的JSON输出
{
"deleted_at": {
"Time": "", // 或 0000-00-00 00:00:00
"Valid": false
}
}
用法
DeletedAt database.NullTime `json:"deleted_at"`
package database
import (
"fmt"
"time"
)
type NullTime sql.NullTime
func (nt *NullTime) Scan(value interface{}) error {
if value == nil {
nt.Time, nt.Valid = time.Time{}, false
return nil
}
var err error
switch v := value.(type) {
case time.Time:
nt.Time, nt.Valid = v, true
return nil
case string:
nt.Time, err = time.Parse("2006-01-02 15:04:05", v)
nt.Valid = err == nil
return nil
case []byte:
nt.Time, err = time.Parse("2006-01-02 15:04:05", string(v))
nt.Valid = err == nil
return nil
default:
nt.Time, nt.Valid = time.Time{}, false
return fmt.Errorf("unable to scan %T into type *time.Time", value)
}
}
更多关于Golang中如何处理从数据库获取的可空DateTime格式化问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
“0000-00-00 00:00:00” 是一个无效的时间,因为日期“0000-00-00”并不存在。这就是为什么时间的默认值是“0001-01-01”。
如果您希望在 JSON 输出中使用另一种时间值格式,您必须定义另一个 JSON 编组器。
更多关于Golang中如何处理从数据库获取的可空DateTime格式化问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
要实现你期望的行为,需要自定义 NullTime 类型的 MarshalJSON 方法。以下是完整的解决方案:
package database
import (
"database/sql"
"encoding/json"
"fmt"
"time"
)
type NullTime struct {
sql.NullTime
}
func (nt *NullTime) Scan(value interface{}) error {
if value == nil {
nt.Time, nt.Valid = time.Time{}, false
return nil
}
var err error
switch v := value.(type) {
case time.Time:
nt.Time, nt.Valid = v, true
return nil
case string:
nt.Time, err = time.Parse("2006-01-02 15:04:05", v)
nt.Valid = err == nil
return nil
case []byte:
nt.Time, err = time.Parse("2006-01-02 15:04:05", string(v))
nt.Valid = err == nil
return nil
default:
nt.Time, nt.Valid = time.Time{}, false
return fmt.Errorf("unable to scan %T into type *time.Time", value)
}
}
func (nt NullTime) MarshalJSON() ([]byte, error) {
if !nt.Valid {
// 返回空字符串而不是默认的零值时间
return json.Marshal(struct {
Time string `json:"Time"`
Valid bool `json:"Valid"`
}{
Time: "",
Valid: false,
})
}
// 有效时间按原样序列化
return json.Marshal(struct {
Time time.Time `json:"Time"`
Valid bool `json:"Valid"`
}{
Time: nt.Time,
Valid: true,
})
}
// 如果需要支持UnmarshalJSON,可以添加这个方法
func (nt *NullTime) UnmarshalJSON(data []byte) error {
var aux struct {
Time *time.Time `json:"Time"`
Valid bool `json:"Valid"`
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
if aux.Time != nil && aux.Valid {
nt.Time = *aux.Time
nt.Valid = true
} else {
nt.Time = time.Time{}
nt.Valid = false
}
return nil
}
使用示例:
package main
import (
"encoding/json"
"fmt"
"your-project/database"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
DeletedAt database.NullTime `json:"deleted_at"`
}
func main() {
// 测试无效时间
user1 := User{
ID: 1,
Name: "John",
DeletedAt: database.NullTime{},
}
json1, _ := json.MarshalIndent(user1, "", " ")
fmt.Println("无效时间输出:")
fmt.Println(string(json1))
// 测试有效时间
user2 := User{
ID: 2,
Name: "Jane",
DeletedAt: database.NullTime{
NullTime: sql.NullTime{
Time: time.Date(2024, 1, 15, 14, 30, 0, 0, time.UTC),
Valid: true,
},
},
}
json2, _ := json.MarshalIndent(user2, "", " ")
fmt.Println("\n有效时间输出:")
fmt.Println(string(json2))
}
输出结果:
无效时间输出:
{
"id": 1,
"name": "John",
"deleted_at": {
"Time": "",
"Valid": false
}
}
有效时间输出:
{
"id": 2,
"name": "Jane",
"deleted_at": {
"Time": "2024-01-15T14:30:00Z",
"Valid": true
}
}
这个实现的关键点:
- 自定义
MarshalJSON方法,当Valid为false时返回空字符串 - 保持了
Scan方法的原有逻辑 - 添加了
UnmarshalJSON方法以支持双向序列化 - 有效时间仍然正常序列化为标准时间格式

