diff --git a/README.md b/README.md index 8897e97..3d40f4a 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ go get github.com/goglue/rulesengine | DateBetween | Date / Time | Checks is a time is between a given range. | | WithinLast | Date / Time | Checks is a time is within the last X of a given duration. | | WithinNext | Date / Time | Checks is a time is within the next X a given duration. | +| YearEq | Date / Time | Checks is a time is in a given year. | +| MonthEq | Date / Time | Checks is a time is in a given month. | | Any | Array | Returns `true` if any element statisfy the given rule. | | All | Array | Returns `true` if all elements satisfy the given rule. | | None | Array | Returns `true` if no elements satisfy the given rule. | diff --git a/helper.go b/helper.go index 9421d90..3bb09c0 100644 --- a/helper.go +++ b/helper.go @@ -282,6 +282,26 @@ func isWithinTime(val any, duration any, op Operator) (bool, error) { return false, nil } +func compareTimePart(actual any, expected any, op Operator) (bool, error) { + t, ok := actual.(time.Time) + if !ok { + return false, newError(errType, actual) + } + value, err := toFloat(expected) + if err != nil { + return false, err + } + target := int(value) + + switch op { + case YearEq: + return t.Year() == target, nil + case MonthEq: + return int(t.Month()) == target, nil + } + return false, nil +} + func resolveExpectedTime(expected any, now time.Time) (time.Time, error) { switch v := expected.(type) { case time.Time: diff --git a/operators.go b/operators.go index 4b0262c..048aa3d 100644 --- a/operators.go +++ b/operators.go @@ -46,6 +46,8 @@ const ( DateBetween Operator = "DATE_BETWEEN" WithinLast Operator = "WITHIN_LAST" WithinNext Operator = "WITHIN_NEXT" + YearEq Operator = "YEAR_EQ" + MonthEq Operator = "MONTH_EQ" // Arrays Any Operator = "ANY" diff --git a/rulesengine.go b/rulesengine.go index 94a6054..fab7662 100644 --- a/rulesengine.go +++ b/rulesengine.go @@ -255,6 +255,12 @@ func evaluateRule(operator Operator, actual, expected any) (bool, error) { } return true, nil + case YearEq, MonthEq: + if res, err := compareTimePart(actual, expected, operator); !res || err != nil { + return res, err + } + return true, nil + // ---------- Null / Existence ---------- case IsNull, NotExists: if actual != nil { diff --git a/rulesengine_test.go b/rulesengine_test.go index b3e5e74..9c435c2 100644 --- a/rulesengine_test.go +++ b/rulesengine_test.go @@ -347,6 +347,27 @@ var ( inputData: map[string]any{"startDate": relativeTime}, expResult: RuleResult{Result: true}, }, + { + ruleNodes: Rule{ + Operator: YearEq, Field: "startDate", Value: relativeNow.Year(), + }, + inputData: map[string]any{"startDate": relativeTime}, + expResult: RuleResult{Result: true}, + }, + { + ruleNodes: Rule{ + Operator: YearEq, Field: "startDate", Value: 2020, + }, + inputData: map[string]any{"startDate": relativeTime}, + expResult: RuleResult{Result: false}, + }, + { + ruleNodes: Rule{ + Operator: MonthEq, Field: "startDate", Value: int(time.June), + }, + inputData: map[string]any{"startDate": relativeTime}, + expResult: RuleResult{Result: true}, + }, { ruleNodes: Rule{ Operator: WithinLast, Field: "startDate", Value: last10Sec,