Golang中如何配置GORM以特定格式在SQLite中存储日期

Golang中如何配置GORM以特定格式在SQLite中存储日期 我有这个表:

type Table struct {
   Date time.Time `gorm:"column:date;type:datetime;default:current_timestamp;not null"`
}

但它是以这种格式存储的: 2023-10-25 23:36:29.4928236-05:00

我该如何让 gorm 以这种方式存储它? 10-25-2023 23:36:29.4928236-05:00

7 回复

我以为GORM会通过自定义数据类型为我处理这个问题。

更多关于Golang中如何配置GORM以特定格式在SQLite中存储日期的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


是的,我也这么认为……

那么,这意味着如果我想以我偏好的格式存储数据,就必须使用文本类型而不是日期时间类型,对吗?

Sqlite 始终将日期存储为“YYYY-MM-DD HH:MM:SS”格式。因此,我认为问题在于如何以您想要的特定格式显示日期时间,您可以使用 time.Format 来应用您想要的模式。

func main() {
    fmt.Println("hello world")
}

我知道我来晚了……

在我看来,数据库如何存储数据应该无关紧要。任何使用数据的应用程序都有责任为特定用户以正确的格式显示数据,这包括你用来浏览数据库的任何工具(MySQL Workbench、SSMS……)以及你的程序。

所以,简而言之,我认为将日期存储在字符串中是一个非常糟糕的主意……相反,你应该专注于如何正确显示日期,而不是如何正确存储日期。

日期并非以字符串形式存储,那只是默认的显示格式。在后端,你应该始终使用 ISO8601 完整格式,而仅在前端根据不同的区域设置来显示日期或时间戳。

相信我,你绝对不想在后端以那种混乱的传统美国格式来存储日期。

默认格式可以自然排序,并且各个字段的含义是明确无歧义的。

在Golang中使用GORM配置SQLite以特定格式存储日期,可以通过自定义数据类型和序列化/反序列化逻辑实现。以下是实现方案:

import (
    "database/sql/driver"
    "fmt"
    "time"
)

// CustomTime 自定义时间类型
type CustomTime struct {
    time.Time
}

// 自定义格式
const customTimeFormat = "01-02-2006 15:04:05.9999999-07:00"

// Value 实现 driver.Valuer 接口,用于存储到数据库
func (ct CustomTime) Value() (driver.Value, error) {
    if ct.IsZero() {
        return nil, nil
    }
    return ct.Time.Format(customTimeFormat), nil
}

// Scan 实现 sql.Scanner 接口,用于从数据库读取
func (ct *CustomTime) Scan(value interface{}) error {
    if value == nil {
        ct.Time = time.Time{}
        return nil
    }
    
    switch v := value.(type) {
    case []byte:
        return ct.parseTime(string(v))
    case string:
        return ct.parseTime(v)
    case time.Time:
        ct.Time = v
        return nil
    default:
        return fmt.Errorf("无法扫描类型 %T 到 CustomTime", value)
    }
}

// parseTime 解析时间字符串
func (ct *CustomTime) parseTime(str string) error {
    t, err := time.Parse(customTimeFormat, str)
    if err != nil {
        // 尝试其他格式作为后备
        t, err = time.Parse(time.RFC3339Nano, str)
        if err != nil {
            return err
        }
    }
    ct.Time = t
    return nil
}

// 在模型中使用自定义类型
type Table struct {
    Date CustomTime `gorm:"column:date;type:text;default:(strftime('%m-%d-%Y %H:%M:%f', 'now', 'localtime'));not null"`
}

// 如果需要JSON序列化,可以添加MarshalJSON/UnmarshalJSON方法
func (ct CustomTime) MarshalJSON() ([]byte, error) {
    if ct.IsZero() {
        return []byte("null"), nil
    }
    return []byte(fmt.Sprintf(`"%s"`, ct.Time.Format(customTimeFormat))), nil
}

func (ct *CustomTime) UnmarshalJSON(data []byte) error {
    str := string(data)
    if str == "null" || str == `""` {
        ct.Time = time.Time{}
        return nil
    }
    // 移除JSON引号
    if len(str) >= 2 && str[0] == '"' && str[len(str)-1] == '"' {
        str = str[1 : len(str)-1]
    }
    return ct.parseTime(str)
}

// 使用示例
func main() {
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    if err != nil {
        panic(err)
    }
    
    // 自动迁移
    db.AutoMigrate(&Table{})
    
    // 创建记录
    now := CustomTime{Time: time.Now()}
    table := Table{Date: now}
    db.Create(&table)
    
    // 查询记录
    var result Table
    db.First(&result)
    fmt.Printf("存储的时间: %s\n", result.Date.Time.Format(customTimeFormat))
}

对于SQLite的默认值,可以使用SQLite的strftime函数:

type Table struct {
    Date CustomTime `gorm:"column:date;type:text;default:(strftime('%m-%d-%Y %H:%M:%f', 'now', 'localtime'));not null"`
}

如果需要处理时区,可以这样调整:

const customTimeFormatWithTZ = "01-02-2006 15:04:05.9999999-07:00"

// 或者在解析时指定时区
func (ct *CustomTime) parseTime(str string) error {
    loc, _ := time.LoadLocation("Local")
    t, err := time.ParseInLocation(customTimeFormat, str, loc)
    if err != nil {
        t, err = time.Parse(time.RFC3339Nano, str)
        if err != nil {
            return err
        }
    }
    ct.Time = t
    return nil
}

这个实现确保了日期以"MM-DD-YYYY HH:MM:SS.FFFFFFF±HH:MM"格式在SQLite中存储和读取。

回到顶部