package types import ( "database/sql" "database/sql/driver" "encoding/json" "fmt" "time" "go.uber.org/zap" ) var ( loc *time.Location = time.FixedZone("+0800", 8*60*60) datetimeLayouts = []string{ "2006-01-02 15:04:05", "2006-1-2 15:04:05", "2006/01/02 15:04:05", "06-1-2 15:04:05", "06-01-02 15:04:05", "01/02/06 15:04:05", "1/2/06 15:04:05", "2006年01月02日 15:04:05", "06年1月2日 15:04:05", "2006-01-02 15:04", "2006-1-2 15:04", "2006/01/02 15:04", "06-1-2 15:04", "06-01-02 15:04", "01/02/06 15:04", "1/2/06 15:04", "2006年01月02日 15:04", "06年1月2日 15:04", } ) // 封装日期时间类型。 type DateTime struct { time.Time } // 获取当前的日期时间。 func Now() DateTime { return DateTime{ Time: time.Now().In(loc), } } // 创建一个空白的日期时间类型实例。 func NewEmptyDateTime() DateTime { return DateTime{ Time: time.Time{}.In(loc), } } // 获取一个自`2022-02-22 22:22:22.0`以来的秒数时间戳。 func Timestamp() int64 { startline := time.Date(2022, 2, 22, 22, 22, 22, 0, loc).Unix() return Now().Unix() - startline } // 从指定的字符串中解析日期时间。如果无法解析则返回错误。 func ParseDateTime(t string) (DateTime, error) { if len(t) == 0 { return NewEmptyDateTime(), fmt.Errorf("不能解析空白的日期时间。") } for _, layout := range datetimeLayouts { fmt.Printf("Parse: %s, Try layout: %s\n", t, layout) d, err := time.ParseInLocation(layout, t, loc) if err == nil { return DateTime{ Time: d, }, nil } } return NewEmptyDateTime(), fmt.Errorf("无法解析给定的日期时间,格式不正确。") } // 封装一个标准库中的时间类型。 func FromTime(t time.Time) DateTime { return DateTime{ Time: t, } } // 解析一个Unix时间戳。 func FromUnixMicro(sec int64) DateTime { return DateTime{ Time: time.UnixMicro(sec).In(loc), } } var _ driver.Valuer = (*DateTime)(nil) func (dt DateTime) Value() (driver.Value, error) { return dt.In(loc).Format("2006-01-02 15:04:05"), nil } var _ sql.Scanner = (*DateTime)(nil) func (dt *DateTime) Scan(src interface{}) (err error) { switch src := src.(type) { case time.Time: dt.Time = src case string: t, err := time.ParseInLocation("2006-01-02 15:04:05", src, loc) if err != nil { return err } *dt = DateTime{Time: t} case []byte: dt.Time, err = time.ParseInLocation("2006-01-02 15:04:05", string(src), loc) return err case nil: dt = nil default: return fmt.Errorf("该数据类型不支持解析到日期时间: %T", src) } return nil } var _ json.Marshaler = (*DateTime)(nil) func (dt DateTime) MarshalJSON() ([]byte, error) { return json.Marshal(dt.Format("2006-01-02 15:04:05")) } var _ json.Unmarshaler = (*DateTime)(nil) func (dt *DateTime) UnmarshalJSON(data []byte) error { var str string if err := json.Unmarshal(data, &str); err != nil { return fmt.Errorf("不能解析指定的日期时间值: %w", err) } t, err := time.ParseInLocation("2006-01-02 15:04:05", str, loc) dt.Time = t return err } // 计算给定日期时间与当前日期时间的月份差值。 func (dt DateTime) DifferenceInMonth(d DateTime) int { var differYear, differMonth int differYear = dt.Year() - d.Year() differMonth = int(dt.Month() - d.Month()) return differYear*12 + differMonth } // 判断给定日期时间是否是当前日期时间的下一个月。 func (dt DateTime) IsNextMonth(target DateTime) bool { return dt.DifferenceInMonth(target) == 1 } // 将当前日期时间调整为当天的开始时间。 func (dt *DateTime) ToBeginningOfDate() { dt.Time = time.Date(dt.Year(), dt.Month(), dt.Day(), 0, 0, 0, 0, loc) } // 将当前日期时间调整为当天的结束时间。 func (dt *DateTime) ToEndingOfDate() { dt.Time = time.Date(dt.Year(), dt.Month(), dt.Day(), 23, 59, 59, 999999, loc) } // 从当前日期时间中获取日期部分。 func (dt DateTime) Date() Date { return Date{ Time: time.Date(dt.Year(), dt.Month(), dt.Day(), 0, 0, 0, 0, loc), } } // 判断当前日期时间是否为空。 func (dt DateTime) IsEmpty() bool { return dt.Time.IsZero() } // 以`YYYY-MM-DD HH:mm:ss`格式输出日期时间。 func (dt DateTime) ToString() string { return dt.Time.Format("2006-01-02 15:04:05") } // 在Zap日志中以给定`fieldName`为字段名输出日期时间。 func (dt DateTime) Log(fieldName string) zap.Field { return zap.String(fieldName, dt.ToString()) }