feat(types):增加日期时间类型。
This commit is contained in:
		| @@ -20,6 +20,7 @@ Golang 中可以使用的常用辅助功能工具箱。主要配备以下功能 | ||||
|     - [ ] 1024 位长 | ||||
|     - [ ] 2048 位长 | ||||
|     - [ ] KeyPair 生成器 | ||||
|   - [ ] RSA 签名算法 | ||||
| - 散列算法。 | ||||
|   - [ ] Sha512 散列算法 | ||||
|   - [ ] Sha256 散列算法 | ||||
| @@ -30,8 +31,6 @@ Golang 中可以使用的常用辅助功能工具箱。主要配备以下功能 | ||||
|   - [ ] 冰雹 ID 生成器(短主机精简日期版雪花 ID) | ||||
|   - [ ] UUID 生成器 | ||||
|   - [ ] short UUID 生成器 | ||||
| - 签名算法 | ||||
|   - [ ] RSA 签名算法 | ||||
| - 验证码生成器 | ||||
|   - [ ] 随机验证码生成算法 | ||||
| - 序列化算法 | ||||
|   | ||||
							
								
								
									
										7
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,3 +1,10 @@ | ||||
| module archgrid.xyz/ag/toolsbox | ||||
|  | ||||
| go 1.20 | ||||
|  | ||||
| require go.uber.org/zap v1.24.0 | ||||
|  | ||||
| require ( | ||||
| 	go.uber.org/atomic v1.7.0 // indirect | ||||
| 	go.uber.org/multierr v1.6.0 // indirect | ||||
| ) | ||||
|   | ||||
							
								
								
									
										11
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= | ||||
| go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= | ||||
| go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= | ||||
| go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= | ||||
| go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= | ||||
| go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= | ||||
							
								
								
									
										159
									
								
								types/date.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								types/date.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,159 @@ | ||||
| package types | ||||
|  | ||||
| import ( | ||||
| 	"database/sql" | ||||
| 	"database/sql/driver" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
|  | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	dateLayouts = []string{ | ||||
| 		"2006-01-02", "2006-1-2", "2006/01/02", "06-1-2", "6-01-02", "01/02/06", "1/2/06", "2006年01月02日", "06年1月2日", | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| // 封装日期类型。 | ||||
| type Date struct { | ||||
| 	time.Time | ||||
| } | ||||
|  | ||||
| // 根据给定的年月日创建一个日期类型实例。 | ||||
| func NewDate(year int, month time.Month, day int) Date { | ||||
| 	return Date{ | ||||
| 		Time: time.Date(year, month, day, 0, 0, 0, 0, loc), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 创建一个新的空白日期类型实例。 | ||||
| func NewEmptyDate() Date { | ||||
| 	return Date{ | ||||
| 		Time: time.Time{}.In(loc), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 获取当前日期。 | ||||
| func NowDate() Date { | ||||
| 	return Now().Date() | ||||
| } | ||||
|  | ||||
| // 从给定的字符串中解析日期。如果无法解析则返回错误。 | ||||
| func ParseDate(t string) (Date, error) { | ||||
| 	if len(t) == 0 { | ||||
| 		return NewEmptyDate(), fmt.Errorf("不能解析空白的日期时间。") | ||||
| 	} | ||||
| 	for _, layout := range dateLayouts { | ||||
| 		d, err := time.ParseInLocation(layout, t, loc) | ||||
| 		if err == nil { | ||||
| 			return Date{ | ||||
| 				Time: d, | ||||
| 			}, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return NewEmptyDate(), fmt.Errorf("无法解析给定的日期,格式不正确。") | ||||
| } | ||||
|  | ||||
| // 尝试从字符串中解析日期,如果无法解析则返回给定的默认值。 | ||||
| func ParseDateWithDefault(t string, defaultDate Date) Date { | ||||
| 	if len(t) == 0 { | ||||
| 		return defaultDate | ||||
| 	} | ||||
| 	d, err := ParseDate(t) | ||||
| 	if err != nil { | ||||
| 		return defaultDate | ||||
| 	} | ||||
| 	return d | ||||
| } | ||||
|  | ||||
| var _ driver.Valuer = (*Date)(nil) | ||||
|  | ||||
| func (dt Date) Value() (driver.Value, error) { | ||||
| 	return dt.In(loc).Format("2006-01-02"), nil | ||||
| } | ||||
|  | ||||
| var _ sql.Scanner = (*Date)(nil) | ||||
|  | ||||
| func (d *Date) Scan(src interface{}) (err error) { | ||||
| 	switch src := src.(type) { | ||||
| 	case time.Time: | ||||
| 		d.Time = src | ||||
| 	case string: | ||||
| 		t, err := time.ParseInLocation("2006-01-02", src, loc) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		*d = Date{Time: t} | ||||
| 	case []byte: | ||||
| 		d.Time, err = time.ParseInLocation("2006-01-02", string(src), loc) | ||||
| 		return err | ||||
| 	case nil: | ||||
| 		d = nil | ||||
| 	default: | ||||
| 		return fmt.Errorf("该数据类型不支持解析到日期: %T", src) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| var _ json.Marshaler = (*Date)(nil) | ||||
|  | ||||
| func (d Date) MarshalJSON() ([]byte, error) { | ||||
| 	return json.Marshal(d.Format("2006-01-02")) | ||||
| } | ||||
|  | ||||
| var _ json.Unmarshaler = (*Date)(nil) | ||||
|  | ||||
| func (d *Date) 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", str, loc) | ||||
| 	d.Time = t | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // 计算两个日期之间的月份差。 | ||||
| func (d Date) DifferenceInMonth(d2 *Date) int { | ||||
| 	var differYear, differMonth int | ||||
| 	differYear = d.Year() - d2.Year() | ||||
| 	differMonth = int(d.Month() - d2.Month()) | ||||
| 	return differYear*12 + differMonth | ||||
| } | ||||
|  | ||||
| // 判断给定日期是否是当前日期的下一个月。 | ||||
| func (d Date) IsNextMonth(d2 *Date) bool { | ||||
| 	return d.DifferenceInMonth(d2) == 1 | ||||
| } | ||||
|  | ||||
| // 获取当前日期的最开始时间。 | ||||
| func (d Date) ToBeginningOfDate() DateTime { | ||||
| 	return FromTime(time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, loc)) | ||||
| } | ||||
|  | ||||
| // 获取当前日期的最末尾时间。 | ||||
| func (d Date) ToEndingOfDate() DateTime { | ||||
| 	return FromTime(time.Date(d.Year(), d.Month(), d.Day(), 23, 59, 59, 999999, loc)) | ||||
| } | ||||
|  | ||||
| // 判断当前日期是否为空白日期。 | ||||
| func (d Date) IsEmpty() bool { | ||||
| 	return d.Time.IsZero() | ||||
| } | ||||
|  | ||||
| // 使用`YYYY-MM-DD`格式输出日期。 | ||||
| func (d Date) ToString() string { | ||||
| 	return d.Time.Format("2006-01-02") | ||||
| } | ||||
|  | ||||
| // 将当前日期转换为日期时间类型。 | ||||
| func (d Date) ToDateTime() DateTime { | ||||
| 	return FromTime(d.Time) | ||||
| } | ||||
|  | ||||
| // 在Zap日志中以给定的`fieldName`为字段名输出日期。 | ||||
| func (d Date) Log(fieldName string) zap.Field { | ||||
| 	return zap.String(fieldName, d.ToString()) | ||||
| } | ||||
							
								
								
									
										167
									
								
								types/datetime.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								types/datetime.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | ||||
| 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()) | ||||
| } | ||||
							
								
								
									
										2
									
								
								types/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								types/types.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| // 定义常用的数据类型,包括数据类型在数据库内的存储和读取,以及数据类型在JSON格式中的输出与解析。 | ||||
| package types | ||||
		Reference in New Issue
	
	Block a user