package utils import ( "database/sql/driver" "fmt" "strings" "time" "gorm.io/gorm" "gorm.io/gorm/schema" ) var AsiaShanghai *time.Location func init() { var err error AsiaShanghai, err = time.LoadLocation("Asia/Shanghai") if err != nil { AsiaShanghai = time.FixedZone("CST", 8*3600) } } // CustomTime 自定义时间类型 type CustomTime struct { time.Time } // Now 获取当前时间 func Now() CustomTime { return CustomTime{time.Now().In(AsiaShanghai)} } // Format 返回 yyyy-mm-dd H:i:s 格式字符串 func (ct CustomTime) Format(layouts ...string) string { if ct.IsZero() { return "" } layout := time.DateTime // 默认格式:2006-01-02 15:04:05 if len(layouts) > 0 && layouts[0] != "" { layout = layouts[0] } return ct.Time.Format(layout) } // String 实现 Stringer 接口 // func (ct CustomTime) String() string { // return ct.Format() // } func (ct CustomTime) String() string { if ct.Time.IsZero() { return "" } return ct.Time.Format(time.DateTime) } // MarshalJSON 实现 JSON 序列化 func (ct CustomTime) MarshalJSON() ([]byte, error) { if ct.IsZero() { return []byte("null"), nil } return []byte(`"` + ct.Format() + `"`), nil } // UnmarshalJSON 实现 JSON 反序列化 func (ct *CustomTime) UnmarshalJSON(data []byte) error { str := string(data) if str == "null" || str == `""` { ct.Time = time.Time{} return nil } // 去除引号 str = strings.Trim(str, `"`) // 尝试解析多种格式 formats := []string{ "2006-01-02", "2006-01-02 15:04:05", "2006-01-02T15:04:05Z07:00", time.RFC3339, time.RFC3339Nano, } var err error for _, layout := range formats { ct.Time, err = time.Parse(layout, str) if err == nil { return nil } } return fmt.Errorf("invalid time format: %s", str) } // Value 实现 driver.Valuer 接口 (数据库写入) func (ct CustomTime) Value() (driver.Value, error) { if ct.IsZero() { return nil, nil } return ct.Time, 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 time.Time: ct.Time = v return nil case []byte: return ct.UnmarshalJSON(v) case string: return ct.UnmarshalJSON([]byte(v)) default: return fmt.Errorf("unsupported type: %T", value) } } // GormDataType 定义 GORM 数据库类型 func (CustomTime) GormDataType() string { return "datetime" } // GormDBDataType 定义 GORM 数据库类型 (v2 版本) func (CustomTime) GormDBDataType(db *gorm.DB, field *schema.Field) string { switch db.Dialector.Name() { case "mysql", "sqlite": return "datetime" case "postgres": return "timestamp" default: return "datetime" } } func ParseTime(s string) (time.Time, error) { formats := []string{ "2006-01-02", "2006-01-02 15:04:05", } var t time.Time var err error for _, layout := range formats { t, err = time.ParseInLocation(layout, s, time.Local) if err == nil { return t, nil } } return time.Time{}, fmt.Errorf("invalid time format: %s", s) } func dayBoundary(s string, h, m, sec int) (CustomTime, error) { t, err := ParseTime(s) if err != nil { return CustomTime{}, err } return CustomTime{ Time: time.Date(t.Year(), t.Month(), t.Day(), h, m, sec, 0, t.Location()), }, nil } func TimeStart(day int, timeStr string) string { // 2. 格式化开始时间:前一天 23:59:59 startTime, err := time.Parse(time.DateOnly, timeStr) if err == nil { startTime = startTime.Add(time.Duration(-24*day) * time.Hour) // 减N天 startResult := startTime.Format(time.DateOnly) return startResult + " 23:59:59" } return "" } func TimeEnd(day int, timeStr string) string { endTime, err := time.Parse(time.DateOnly, timeStr) if err == nil { endTime = endTime.Add(time.Duration(+24*day) * time.Hour) // 加一天 endResult := endTime.Format(time.DateTime) return endResult } return "" } func TimeToUnix(timeStr string, layout string) (int64, error) { t, err := time.ParseInLocation(layout, timeStr, time.FixedZone("CST", 8*3600)) if err != nil { Logger.Error(err) return 0, err } // 转 11 位时间戳(秒) return t.Unix(), nil } func ParseCustomTime(s string) (CustomTime, error) { ct := CustomTime{} // UnmarshalJSON 需要带双引号的json字符串,所以包装成json字符串格式 err := ct.UnmarshalJSON([]byte(`"` + s + `"`)) if err != nil { return CustomTime{}, err } return ct, nil }