mirror of
https://github.com/MontFerret/ferret.git
synced 2025-01-16 03:21:03 +02:00
added DATE_ADD, DATE_SUBTRACT functions (#165)
This commit is contained in:
parent
528b60df24
commit
64ca68d930
@ -2,6 +2,8 @@ package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
//revive:disable-next-line redefines-builtin-id
|
||||
@ -39,6 +41,8 @@ func (t Type) String() string {
|
||||
return typestr[t]
|
||||
}
|
||||
|
||||
// Value represents an interface of
|
||||
// any type that needs to be used during runtime
|
||||
type Value interface {
|
||||
json.Marshaler
|
||||
Type() Type
|
||||
@ -49,10 +53,15 @@ type Value interface {
|
||||
Copy() Value
|
||||
}
|
||||
|
||||
// IsTypeOf return true when value's type
|
||||
// is equal to check type.
|
||||
// Returns false, otherwise.
|
||||
func IsTypeOf(value Value, check Type) bool {
|
||||
return value.Type() == check
|
||||
}
|
||||
|
||||
// ValidateType checks the match of
|
||||
// value's type and required types.
|
||||
func ValidateType(value Value, required ...Type) error {
|
||||
var valid bool
|
||||
ct := value.Type()
|
||||
@ -70,3 +79,26 @@ func ValidateType(value Value, required ...Type) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PairValueType is a supporting
|
||||
// structure that used in validateValueTypePairs.
|
||||
type PairValueType struct {
|
||||
Value Value
|
||||
Types []Type
|
||||
}
|
||||
|
||||
// ValidateValueTypePairs validate pairs of
|
||||
// Values and Types.
|
||||
// Returns error when type didn't match
|
||||
func ValidateValueTypePairs(pairs ...PairValueType) error {
|
||||
var err error
|
||||
|
||||
for idx, pair := range pairs {
|
||||
err = ValidateType(pair.Value, pair.Types...)
|
||||
if err != nil {
|
||||
return errors.Errorf("pair %d: %v", idx, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
121
pkg/stdlib/datetime/add_subtract.go
Normal file
121
pkg/stdlib/datetime/add_subtract.go
Normal file
@ -0,0 +1,121 @@
|
||||
package datetime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
sliceDateTime = []core.Type{core.DateTimeType}
|
||||
sliceIntType = []core.Type{core.IntType}
|
||||
sliceStringType = []core.Type{core.StringType}
|
||||
|
||||
emptyDateTime values.DateTime
|
||||
emptyInt values.Int
|
||||
emptyString values.String
|
||||
)
|
||||
|
||||
// DateAdd add amount given in unit to date.
|
||||
// @params date (DateTime) - source date.
|
||||
// @params amount (Int) - amount of units
|
||||
// @params unit (String) - unit.
|
||||
// @return (DateTime) - calculated date.
|
||||
// The following units are available:
|
||||
// * y, year, year
|
||||
// * m, month, months
|
||||
// * w, week, weeks
|
||||
// * d, day, days
|
||||
// * h, hour, hours
|
||||
// * i, minute, minutes
|
||||
// * s, second, seconds
|
||||
// * f, millisecond, milliseconds
|
||||
func DateAdd(_ context.Context, args ...core.Value) (core.Value, error) {
|
||||
date, amount, unit, err := getArgs(args)
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
dt, err := addUnit(date, int(amount), unit.String())
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
return dt, nil
|
||||
}
|
||||
|
||||
// DateSubtract subtract amount given in unit to date.
|
||||
// @params date (DateTime) - source date.
|
||||
// @params amount (Int) - amount of units
|
||||
// @params unit (String) - unit.
|
||||
// @return (DateTime) - calculated date.
|
||||
// The following units are available:
|
||||
// * y, year, year
|
||||
// * m, month, months
|
||||
// * w, week, weeks
|
||||
// * d, day, days
|
||||
// * h, hour, hours
|
||||
// * i, minute, minutes
|
||||
// * s, second, seconds
|
||||
// * f, millisecond, milliseconds
|
||||
func DateSubtract(_ context.Context, args ...core.Value) (core.Value, error) {
|
||||
date, amount, unit, err := getArgs(args)
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
dt, err := addUnit(date, -1*int(amount), unit.String())
|
||||
if err != nil {
|
||||
return values.None, err
|
||||
}
|
||||
|
||||
return dt, nil
|
||||
}
|
||||
|
||||
func getArgs(args []core.Value) (values.DateTime, values.Int, values.String, error) {
|
||||
err := core.ValidateArgs(args, 3, 3)
|
||||
if err != nil {
|
||||
return emptyDateTime, emptyInt, emptyString, err
|
||||
}
|
||||
|
||||
err = core.ValidateValueTypePairs(
|
||||
core.PairValueType{Value: args[0], Types: sliceDateTime},
|
||||
core.PairValueType{Value: args[1], Types: sliceIntType},
|
||||
core.PairValueType{Value: args[2], Types: sliceStringType},
|
||||
)
|
||||
if err != nil {
|
||||
return emptyDateTime, emptyInt, emptyString, err
|
||||
}
|
||||
|
||||
date := args[0].(values.DateTime)
|
||||
amount := args[1].(values.Int)
|
||||
unit := args[2].(values.String)
|
||||
|
||||
return date, amount, unit, nil
|
||||
}
|
||||
|
||||
func addUnit(dt values.DateTime, amount int, unit string) (values.DateTime, error) {
|
||||
switch strings.ToLower(unit) {
|
||||
case "y", "year", "years":
|
||||
return values.NewDateTime(dt.AddDate(amount*1, 0, 0)), nil
|
||||
case "m", "month", "months":
|
||||
return values.NewDateTime(dt.AddDate(0, amount*1, 0)), nil
|
||||
case "w", "week", "weeks":
|
||||
return values.NewDateTime(dt.AddDate(0, 0, amount*7)), nil
|
||||
case "d", "day", "days":
|
||||
return values.NewDateTime(dt.AddDate(0, 0, amount*1)), nil
|
||||
case "h", "hour", "hours":
|
||||
return values.NewDateTime(dt.Add(time.Hour)), nil
|
||||
case "i", "minute", "minutes":
|
||||
return values.NewDateTime(dt.Add(time.Minute)), nil
|
||||
case "s", "second", "seconds":
|
||||
return values.NewDateTime(dt.Add(time.Second)), nil
|
||||
case "f", "millisecond", "milliseconds":
|
||||
return values.NewDateTime(dt.Add(time.Millisecond)), nil
|
||||
}
|
||||
return values.DateTime{}, errors.Errorf("no such unit '%s'", unit)
|
||||
}
|
222
pkg/stdlib/datetime/add_subtract_test.go
Normal file
222
pkg/stdlib/datetime/add_subtract_test.go
Normal file
@ -0,0 +1,222 @@
|
||||
package datetime_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||
|
||||
"github.com/MontFerret/ferret/pkg/stdlib/datetime"
|
||||
)
|
||||
|
||||
var (
|
||||
utcLoc, _ = time.LoadLocation("UTC")
|
||||
)
|
||||
|
||||
func TestDateAdd(t *testing.T) {
|
||||
tcs := []*testCase{
|
||||
&testCase{
|
||||
Name: "When more than 3 arguments",
|
||||
Expected: values.None,
|
||||
Args: []core.Value{
|
||||
values.NewInt(0),
|
||||
values.NewInt(0),
|
||||
values.NewInt(0),
|
||||
values.NewInt(0),
|
||||
},
|
||||
ShouldErr: true,
|
||||
},
|
||||
&testCase{
|
||||
Name: "When less than 3 arguments",
|
||||
Expected: values.None,
|
||||
Args: []core.Value{
|
||||
values.NewInt(0),
|
||||
},
|
||||
ShouldErr: true,
|
||||
},
|
||||
&testCase{
|
||||
Name: "When incorrect arguments",
|
||||
Expected: values.None,
|
||||
Args: []core.Value{
|
||||
values.NewString("bla-bla"),
|
||||
values.NewInt(0),
|
||||
values.NewString("be-be"),
|
||||
},
|
||||
ShouldErr: true,
|
||||
},
|
||||
&testCase{
|
||||
Name: "When wrong unit given",
|
||||
Expected: values.None,
|
||||
Args: []core.Value{
|
||||
mustLayoutDt("2006-01-02", "1999-02-07"),
|
||||
values.NewInt(5),
|
||||
values.NewString("not_exist"),
|
||||
},
|
||||
ShouldErr: true,
|
||||
},
|
||||
&testCase{
|
||||
Name: "When argument have correct types",
|
||||
Expected: func() core.Value {
|
||||
expected, _ := datetime.DateAdd(
|
||||
context.Background(),
|
||||
mustDefaultLayoutDt("1999-02-07T15:04:05Z"),
|
||||
values.NewInt(1),
|
||||
values.NewString("day"),
|
||||
)
|
||||
return expected
|
||||
}(),
|
||||
Args: []core.Value{
|
||||
mustDefaultLayoutDt("1999-02-07T15:04:05Z"),
|
||||
values.NewInt(1),
|
||||
values.NewString("day"),
|
||||
},
|
||||
},
|
||||
&testCase{
|
||||
Name: "-1 day",
|
||||
Expected: mustDefaultLayoutDt("1999-02-06T15:04:05Z"),
|
||||
Args: []core.Value{
|
||||
mustDefaultLayoutDt("1999-02-07T15:04:05Z"),
|
||||
values.NewInt(-1),
|
||||
values.NewString("day"),
|
||||
},
|
||||
},
|
||||
&testCase{
|
||||
Name: "+3 months",
|
||||
Expected: mustDefaultLayoutDt("1999-05-07T15:04:05Z"),
|
||||
Args: []core.Value{
|
||||
mustDefaultLayoutDt("1999-02-07T15:04:05Z"),
|
||||
values.NewInt(3),
|
||||
values.NewString("months"),
|
||||
},
|
||||
},
|
||||
&testCase{
|
||||
Name: "+5 years",
|
||||
Expected: mustLayoutDt("2006-01-02", "2004-02-07"),
|
||||
Args: []core.Value{
|
||||
mustLayoutDt("2006-01-02", "1999-02-07"),
|
||||
values.NewInt(5),
|
||||
values.NewString("y"),
|
||||
},
|
||||
},
|
||||
&testCase{
|
||||
Name: "1999 minus 2000 years",
|
||||
Expected: values.NewDateTime(
|
||||
time.Date(-1, 2, 7, 0, 0, 0, 0, utcLoc),
|
||||
),
|
||||
Args: []core.Value{
|
||||
mustLayoutDt("2006-01-02", "1999-02-07"),
|
||||
values.NewInt(-2000),
|
||||
values.NewString("year"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
tc.Do(t, datetime.DateAdd)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDateSubtract(t *testing.T) {
|
||||
tcs := []*testCase{
|
||||
&testCase{
|
||||
Name: "When more than 3 arguments",
|
||||
Expected: values.None,
|
||||
Args: []core.Value{
|
||||
values.NewInt(0),
|
||||
values.NewInt(0),
|
||||
values.NewInt(0),
|
||||
values.NewInt(0),
|
||||
},
|
||||
ShouldErr: true,
|
||||
},
|
||||
&testCase{
|
||||
Name: "When less than 3 arguments",
|
||||
Expected: values.None,
|
||||
Args: []core.Value{
|
||||
values.NewInt(0),
|
||||
},
|
||||
ShouldErr: true,
|
||||
},
|
||||
&testCase{
|
||||
Name: "When incorrect arguments",
|
||||
Expected: values.None,
|
||||
Args: []core.Value{
|
||||
values.NewString("bla-bla"),
|
||||
values.NewInt(0),
|
||||
values.NewString("be-be"),
|
||||
},
|
||||
ShouldErr: true,
|
||||
},
|
||||
&testCase{
|
||||
Name: "When wrong unit given",
|
||||
Expected: values.None,
|
||||
Args: []core.Value{
|
||||
mustLayoutDt("2006-01-02", "1999-02-07"),
|
||||
values.NewInt(5),
|
||||
values.NewString("not_exist"),
|
||||
},
|
||||
ShouldErr: true,
|
||||
},
|
||||
&testCase{
|
||||
Name: "When argument have correct types",
|
||||
Expected: func() core.Value {
|
||||
expected, _ := datetime.DateSubtract(
|
||||
context.Background(),
|
||||
mustDefaultLayoutDt("1999-02-07T15:04:05Z"),
|
||||
values.NewInt(1),
|
||||
values.NewString("day"),
|
||||
)
|
||||
return expected
|
||||
}(),
|
||||
Args: []core.Value{
|
||||
mustDefaultLayoutDt("1999-02-07T15:04:05Z"),
|
||||
values.NewInt(1),
|
||||
values.NewString("day"),
|
||||
},
|
||||
},
|
||||
&testCase{
|
||||
Name: "-1 day",
|
||||
Expected: mustDefaultLayoutDt("1999-02-08T15:04:05Z"),
|
||||
Args: []core.Value{
|
||||
mustDefaultLayoutDt("1999-02-07T15:04:05Z"),
|
||||
values.NewInt(-1),
|
||||
values.NewString("day"),
|
||||
},
|
||||
},
|
||||
&testCase{
|
||||
Name: "+3 months",
|
||||
Expected: mustDefaultLayoutDt("1999-02-07T15:04:05Z"),
|
||||
Args: []core.Value{
|
||||
mustDefaultLayoutDt("1999-05-07T15:04:05Z"),
|
||||
values.NewInt(3),
|
||||
values.NewString("months"),
|
||||
},
|
||||
},
|
||||
&testCase{
|
||||
Name: "+5 years",
|
||||
Expected: mustLayoutDt("2006-01-02", "1994-02-07"),
|
||||
Args: []core.Value{
|
||||
mustLayoutDt("2006-01-02", "1999-02-07"),
|
||||
values.NewInt(5),
|
||||
values.NewString("y"),
|
||||
},
|
||||
},
|
||||
&testCase{
|
||||
Name: "1999 minus 2000 years",
|
||||
Expected: values.NewDateTime(
|
||||
time.Date(-1, 2, 7, 0, 0, 0, 0, utcLoc),
|
||||
),
|
||||
Args: []core.Value{
|
||||
mustLayoutDt("2006-01-02", "1999-02-07"),
|
||||
values.NewInt(2000),
|
||||
values.NewString("year"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
tc.Do(t, datetime.DateSubtract)
|
||||
}
|
||||
}
|
@ -19,5 +19,7 @@ func NewLib() map[string]core.Function {
|
||||
"DATE_QUARTER": DateQuarter,
|
||||
"DATE_DAYS_IN_MONTH": DateDaysInMonth,
|
||||
"DATE_FORMAT": DateFormat,
|
||||
"DATE_ADD": DateAdd,
|
||||
"DATE_SUBTRACT": DateSubtract,
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user