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
  }
}

这个实现的关键点:

  1. 自定义 MarshalJSON 方法,当 Validfalse 时返回空字符串
  2. 保持了 Scan 方法的原有逻辑
  3. 添加了 UnmarshalJSON 方法以支持双向序列化
  4. 有效时间仍然正常序列化为标准时间格式
回到顶部