timex 处理时间戳

image
date.go

package timex

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

type Date time.Time

func (t *Date) UnmarshalJSON(data []byte) (err error) {
	dataTime, err := time.ParseInLocation(`"`+DateFormatter+`"`, string(data), time.Local)
	*t = Date(dataTime)
	return
}

func (t Date) MarshalJSON() ([]byte, error) {
	b := make([]byte, 0, len(DateFormatter)+2)
	b = append(b, '"')
	b = time.Time(t).AppendFormat(b, DateFormatter)
	b = append(b, '"')
	return b, nil
}

// Scan valueOf time.Time
func (t *Date) Scan(v any) error {
	value, ok := v.(time.Time)
	if ok {
		*t = Date(value)
		return nil
	}
	return fmt.Errorf("can not convert %T:%v to timestamp", v, v)
}

func (t Date) Value() (driver.Value, error) {
	var zeroTime time.Time
	if t.UnixNano() == zeroTime.UnixNano() {
		return nil, nil
	}
	return time.Time(t), nil
}

func (t Date) Unix() int64 {
	return time.Time(t).Unix()
}

func (t Date) UnixMilli() int64 {
	return time.Time(t).UnixMilli()
}

func (t Date) UnixMicro() int64 {
	return time.Time(t).UnixMicro()
}

func (t Date) UnixNano() int64 {
	return time.Time(t).UnixNano()
}

func (t Date) String() string {
	return time.Time(t).Format(DateFormatter)
}

func (t Date) GetTime() time.Time {
	return time.Time(t)
}

func (t Date) Compare(t1 Date) int {
	return time.Time(t).Compare(time.Time(t1))
}

datetime.go

package timex

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

type DateTime time.Time

func (t *DateTime) UnmarshalJSON(data []byte) (err error) {
	dataTime, err := time.ParseInLocation(`"`+DateTimeFormatter+`"`, string(data), time.Local)
	*t = DateTime(dataTime)
	return
}

func (t DateTime) MarshalJSON() ([]byte, error) {
	b := make([]byte, 0, len(DateTimeFormatter)+2)
	b = append(b, '"')
	b = time.Time(t).AppendFormat(b, DateTimeFormatter)
	b = append(b, '"')
	return b, nil
}

// Scan valueOf time.Time
func (t *DateTime) Scan(v any) error {
	value, ok := v.(time.Time)
	if ok {
		*t = DateTime(value)
		return nil
	}
	return fmt.Errorf("can not convert %T:%v to timestamp", v, v)
}

func (t DateTime) Value() (driver.Value, error) {
	var zeroTime time.Time
	if t.UnixNano() == zeroTime.UnixNano() {
		return nil, nil
	}
	return time.Time(t), nil
}

func (t DateTime) Unix() int64 {
	return time.Time(t).Unix()
}

func (t DateTime) UnixMilli() int64 {
	return time.Time(t).UnixMilli()
}

func (t DateTime) UnixMicro() int64 {
	return time.Time(t).UnixMicro()
}

func (t DateTime) UnixNano() int64 {
	return time.Time(t).UnixNano()
}

func (t DateTime) String() string {
	if time.Time(t).IsZero() {
		return ""
	}
	return time.Time(t).Format(DateTimeFormatter)
}

func (t DateTime) GetTime() time.Time {
	return time.Time(t)
}

func (t DateTime) Nullable() NullDateTime {
	timeObj := time.Time(t)
	if timeObj.IsZero() {
		return NullDateTime(sql.NullTime{
			Time:  timeObj,
			Valid: false,
		})
	}

	return NullDateTime(sql.NullTime{
		Time:  timeObj,
		Valid: true,
	})
}

func (t DateTime) Compare(t1 DateTime) int {
	return time.Time(t).Compare(time.Time(t1))
}

formatters.go

package timex

const (
	DateTimeFormatter           = "2006-01-02 15:04:05"
	DateTimeMilliFormatter      = "2006-01-02 15:04:05.000"
	DateTimeMicroFormatter      = "2006-01-02 15:04:05.000000"
	DateFormatter               = "2006-01-02"
	DateNumberFormatter         = "20060102"
	DateShortNumberFormatter    = "060102"
	DateTimeNumFormatter        = "20060102150405"
	ISO8601DateTimeNumFormatter = "20060102T150405"
)

nullable_datetime.go

package timex

import (
	"database/sql"
	"database/sql/driver"
	"encoding/json"
	"time"
)

type NullDateTime sql.NullTime

func (t NullDateTime) MarshalJSON() ([]byte, error) {
	if !t.Valid {
		return []byte("null"), nil
	}

	b := make([]byte, 0, len(DateTimeFormatter)+2)
	b = append(b, '"')
	b = sql.NullTime(t).Time.AppendFormat(b, DateTimeFormatter)
	b = append(b, '"')
	return b, nil
}

func (t *NullDateTime) UnmarshalJSON(b []byte) error {
	if string(b) == "null" {
		t.Valid = false
		return nil
	}
	err := json.Unmarshal(b, &t.Time)
	if err == nil {
		t.Valid = true
	}
	return err
}

// Scan valueOf time.Time
func (t *NullDateTime) Scan(v any) error {
	return (*sql.NullTime)(t).Scan(v)
}

func (t NullDateTime) Value() (driver.Value, error) {
	if !t.Valid {
		return nil, nil
	}
	return t.Time, nil
}

func (t NullDateTime) Unix() int64 {
	if t.Valid {
		return t.Time.Unix()
	}

	return 0
}

func (t NullDateTime) UnixMilli() int64 {
	if t.Valid {
		return t.Time.UnixMilli()
	}

	return 0
}

func (t NullDateTime) UnixMicro() int64 {
	if t.Valid {
		return t.Time.UnixMicro()
	}

	return 0
}

func (t NullDateTime) UnixNano() int64 {
	if t.Valid {
		return t.Time.UnixNano()
	}

	return 0
}

func (t NullDateTime) String() string {
	if sql.NullTime(t).Time.IsZero() {
		return ""
	}
	return sql.NullTime(t).Time.Format(DateTimeFormatter)
}

func (t NullDateTime) GetTime() time.Time {
	return t.Time
}

func (t NullDateTime) Compare(t1 NullDateTime) int {
	return t.Time.Compare(t1.Time)
}

timestamp.go

package timex

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

type TimeStampType string

const (
	TimeStampTypeSec   TimeStampType = "sec"
	TimeStampTypeMilli TimeStampType = "milli"
	TimeStampTypeMicro TimeStampType = "micro"
)

// TimeStamp support second or millisecond precision
type TimeStamp struct {
	Time  time.Time
	Stamp int64
	Type  TimeStampType
	Valid bool // Valid is true if Time is not NULL
}

func (t *TimeStamp) UnmarshalJSON(stampByte []byte) (err error) {
	*t = TimeStamp{}
	if string(stampByte) == "null" {
		return nil
	}

	stamp := string(stampByte)
	stampLen := len(stamp)
	t.Stamp, err = strconv.ParseInt(stamp, 10, 64)
	if err != nil {
		return
	}

	switch {
	case stampLen <= 10:
		t.Time = time.Unix(t.Stamp, 0)
		t.Type = TimeStampTypeSec
	case stampLen == 13:
		t.Time = time.UnixMilli(t.Stamp)
		t.Type = TimeStampTypeMilli
	default:
		t.Time = time.UnixMicro(t.Stamp)
		t.Type = TimeStampTypeMicro
	}
	t.Valid = true
	return
}

func (t TimeStamp) MarshalJSON() ([]byte, error) {
	return []byte(strconv.FormatInt(t.Time.Unix(), 10)), nil
}

// Scan valueOf int
func (t *TimeStamp) Scan(v any) error {
	*t = TimeStamp{}

	ts, ok := v.(int64)
	if !ok {
		return fmt.Errorf("can not convert %T:%v to timestamp", v, v)
	}
	if ts <= 0 {
		t.Time = time.Time{}
		t.Type = TimeStampTypeSec
		t.Stamp = ts
		t.Valid = false
	}

	stamp := strconv.FormatInt(ts, 10)
	strLen := len(stamp)
	switch {
	case strLen <= 10:
		t.Time = time.Unix(ts, 0)
		t.Type = TimeStampTypeSec
	case strLen == 13:
		t.Time = time.UnixMilli(ts)
		t.Type = TimeStampTypeMilli
	default:
		t.Time = time.UnixMicro(ts)
		t.Type = TimeStampTypeMicro
	}
	t.Stamp = ts
	t.Valid = true
	return nil
}

func (t TimeStamp) Value() (driver.Value, error) {
	if !t.Valid || t.Time.IsZero() {
		return nil, nil
	}
	return t.Time.Unix(), nil
}

func (t TimeStamp) Unix() int64 {
	if t.Valid {
		return t.Time.Unix()
	}
	return 0
}

func (t TimeStamp) UnixMilli() int64 {
	if t.Valid {
		return t.Time.UnixMilli()
	}

	return 0
}

func (t TimeStamp) UnixMicro() int64 {
	if t.Valid {
		return t.Time.UnixMicro()
	}

	return 0
}

func (t TimeStamp) UnixNano() int64 {
	if t.Valid {
		return t.Time.UnixNano()
	}

	return 0
}

func (t TimeStamp) String() string {
	if !t.Valid || t.Time.IsZero() {
		return ""
	}

	switch t.Type {
	case TimeStampTypeSec:
		return t.Time.Format(DateTimeFormatter)
	case TimeStampTypeMilli:
		return t.Time.Format(DateTimeMilliFormatter)
	case TimeStampTypeMicro:
		fallthrough
	default:
		return t.Time.Format(DateTimeMicroFormatter)
	}
}

func (t TimeStamp) GetTime() time.Time {
	return t.Time
}

func (t TimeStamp) Compare(t1 TimeStamp) int {
	return t.Time.Compare(t1.Time)
}

timex.go

package timex

import "time"

type Timex[T any] interface {
	Unix() int64
	UnixMilli() int64
	UnixMicro() int64
	UnixNano() int64
	String() string
	GetTime() time.Time
	Compare(T) int
}

timex_utils.go

package timex

import (
	"database/sql"
	"time"
)

type TimeCmd struct {
	t time.Time
}

func (cmd TimeCmd) DateTime() DateTime {
	return DateTime(cmd.t)
}

func (cmd TimeCmd) Date() Date {
	return Date(cmd.t)
}

func (cmd TimeCmd) NullDateTime() NullDateTime {
	return NullDateTime(
		sql.NullTime{
			Time:  cmd.t,
			Valid: true,
		})
}

func (cmd TimeCmd) TimeStamp() TimeStamp {
	return TimeStamp{
		Time:  cmd.t,
		Stamp: cmd.t.Unix(),
		Type:  TimeStampTypeSec,
		Valid: true,
	}
}

func (cmd TimeCmd) StartOfDay() DateTime {
	t := cmd.t
	return DateTime(time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()))
}

func (cmd TimeCmd) EndOfDay() DateTime {
	t := cmd.t
	return DateTime(time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, t.Location()))
}

func (cmd TimeCmd) Unix() int64 {
	return cmd.t.Unix()
}

func (cmd TimeCmd) UnixMilli() int64 {
	return cmd.t.UnixMilli()
}

func (cmd TimeCmd) UnixMicro() int64 {
	return cmd.t.UnixMicro()
}

func (cmd TimeCmd) UnixNano() int64 {
	return cmd.t.UnixNano()
}

func (cmd TimeCmd) Format(formatter string) string {
	return cmd.t.Format(formatter)
}

func (cmd TimeCmd) FormatDateTime() string {
	return cmd.t.Format(DateTimeFormatter)
}

func (cmd TimeCmd) FormatDate() string {
	return cmd.t.Format(DateFormatter)
}

func (cmd TimeCmd) FormatDateTimeNum() string {
	return cmd.t.Format(DateTimeNumFormatter)
}

func Now() TimeCmd {
	return TimeCmd{t: time.Now()}
}

func Yesterday() TimeCmd {
	return TimeCmd{t: time.Now().AddDate(0, 0, -1)}
}

func Tomorrow() TimeCmd {
	return TimeCmd{t: time.Now().AddDate(0, 0, 1)}
}

func StartOfToday() TimeCmd {
	now := time.Now()
	return TimeCmd{t: time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())}
}

func EndOfToday() TimeCmd {
	now := time.Now()
	return TimeCmd{t: time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location())}
}

func FromTime(t time.Time) TimeCmd {
	return TimeCmd{t: t}
}

func FromFormatterStr(timeStr string, formatters ...string) TimeCmd {
	formatter := time.RFC3339
	if len(formatters) > 0 {
		formatter = formatters[0]
	}
	t, err := time.Parse(formatter, timeStr)
	if err != nil {
		t = time.Time{}
	}

	return TimeCmd{t: t}
}

func FromUnix(second int64) TimeCmd {
	return TimeCmd{t: time.Unix(second, 0)}
}

func FromUnixMilli(ms int64) TimeCmd {
	return TimeCmd{t: time.UnixMilli(ms)}
}

func FromUnixMicro(ms int64) TimeCmd {
	return TimeCmd{t: time.UnixMicro(ms)}
}

func AfterOrEqual(t1, t2 time.Time) bool {
	return t1.After(t2) || t1.Equal(t2)
}

func BeforeOrEqual(t1, t2 time.Time) bool {
	return t1.Before(t2) || t1.Equal(t2)
}

ts.go

package timex

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

type Ts int64

func (t Ts) GetType() TimeStampType {
	val := t.GetValue()
	switch {
	case val < 1e11:
		return TimeStampTypeSec
	case val < 1e14:
		return TimeStampTypeMilli
	default:
		return TimeStampTypeMicro
	}
}

// UnmarshalJSON 从 JSON 字节数组解析时间戳
func (t *Ts) UnmarshalJSON(stampByte []byte) error {
	stamp := string(stampByte)
	if stamp == "null" {
		return nil
	}

	ts, err := strconv.ParseInt(stamp, 10, 64)
	if err != nil {
		return err
	}

	*t = Ts(ZeroOrTs(ts))
	return nil
}

// MarshalJSON 将时间戳转换为 JSON 格式
func (t Ts) MarshalJSON() ([]byte, error) {
	return []byte(strconv.FormatInt(t.GetValue(), 10)), nil
}

// Scan 从数据库值中扫描时间戳
func (t *Ts) Scan(v any) error {
	ts, ok := v.(int64)
	if !ok {
		return fmt.Errorf("can not convert %T:%v to timestamp", v, v)
	}

	*t = Ts(ZeroOrTs(ts))
	return nil
}

// Value 将时间戳转换为数据库值
func (t Ts) Value() (driver.Value, error) {
	return t.GetValue(), nil
}

// Unix 返回秒级时间戳
func (t Ts) Unix() int64 {
	return t.GetTime().Unix()
}

// UnixMilli 返回毫秒级时间戳
func (t Ts) UnixMilli() int64 {
	return t.GetTime().UnixMilli()
}

// UnixMicro 返回微秒级时间戳
func (t Ts) UnixMicro() int64 {
	return t.GetTime().UnixMicro()
}

// UnixNano 返回纳秒级时间戳
func (t Ts) UnixNano() int64 {
	return t.GetTime().UnixNano()
}

// String 返回时间戳的字符串表示
func (t Ts) String() string {
	if !t.IsValid() {
		return "0"
	}

	time := t.GetTime()
	switch t.GetType() {
	case TimeStampTypeSec:
		return time.Format(DateTimeFormatter)
	case TimeStampTypeMilli:
		return time.Format(DateTimeMilliFormatter)
	case TimeStampTypeMicro:
		fallthrough
	default:
		return time.Format(DateTimeMicroFormatter)
	}
}

func (t Ts) GetTime() time.Time {
	switch t.GetType() {
	case TimeStampTypeSec:
		return time.Unix(t.GetValue(), 0)
	case TimeStampTypeMilli:
		return time.UnixMilli(t.GetValue())
	case TimeStampTypeMicro:
		return time.UnixMicro(t.GetValue())
	default:
		return time.Time{}
	}
}

func (t Ts) IsValid() bool {
	return t > 0
}

func (t Ts) GetValue() int64 {
	return ZeroOrTs(int64(t))
}

func ZeroOrTs(ts int64) int64 {
	if ts <= 0 {
		// TODO: 目前不处理负数时间戳,后续可以优化
		ts = 0
	}

	return ts
}

func (t Ts) Compare(t1 Ts) int {
	return t.GetTime().Compare(t1.GetTime())
}

type TsMilli Ts

// MarshalJSON 将时间戳转换为 JSON 格式
func (t TsMilli) MarshalJSON() ([]byte, error) {
	return []byte(strconv.FormatInt(Ts(t).UnixMilli(), 10)), nil
}

// Value 将时间戳转换为数据库值
func (t TsMilli) Value() (driver.Value, error) {
	return Ts(t).UnixMilli(), nil
}

// String 返回时间戳的字符串表示
func (t TsMilli) String() string {
	if t <= 0 {
		return "0"
	}

	return (Ts(t).GetTime()).Format(DateTimeMilliFormatter)
}

type TsMicro Ts

// MarshalJSON 将时间戳转换为 JSON 格式
func (t TsMicro) MarshalJSON() ([]byte, error) {
	return []byte(strconv.FormatInt(Ts(t).UnixMicro(), 10)), nil
}

// Value 将时间戳转换为数据库值
func (t TsMicro) Value() (driver.Value, error) {
	return Ts(t).UnixMicro(), nil
}

// String 返回时间戳的字符串表示
func (t TsMicro) String() string {
	if t <= 0 {
		return "0"
	}

	return (Ts(t).GetTime()).Format(DateTimeMicroFormatter)
}

posted @ 2026-06-04 11:43  干炸小黄鱼  阅读(4)  评论(0)    收藏  举报