From b0e2c06f51296851d6d407f3fddeb85aecb03a63 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 30 Oct 2018 21:16:18 +0300 Subject: [PATCH 1/8] init datetime --- pkg/stdlib/datetime/now.go | 20 ++++++++++++++++ pkg/stdlib/datetime/now_test.go | 42 +++++++++++++++++++++++++++++++++ pkg/stdlib/strings/fmt_test.go | 1 - 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 pkg/stdlib/datetime/now.go create mode 100644 pkg/stdlib/datetime/now_test.go diff --git a/pkg/stdlib/datetime/now.go b/pkg/stdlib/datetime/now.go new file mode 100644 index 00000000..8abd0b7e --- /dev/null +++ b/pkg/stdlib/datetime/now.go @@ -0,0 +1,20 @@ +package datetime + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/values" + + "github.com/MontFerret/ferret/pkg/runtime/core" +) + +// Now returns new DateTime object with Time equal to time.Now(). +// @returns (DateTime) - New DateTime object. +func Now(_ context.Context, args ...core.Value) (core.Value, error) { + err := core.ValidateArgs(args, 0, 0) + if err != nil { + return values.None, err + } + + return values.NewCurrentDateTime(), nil +} diff --git a/pkg/stdlib/datetime/now_test.go b/pkg/stdlib/datetime/now_test.go new file mode 100644 index 00000000..28b521ec --- /dev/null +++ b/pkg/stdlib/datetime/now_test.go @@ -0,0 +1,42 @@ +package datetime_test + +import ( + "context" + "testing" + + "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 values.DateTime + Args []core.Value + ShouldErr bool +} + +func TestNow(t *testing.T) { + +} + +func (tc *testCase) Do(t *testing.T) { + Convey(tc.Name, t, func() { + var expected core.Value + + expected = values.NewDateTime(tc.Expected.Time) + + 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/strings/fmt_test.go b/pkg/stdlib/strings/fmt_test.go index cf4603ff..7995cd64 100644 --- a/pkg/stdlib/strings/fmt_test.go +++ b/pkg/stdlib/strings/fmt_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/MontFerret/ferret/pkg/runtime/core" - "github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/stdlib/strings" . "github.com/smartystreets/goconvey/convey" From 6f82025e0e77e23a337e84db948cab2ddcd770c2 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 30 Oct 2018 22:23:20 +0300 Subject: [PATCH 2/8] added test for datetime --- pkg/stdlib/datetime/now_test.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/pkg/stdlib/datetime/now_test.go b/pkg/stdlib/datetime/now_test.go index 28b521ec..acc6d8e8 100644 --- a/pkg/stdlib/datetime/now_test.go +++ b/pkg/stdlib/datetime/now_test.go @@ -3,6 +3,7 @@ package datetime_test import ( "context" "testing" + "time" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" @@ -13,20 +14,34 @@ import ( type testCase struct { Name string - Expected values.DateTime + Expected core.Value + TimeArg time.Time Args []core.Value ShouldErr bool } func TestNow(t *testing.T) { - + tcs := []*testCase{ + &testCase{ + Name: "When too many arguments", + Expected: values.None, + Args: []core.Value{ + values.NewCurrentDateTime(), + }, + ShouldErr: true, + }, + } + + for _, tc := range tcs { + tc.Do(t) + } } func (tc *testCase) Do(t *testing.T) { Convey(tc.Name, t, func() { var expected core.Value - expected = values.NewDateTime(tc.Expected.Time) + expected = values.NewDateTime(tc.TimeArg) dt, err := datetime.Now(context.Background(), tc.Args...) From 09546ae2ecd78dd62bcae9339eaf3be465c943b2 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 30 Oct 2018 22:39:42 +0300 Subject: [PATCH 3/8] added lib.go --- pkg/stdlib/datetime/lib.go | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 pkg/stdlib/datetime/lib.go diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go new file mode 100644 index 00000000..d3d3ea33 --- /dev/null +++ b/pkg/stdlib/datetime/lib.go @@ -0,0 +1,9 @@ +package datetime + +import "github.com/MontFerret/ferret/pkg/runtime/core" + +func NewLib() map[string]core.Function { + return map[string]core.Function{ + "NOW": Now, + } +} From d87079898d9391aedc29c2cbf9cba83cc4141b5c Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:20:21 +0300 Subject: [PATCH 4/8] add helpers functions --- pkg/stdlib/datetime/helpers_test.go | 65 +++++++++++++++++++++++++++++ pkg/stdlib/datetime/now_test.go | 36 ++-------------- 2 files changed, 68 insertions(+), 33 deletions(-) create mode 100644 pkg/stdlib/datetime/helpers_test.go 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/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/runtime/core" - "github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/stdlib/datetime" - . "github.com/smartystreets/goconvey/convey" + "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" ) -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) - }) -} From 1cb998e3e10559654932ee9f03071119d5a034c2 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:21:38 +0300 Subject: [PATCH 5/8] made values.DefaultTimeLayout public --- pkg/runtime/values/date_time.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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) { From f3b1372e2e6020d7802f14d05aa6509f2e574ea0 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:34:59 +0300 Subject: [PATCH 6/8] added DATE function --- pkg/stdlib/datetime/date.go | 33 +++++++++++++++++++++ pkg/stdlib/datetime/date_test.go | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 pkg/stdlib/datetime/date.go create mode 100644 pkg/stdlib/datetime/date_test.go 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) + } +} From f5b7cb95b63bcf23d1a4cb8f7e75cbfed59bc5ce Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:36:03 +0300 Subject: [PATCH 7/8] added DATE_DAYOFWEEK function --- pkg/stdlib/datetime/dayofweek.go | 28 +++++++++++++++++ pkg/stdlib/datetime/dayofweek_test.go | 43 +++++++++++++++++++++++++++ pkg/stdlib/datetime/lib.go | 4 ++- 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 pkg/stdlib/datetime/dayofweek.go create mode 100644 pkg/stdlib/datetime/dayofweek_test.go 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/lib.go b/pkg/stdlib/datetime/lib.go index d3d3ea33..b398e909 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -4,6 +4,8 @@ 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, } } From db2ab4b19e83919565a1edb83201d09dbc152841 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 31 Oct 2018 23:36:39 +0300 Subject: [PATCH 8/8] added DATE_YEAR function --- pkg/stdlib/datetime/lib.go | 1 + pkg/stdlib/datetime/year.go | 27 ++++++++++++++++++++ pkg/stdlib/datetime/year_test.go | 43 ++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 pkg/stdlib/datetime/year.go create mode 100644 pkg/stdlib/datetime/year_test.go diff --git a/pkg/stdlib/datetime/lib.go b/pkg/stdlib/datetime/lib.go index b398e909..96e319f3 100644 --- a/pkg/stdlib/datetime/lib.go +++ b/pkg/stdlib/datetime/lib.go @@ -7,5 +7,6 @@ func NewLib() map[string]core.Function { "NOW": Now, "DATE": Date, "DATE_DAYOFWEEK": DateDayOfWeek, + "DATE_YEAR": DateYear, } } 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) + } +}