mirror of
https://github.com/MontFerret/ferret.git
synced 2025-07-17 01:32:22 +02:00
Feature/#8 datetime (#153)
This commit is contained in:
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultTimeLayout = time.RFC3339
|
const DefaultTimeLayout = time.RFC3339
|
||||||
|
|
||||||
type DateTime struct {
|
type DateTime struct {
|
||||||
time.Time
|
time.Time
|
||||||
@ -26,7 +26,7 @@ func NewDateTime(time time.Time) DateTime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ParseDateTime(input interface{}) (DateTime, error) {
|
func ParseDateTime(input interface{}) (DateTime, error) {
|
||||||
return ParseDateTimeWith(input, defaultTimeLayout)
|
return ParseDateTimeWith(input, DefaultTimeLayout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseDateTimeWith(input interface{}, layout string) (DateTime, error) {
|
func ParseDateTimeWith(input interface{}, layout string) (DateTime, error) {
|
||||||
|
33
pkg/stdlib/datetime/date.go
Normal file
33
pkg/stdlib/datetime/date.go
Normal file
@ -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
|
||||||
|
}
|
49
pkg/stdlib/datetime/date_test.go
Normal file
49
pkg/stdlib/datetime/date_test.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
28
pkg/stdlib/datetime/dayofweek.go
Normal file
28
pkg/stdlib/datetime/dayofweek.go
Normal file
@ -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
|
||||||
|
}
|
43
pkg/stdlib/datetime/dayofweek_test.go
Normal file
43
pkg/stdlib/datetime/dayofweek_test.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
65
pkg/stdlib/datetime/helpers_test.go
Normal file
65
pkg/stdlib/datetime/helpers_test.go
Normal file
@ -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
|
||||||
|
}
|
@ -5,5 +5,8 @@ import "github.com/MontFerret/ferret/pkg/runtime/core"
|
|||||||
func NewLib() map[string]core.Function {
|
func NewLib() map[string]core.Function {
|
||||||
return map[string]core.Function{
|
return map[string]core.Function{
|
||||||
"NOW": Now,
|
"NOW": Now,
|
||||||
|
"DATE": Date,
|
||||||
|
"DATE_DAYOFWEEK": DateDayOfWeek,
|
||||||
|
"DATE_YEAR": DateYear,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,14 @@
|
|||||||
package datetime_test
|
package datetime_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/stdlib/datetime"
|
||||||
|
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
"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) {
|
func TestNow(t *testing.T) {
|
||||||
tcs := []*testCase{
|
tcs := []*testCase{
|
||||||
&testCase{
|
&testCase{
|
||||||
@ -33,25 +22,6 @@ func TestNow(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tcs {
|
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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
27
pkg/stdlib/datetime/year.go
Normal file
27
pkg/stdlib/datetime/year.go
Normal file
@ -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
|
||||||
|
}
|
43
pkg/stdlib/datetime/year_test.go
Normal file
43
pkg/stdlib/datetime/year_test.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user