1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-08-13 19:52:52 +02:00

added DATE_DAYOFYEAR, DATE_MILLISECOND, DATE_LEAPYEAR, DATE_QUARTER, DATE_DAYS_IN_MONTH, DATE_FORMAT functions (#157)

This commit is contained in:
3timeslazy
2018-11-05 19:54:36 +03:00
committed by Tim Voronov
parent 5e6701f1c1
commit e4a7b581f0
14 changed files with 592 additions and 10 deletions

View File

@@ -22,7 +22,7 @@ install:
dep ensure
test:
go test -race ${DIR_PKG}/...
go test -race -v ${DIR_PKG}/...
cover:
go test -race -coverprofile=coverage.txt -covermode=atomic ${DIR_PKG}/... && \

View File

@@ -0,0 +1,29 @@
package datetime
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
// DateDayOfYear returns the day of year number of date.
// The return value range from 1 to 365 (366 in a leap year).
// @params date (DateTime) - source DateTime.
// @return (Int) - a day of year number.
func DateDayOfYear(_ 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
}
dayOfYear := args[0].(values.DateTime).YearDay()
return values.NewInt(dayOfYear), nil
}

View File

@@ -0,0 +1,51 @@
package datetime_test
import (
"testing"
"time"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/datetime"
)
func TestDateDayOfYear(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 38th day of the year",
Expected: values.NewInt(38),
Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")},
},
&testCase{
Name: "When 59th day of the year",
Expected: values.NewInt(59),
Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:59:05Z")},
},
&testCase{
Name: "When 366th day of the year",
Expected: values.NewInt(366),
Args: []core.Value{
values.NewDateTime(time.Date(1972, time.December, 31, 0, 0, 0, 0, time.Local)),
},
},
}
for _, tc := range tcs {
tc.Do(t, datetime.DateDayOfYear)
}
}

View File

@@ -0,0 +1,49 @@
package datetime
import (
"context"
"time"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)
var daysCount = map[time.Month]int{
time.January: 31,
time.February: 28,
time.March: 31,
time.April: 30,
time.May: 31,
time.June: 30,
time.July: 30,
time.August: 31,
time.September: 30,
time.October: 31,
time.November: 30,
time.December: 31,
}
// DateDaysInMonth returns the number of days in the month of date.
// @params date (DateTime) - source DateTime.
// @return (Int) - number of the days.
func DateDaysInMonth(_ 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
}
dt := args[0].(values.DateTime)
month := dt.Month()
count := daysCount[month]
if month == time.February && isLeap(dt.Year()) {
count++
}
return values.NewInt(count), nil
}

View File

@@ -0,0 +1,62 @@
package datetime_test
import (
"testing"
"time"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/datetime"
)
func TestDateDateDaysInMonth(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 Feb and a leap year",
Expected: values.NewInt(29),
Args: []core.Value{
values.NewDateTime(time.Date(1972, time.February, 1, 1, 1, 1, 1, time.Local)),
},
},
&testCase{
Name: "When Feb and not a leap year",
Expected: values.NewInt(28),
Args: []core.Value{
values.NewDateTime(time.Date(1999, time.February, 1, 1, 1, 1, 1, time.Local)),
},
},
&testCase{
Name: "When January",
Expected: values.NewInt(31),
Args: []core.Value{
values.NewDateTime(time.Date(1999, time.January, 1, 1, 1, 1, 1, time.Local)),
},
},
&testCase{
Name: "When November",
Expected: values.NewInt(30),
Args: []core.Value{
values.NewDateTime(time.Date(1999, time.November, 1, 1, 1, 1, 1, time.Local)),
},
},
}
for _, tc := range tcs {
tc.Do(t, datetime.DateDaysInMonth)
}
}

View File

@@ -0,0 +1,33 @@
package datetime
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)
// DateFormat format date according to the given format string.
// @params date (DateTime) - source DateTime object.
// @return (String) - formatted date.
func DateFormat(_ context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 2, 2)
if err != nil {
return values.None, err
}
err = core.ValidateType(args[0], core.DateTimeType)
if err != nil {
return values.None, err
}
err = core.ValidateType(args[1], core.StringType)
if err != nil {
return values.None, err
}
date := args[0].(values.DateTime)
format := args[1].(values.String).String()
return values.NewString(date.Format(format)), nil
}

View File

@@ -0,0 +1,115 @@
package datetime_test
import (
"testing"
"time"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/datetime"
)
func TestDateFormat(t *testing.T) {
tcs := []*testCase{
&testCase{
Name: "When more than 2 arguments",
Expected: values.None,
Args: []core.Value{
values.NewString("string"),
values.NewInt(0),
values.NewArray(0),
},
ShouldErr: true,
},
&testCase{
Name: "When less than 2 arguments",
Expected: values.None,
Args: []core.Value{
values.NewInt(0),
},
ShouldErr: true,
},
&testCase{
Name: "When first argument is wrong",
Expected: values.None,
Args: []core.Value{
values.NewInt(0),
values.NewString(time.RFC822),
},
ShouldErr: true,
},
&testCase{
Name: "When second argument is wrong",
Expected: values.None,
Args: []core.Value{
values.NewCurrentDateTime(),
values.NewInt(0),
},
ShouldErr: true,
},
&testCase{
Name: "When DefaultTimeLayout",
Expected: values.NewString("1999-02-07T15:04:05Z"),
Args: []core.Value{
mustDefaultLayoutDt("1999-02-07T15:04:05Z"),
values.NewString(values.DefaultTimeLayout),
},
},
&testCase{
Name: "When RFC3339Nano",
Expected: values.NewString(
time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local).
Format(time.RFC3339Nano),
),
Args: []core.Value{
values.NewDateTime(
time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local),
),
values.NewString(time.RFC3339Nano),
},
},
&testCase{
Name: "When custom format",
Expected: values.NewString(
time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local).
Format("2006-01-02"),
),
Args: []core.Value{
values.NewDateTime(
time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local),
),
values.NewString("2006-01-02"),
},
},
&testCase{
Name: "When empty string",
Expected: values.NewString(""),
Args: []core.Value{
values.NewCurrentDateTime(),
values.NewString(""),
},
},
&testCase{
Name: "When random string without numbers",
Expected: values.NewString("qwerty"),
Args: []core.Value{
values.NewCurrentDateTime(),
values.NewString("qwerty"),
},
},
&testCase{
Name: "When random string with numbers",
Expected: values.NewString("qwerty2018uio"),
Args: []core.Value{
values.NewDateTime(
time.Date(2018, time.November, 5, 0, 54, 15, 5125, time.Local),
),
values.NewString("qwerty2006uio"),
},
},
}
for _, tc := range tcs {
tc.Do(t, datetime.DateFormat)
}
}

View File

@@ -0,0 +1,31 @@
package datetime
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)
// DateLeapYear returns true if date is in a leap year else false.
// @params date (DateTime) - source DateTime.
// @return (Boolean) - date is in a leap year.
func DateLeapYear(_ 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.NewBoolean(isLeap(year)), nil
}
func isLeap(year int) bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}

View 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 TestDateLeapYear(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 not a leap year",
Expected: values.NewBoolean(false),
Args: []core.Value{mustDefaultLayoutDt("1999-02-07T15:04:05Z")},
},
&testCase{
Name: "When a leap year",
Expected: values.NewBoolean(true),
Args: []core.Value{mustDefaultLayoutDt("1972-12-07T15:04:05Z")},
},
}
for _, tc := range tcs {
tc.Do(t, datetime.DateLeapYear)
}
}

View File

@@ -4,14 +4,20 @@ import "github.com/MontFerret/ferret/pkg/runtime/core"
func NewLib() map[string]core.Function {
return map[string]core.Function{
"NOW": Now,
"DATE": Date,
"DATE_DAYOFWEEK": DateDayOfWeek,
"DATE_YEAR": DateYear,
"DATE_MONTH": DateMonth,
"DATE_DAY": DateDay,
"DATE_HOUR": DateHour,
"DATE_MINUTE": DateMinute,
"DATE_SECOND": DateSecond,
"NOW": Now,
"DATE": Date,
"DATE_DAYOFWEEK": DateDayOfWeek,
"DATE_YEAR": DateYear,
"DATE_MONTH": DateMonth,
"DATE_DAY": DateDay,
"DATE_HOUR": DateHour,
"DATE_MINUTE": DateMinute,
"DATE_SECOND": DateSecond,
"DATE_MILLISECOND": DateMillisecond,
"DATE_DAYOFYEAR": DateDayOfYear,
"DATE_LEAPYEAR": DateLeapYear,
"DATE_QUARTER": DateQuarter,
"DATE_DAYS_IN_MONTH": DateDaysInMonth,
"DATE_FORMAT": DateFormat,
}
}

View File

@@ -0,0 +1,27 @@
package datetime
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)
// DateMillisecond returns the millisecond of date as a number.
// @params date (DateTime) - source DateTime.
// @return (Int) - a millisecond number.
func DateMillisecond(_ 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
}
msec := args[0].(values.DateTime).Nanosecond() / 1000000
return values.NewInt(msec), nil
}

View File

@@ -0,0 +1,54 @@
package datetime_test
import (
"testing"
"time"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/datetime"
)
func TestDateMillisecond(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 129 millisecond",
Expected: values.NewInt(129),
Args: []core.Value{
values.NewDateTime(time.Date(2018, 11, 4, 17, 3, 12, 129410001, time.Local)),
},
},
&testCase{
Name: "When 0 milliseconds [0]",
Expected: values.NewInt(0),
Args: []core.Value{mustDefaultLayoutDt("1629-02-28T15:59:59Z")},
},
// any nanosec < 1000000 equal to 0 milliseconds
&testCase{
Name: "When 0 milliseconds [1]",
Expected: values.NewInt(0),
Args: []core.Value{
values.NewDateTime(time.Date(0, 0, 0, 0, 0, 0, 999999, time.Local)),
},
},
}
for _, tc := range tcs {
tc.Do(t, datetime.DateMillisecond)
}
}

View File

@@ -0,0 +1,38 @@
package datetime
import (
"context"
"time"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)
// DateQuarter returns which quarter date belongs to.
// @params date (DateTime) - source DateTime.
// @return (Int) - a quarter number.
func DateQuarter(_ 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
}
month := args[0].(values.DateTime).Month()
quarter := values.NewInt(1)
switch month {
case time.April, time.May, time.June:
quarter = values.NewInt(2)
case time.July, time.August, time.September:
quarter = values.NewInt(3)
case time.October, time.November, time.December:
quarter = values.NewInt(4)
}
return quarter, nil
}

View File

@@ -0,0 +1,44 @@
package datetime_test
import (
"testing"
"time"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/datetime"
)
func TestDateQuarter(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,
},
}
for month := time.January; month <= time.December; month++ {
tcs = append(tcs, &testCase{
Name: "When " + month.String(),
Expected: values.NewInt(((int(month) - 1) / 3) + 1),
Args: []core.Value{
values.NewDateTime(time.Date(1999, month, 1, 1, 1, 1, 1, time.Local)),
},
})
}
for _, tc := range tcs {
tc.Do(t, datetime.DateQuarter)
}
}