diff --git a/pkg/runtime/values/date_time.go b/pkg/runtime/values/date_time.go index 69981ce5..1ab4d585 100644 --- a/pkg/runtime/values/date_time.go +++ b/pkg/runtime/values/date_time.go @@ -7,7 +7,7 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/core" ) -const defaultTimeLayout = time.RFC3339 +const DefaultTimeLayout = time.RFC3339 type DateTime struct { time.Time @@ -26,7 +26,7 @@ func NewDateTime(time time.Time) DateTime { } func ParseDateTime(input interface{}) (DateTime, error) { - return ParseDateTimeWith(input, defaultTimeLayout) + return ParseDateTimeWith(input, DefaultTimeLayout) } func ParseDateTimeWith(input interface{}, layout string) (DateTime, error) { diff --git a/pkg/stdlib/datetime/date.go b/pkg/stdlib/datetime/date.go new file mode 100644 index 00000000..ce7a8ae4 --- /dev/null +++ b/pkg/stdlib/datetime/date.go @@ -0,0 +1,33 @@ +package datetime + +import ( + "context" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// Date convert RFC3339 date time string to DateTime object. +// @params timeString (String) - string in RFC3339 format. +// @return (DateTime) - new DateTime object derived from timeString. +func Date(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.StringType) + if err != nil { + return values.None, err + } + + timeStrings := args[0].(values.String) + + t, err := time.Parse(values.DefaultTimeLayout, timeStrings.String()) + if err != nil { + return values.None, err + } + + return values.NewDateTime(t), nil +} diff --git a/pkg/stdlib/datetime/date_test.go b/pkg/stdlib/datetime/date_test.go new file mode 100644 index 00000000..221fcbb3 --- /dev/null +++ b/pkg/stdlib/datetime/date_test.go @@ -0,0 +1,49 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDate(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When incorrect timeStrings", + Expected: values.None, + Args: []core.Value{ + values.NewString("bla-bla"), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When correct timeString in RFC3339 format", + Expected: mustDefaultLayoutDt("1999-02-07T15:04:05Z"), + Args: []core.Value{ + values.NewString("1999-02-07T15:04:05Z"), + }, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.Date) + } +} diff --git a/pkg/stdlib/datetime/dayofweek.go b/pkg/stdlib/datetime/dayofweek.go new file mode 100644 index 00000000..64b363c4 --- /dev/null +++ b/pkg/stdlib/datetime/dayofweek.go @@ -0,0 +1,28 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/runtime/core" +) + +// DateDayOfWeek returns number of the weekday from the date. Sunday is the 0th day of week. +// @params date (DateTime) - source DateTime. +// @return (Int) - return number of the weekday. +func DateDayOfWeek(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + wday := args[0].(values.DateTime).Weekday() + + return values.NewInt(int(wday)), nil +} diff --git a/pkg/stdlib/datetime/dayofweek_test.go b/pkg/stdlib/datetime/dayofweek_test.go new file mode 100644 index 00000000..617cfef6 --- /dev/null +++ b/pkg/stdlib/datetime/dayofweek_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateDayOfWeek(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When Sunday (0th day)", + Expected: values.NewInt(0), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When Monday (1th day)", + Expected: values.NewInt(1), + Args: []core.Value{mustDefaultLayoutDt("1999-02-08T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateDayOfWeek) + } +} diff --git a/pkg/stdlib/datetime/helpers_test.go b/pkg/stdlib/datetime/helpers_test.go new file mode 100644 index 00000000..1fe4b0b5 --- /dev/null +++ b/pkg/stdlib/datetime/helpers_test.go @@ -0,0 +1,65 @@ +package datetime_test + +import ( + "context" + "testing" + "time" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + + . "github.com/smartystreets/goconvey/convey" +) + +type testCase struct { + Name string + Expected core.Value + TimeArg time.Time + Args []core.Value + ShouldErr bool +} + +func (tc *testCase) Do(t *testing.T, fn core.Function) { + Convey(tc.Name, t, func() { + expected := tc.Expected + + actual, err := fn(context.Background(), tc.Args...) + + if tc.ShouldErr { + So(err, ShouldBeError) + expected = values.None + } else { + So(err, ShouldBeNil) + } + + So(actual.Compare(expected), ShouldEqual, 0) + }) +} + +func mustDefaultLayoutDt(timeString string) values.DateTime { + dt, err := defaultLayoutDt(timeString) + if err != nil { + panic(err) + } + return dt +} + +func mustLayoutDt(layout, value string) values.DateTime { + dt, err := layoutDt(layout, value) + if err != nil { + panic(err) + } + return dt +} + +func defaultLayoutDt(timeString string) (values.DateTime, error) { + return layoutDt(values.DefaultTimeLayout, timeString) +} + +func layoutDt(layout, value string) (values.DateTime, error) { + t, err := time.Parse(layout, value) + if err != nil { + return values.DateTime{}, err + } + return values.NewDateTime(t), nil +} diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index d3d3ea33..96e319f3 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -4,6 +4,9 @@ import "github.com/MontFerret/ferret/pkg/runtime/core" func NewLib() map[string]core.Function { return map[string]core.Function{ - "NOW": Now, + "NOW": Now, + "DATE": Date, + "DATE_DAYOFWEEK": DateDayOfWeek, + "DATE_YEAR": DateYear, } } diff --git a/pkg/stdlib/datetime/now_test.go b/pkg/stdlib/datetime/now_test.go index acc6d8e8..559b12b1 100644 --- a/pkg/stdlib/datetime/now_test.go +++ b/pkg/stdlib/datetime/now_test.go @@ -1,25 +1,14 @@ package datetime_test import ( - "context" "testing" - "time" + + "github.com/MontFerret/ferret/pkg/stdlib/datetime" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" - "github.com/MontFerret/ferret/pkg/stdlib/datetime" - - . "github.com/smartystreets/goconvey/convey" ) -type testCase struct { - Name string - Expected core.Value - TimeArg time.Time - Args []core.Value - ShouldErr bool -} - func TestNow(t *testing.T) { tcs := []*testCase{ &testCase{ @@ -33,25 +22,6 @@ func TestNow(t *testing.T) { } for _, tc := range tcs { - tc.Do(t) + tc.Do(t, datetime.Now) } } - -func (tc *testCase) Do(t *testing.T) { - Convey(tc.Name, t, func() { - var expected core.Value - - expected = values.NewDateTime(tc.TimeArg) - - dt, err := datetime.Now(context.Background(), tc.Args...) - - if tc.ShouldErr { - So(err, ShouldBeError) - expected = values.None - } else { - So(err, ShouldBeNil) - } - - So(dt, ShouldEqual, expected) - }) -} diff --git a/pkg/stdlib/datetime/year.go b/pkg/stdlib/datetime/year.go new file mode 100644 index 00000000..41b66dc6 --- /dev/null +++ b/pkg/stdlib/datetime/year.go @@ -0,0 +1,27 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" +) + +// DateYear returns the year extracted from the given date. +// @params date (DateTime) - source DateTime. +// @return (Int) - a year number. +func DateYear(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 1, 1) + if err != nil { + return values.None, err + } + + err = core.ValidateType(args[0], core.DateTimeType) + if err != nil { + return values.None, err + } + + year := args[0].(values.DateTime).Year() + + return values.NewInt(year), nil +} diff --git a/pkg/stdlib/datetime/year_test.go b/pkg/stdlib/datetime/year_test.go new file mode 100644 index 00000000..29b9ab0b --- /dev/null +++ b/pkg/stdlib/datetime/year_test.go @@ -0,0 +1,43 @@ +package datetime_test + +import ( + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/datetime" +) + +func TestDateYear(t *testing.T) { + tcs := []*testCase{ + &testCase{ + Name: "When more than 1 arguments", + Expected: values.None, + Args: []core.Value{ + values.NewString("string"), + values.NewInt(0), + }, + ShouldErr: true, + }, + &testCase{ + Name: "When 0 arguments", + Expected: values.None, + Args: []core.Value{}, + ShouldErr: true, + }, + &testCase{ + Name: "When 1999th year", + Expected: values.NewInt(1999), + Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")}, + }, + &testCase{ + Name: "When 1629th year", + Expected: values.NewInt(1629), + Args: []core.Value{mustDefaultLayoutDt("1629-02-08T15:04:05Z")}, + }, + } + + for _, tc := range tcs { + tc.Do(t, datetime.DateYear) + } +}