feat(types):增加日期时间的范围区间类型。

This commit is contained in:
徐涛 2023-06-16 08:25:01 +08:00
parent 2d6bff5828
commit 2c303cfba7
5 changed files with 305 additions and 0 deletions

View File

@ -153,6 +153,15 @@ func (d Date) IsEmpty() bool {
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 {
return d.Time.Format("2006-01-02")
}

80
types/daterange.go Normal file
View 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
}

View File

@ -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 {
startline := time.Date(2022, 2, 22, 22, 22, 22, 0, loc).Unix()
return Now().Unix() - startline
@ -159,6 +171,15 @@ func (dt DateTime) IsEmpty() bool {
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 {
return dt.Time.Format("2006-01-02 15:04:05")
}

80
types/datetimerange.go Normal file
View 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
View 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()
}