forked from free-lancers/electricity_bill_calc_service
		
	feat(types):增加日期时间的范围区间类型。
This commit is contained in:
		| @@ -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
									
								
							
							
						
						
									
										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 { | ||||
| 	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
									
								
							
							
						
						
									
										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() | ||||
| } | ||||
		Reference in New Issue
	
	Block a user