feat(types):增加日期时间的范围区间类型。
This commit is contained in:
parent
2d6bff5828
commit
2c303cfba7
|
@ -153,6 +153,15 @@ func (d Date) IsEmpty() bool {
|
||||||
return d.Time.IsZero()
|
return d.Time.IsZero()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Date) Parse(s string) error {
|
||||||
|
t, err := ParseDate(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.Time = t.Time
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d Date) ToString() string {
|
func (d Date) ToString() string {
|
||||||
return d.Time.Format("2006-01-02")
|
return d.Time.Format("2006-01-02")
|
||||||
}
|
}
|
||||||
|
|
80
types/daterange.go
Normal file
80
types/daterange.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"electricity_bill_calc/tools"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DateRange struct {
|
||||||
|
pgtype.Range[Date]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDateRange(lower *Date, upper *Date) DateRange {
|
||||||
|
return DateRange{
|
||||||
|
Range: pgtype.Range[Date]{
|
||||||
|
LowerType: tools.Cond(lower != nil, pgtype.Inclusive, pgtype.Unbounded),
|
||||||
|
Lower: tools.DefaultTo(lower, MinDate()),
|
||||||
|
UpperType: tools.Cond(upper != nil, pgtype.Inclusive, pgtype.Unbounded),
|
||||||
|
Upper: tools.DefaultTo(upper, MaxDate()),
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ driver.Valuer = (*DateRange)(nil)
|
||||||
|
|
||||||
|
func (dr DateRange) Value() (driver.Value, error) {
|
||||||
|
return assembleRange(dr.Range), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ sql.Scanner = (*DateRange)(nil)
|
||||||
|
|
||||||
|
func (dr *DateRange) Scan(src interface{}) (err error) {
|
||||||
|
switch src := src.(type) {
|
||||||
|
case pgtype.Range[Date]:
|
||||||
|
dr.Range = src
|
||||||
|
case string:
|
||||||
|
r, err := destructureToRange[Date](src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dr.Range = r
|
||||||
|
case []byte:
|
||||||
|
r, err := destructureToRange[Date](string(src))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dr.Range = r
|
||||||
|
case nil:
|
||||||
|
dr = nil
|
||||||
|
default:
|
||||||
|
return errors.New("该数据类型不支持解析到日期范围。")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ json.Marshaler = (*DateRange)(nil)
|
||||||
|
|
||||||
|
func (dr DateRange) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(assembleRange(dr.Range))
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ json.Unmarshaler = (*DateRange)(nil)
|
||||||
|
|
||||||
|
func (dr *DateRange) UnmarshalJSON(data []byte) error {
|
||||||
|
var str string
|
||||||
|
if err := json.Unmarshal(data, &str); err != nil {
|
||||||
|
return errors.New("不能解析指定的日期范围值。")
|
||||||
|
}
|
||||||
|
r, err := destructureToRange[Date](str)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dr.Range = r
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -34,6 +34,18 @@ func NewEmptyDateTime() DateTime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MinDateTime() DateTime {
|
||||||
|
return DateTime{
|
||||||
|
Time: time.Date(1, 1, 1, 0, 0, 0, 0, loc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MaxDateTime() DateTime {
|
||||||
|
return DateTime{
|
||||||
|
Time: time.Date(9999, 12, 31, 23, 59, 59, 999999, loc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Timestamp() int64 {
|
func Timestamp() int64 {
|
||||||
startline := time.Date(2022, 2, 22, 22, 22, 22, 0, loc).Unix()
|
startline := time.Date(2022, 2, 22, 22, 22, 22, 0, loc).Unix()
|
||||||
return Now().Unix() - startline
|
return Now().Unix() - startline
|
||||||
|
@ -159,6 +171,15 @@ func (dt DateTime) IsEmpty() bool {
|
||||||
return dt.Time.IsZero()
|
return dt.Time.IsZero()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dt *DateTime) Parse(s string) error {
|
||||||
|
t, err := ParseDateTime(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dt.Time = t.Time
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (dt DateTime) ToString() string {
|
func (dt DateTime) ToString() string {
|
||||||
return dt.Time.Format("2006-01-02 15:04:05")
|
return dt.Time.Format("2006-01-02 15:04:05")
|
||||||
}
|
}
|
||||||
|
|
80
types/datetimerange.go
Normal file
80
types/datetimerange.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"electricity_bill_calc/tools"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DateTimeRange struct {
|
||||||
|
pgtype.Range[DateTime]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDateTimeRange(lower *DateTime, upper *DateTime) DateTimeRange {
|
||||||
|
return DateTimeRange{
|
||||||
|
Range: pgtype.Range[DateTime]{
|
||||||
|
LowerType: tools.Cond(lower != nil, pgtype.Inclusive, pgtype.Unbounded),
|
||||||
|
Lower: tools.DefaultTo(lower, MinDateTime()),
|
||||||
|
UpperType: tools.Cond(upper != nil, pgtype.Inclusive, pgtype.Unbounded),
|
||||||
|
Upper: tools.DefaultTo(upper, MaxDateTime()),
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ driver.Value = (*DateTimeRange)(nil)
|
||||||
|
|
||||||
|
func (dr DateTimeRange) Value() (driver.Value, error) {
|
||||||
|
return assembleRange(dr.Range), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ sql.Scanner = (*DateTimeRange)(nil)
|
||||||
|
|
||||||
|
func (dr *DateTimeRange) Scan(src interface{}) (err error) {
|
||||||
|
switch src := src.(type) {
|
||||||
|
case pgtype.Range[DateTime]:
|
||||||
|
dr.Range = src
|
||||||
|
case string:
|
||||||
|
r, err := destructureToRange[DateTime](src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dr.Range = r
|
||||||
|
case []byte:
|
||||||
|
r, err := destructureToRange[DateTime](string(src))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dr.Range = r
|
||||||
|
case nil:
|
||||||
|
dr = nil
|
||||||
|
default:
|
||||||
|
return errors.New("该数据类型不支持解析到日期范围。")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ json.Marshaler = (*DateTimeRange)(nil)
|
||||||
|
|
||||||
|
func (dr DateTimeRange) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(assembleRange(dr.Range))
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ json.Unmarshaler = (*DateTimeRange)(nil)
|
||||||
|
|
||||||
|
func (dr *DateTimeRange) UnmarshalJSON(data []byte) error {
|
||||||
|
var str string
|
||||||
|
if err := json.Unmarshal(data, &str); err != nil {
|
||||||
|
return errors.New("不能解析指定的日期范围值。")
|
||||||
|
}
|
||||||
|
r, err := destructureToRange[DateTime](str)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dr.Range = r
|
||||||
|
return nil
|
||||||
|
}
|
115
types/range.go
Normal file
115
types/range.go
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Parse interface {
|
||||||
|
Parse(string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToString interface {
|
||||||
|
ToString() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将一个字符串拆解解析为一个 Postgresql 范围类型的值。
|
||||||
|
func destructureToRange[T any, PT interface {
|
||||||
|
Parse
|
||||||
|
*T
|
||||||
|
}](s string) (pgtype.Range[T], error) {
|
||||||
|
var r pgtype.Range[T]
|
||||||
|
r.Valid = false
|
||||||
|
if len(s) == 0 {
|
||||||
|
r.LowerType = pgtype.Empty
|
||||||
|
r.UpperType = pgtype.Empty
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
rangeUnit := strings.Split(s, ",")
|
||||||
|
if len(rangeUnit) != 2 {
|
||||||
|
return r, errors.New("无法解析给定的范围值,格式不正确。")
|
||||||
|
}
|
||||||
|
if unit, found := strings.CutPrefix(rangeUnit[0], "["); found {
|
||||||
|
var t PT
|
||||||
|
if len(unit) > 0 {
|
||||||
|
r.LowerType = pgtype.Inclusive
|
||||||
|
err := t.Parse(unit)
|
||||||
|
if err != nil {
|
||||||
|
return r, errors.New("无法解析给定的最低范围值。")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r.LowerType = pgtype.Unbounded
|
||||||
|
}
|
||||||
|
r.Lower = *t
|
||||||
|
}
|
||||||
|
if unit, found := strings.CutPrefix(rangeUnit[0], "("); found {
|
||||||
|
var t PT
|
||||||
|
if len(unit) > 0 {
|
||||||
|
r.LowerType = pgtype.Exclusive
|
||||||
|
err := t.Parse(unit)
|
||||||
|
if err != nil {
|
||||||
|
return r, errors.New("无法解析给定的最低范围值。")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r.LowerType = pgtype.Unbounded
|
||||||
|
}
|
||||||
|
r.Lower = *t
|
||||||
|
}
|
||||||
|
|
||||||
|
if unit, found := strings.CutSuffix(rangeUnit[1], "]"); found {
|
||||||
|
var t PT
|
||||||
|
if len(unit) > 0 {
|
||||||
|
r.UpperType = pgtype.Inclusive
|
||||||
|
err := t.Parse(unit)
|
||||||
|
if err != nil {
|
||||||
|
return r, errors.New("无法解析给定的最高范围值。")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r.UpperType = pgtype.Unbounded
|
||||||
|
}
|
||||||
|
r.Upper = *t
|
||||||
|
}
|
||||||
|
if unit, found := strings.CutSuffix(rangeUnit[1], ")"); found {
|
||||||
|
var t PT
|
||||||
|
if len(unit) > 0 {
|
||||||
|
r.UpperType = pgtype.Exclusive
|
||||||
|
err := t.Parse(unit)
|
||||||
|
if err != nil {
|
||||||
|
return r, errors.New("无法解析给定的最高范围值。")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r.UpperType = pgtype.Unbounded
|
||||||
|
}
|
||||||
|
r.Upper = *t
|
||||||
|
}
|
||||||
|
r.Valid = true
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将一个范围类型的值转换为一个字符串、
|
||||||
|
func assembleRange[T ToString](r pgtype.Range[T]) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
if r.LowerType == pgtype.Empty || r.UpperType == pgtype.Empty {
|
||||||
|
return "empty"
|
||||||
|
}
|
||||||
|
if r.LowerType == pgtype.Inclusive {
|
||||||
|
sb.WriteString("[")
|
||||||
|
} else {
|
||||||
|
sb.WriteString("(")
|
||||||
|
}
|
||||||
|
if r.LowerType != pgtype.Unbounded {
|
||||||
|
sb.WriteString(r.Lower.ToString())
|
||||||
|
}
|
||||||
|
sb.WriteString(",")
|
||||||
|
if r.UpperType != pgtype.Unbounded {
|
||||||
|
sb.WriteString(r.Upper.ToString())
|
||||||
|
}
|
||||||
|
if r.UpperType == pgtype.Inclusive {
|
||||||
|
sb.WriteString("]")
|
||||||
|
} else {
|
||||||
|
sb.WriteString(")")
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user