-
Couldn't load subscription status.
- Fork 505
-
Is there a method to enable the json-rules-engine to determine "if current date is lessThan 2025年06月30日"?
I can pass the current date as a fact, but how can I compare a date similar to how you can compare numbers?
Beta Was this translation helpful? Give feedback.
All reactions
So I tried using it like:
engine.addOperatorDecorator('dateValue', (factValue, value, next) => next(factValue, new Date(value).getTime()));
And I'm setting a fact like:
currentDate = new Date().getTime();
If you log that, you get the ms since Unix epoch:
currentDate 1738883049146
And then a rule like:
{
"all": [
{
"fact": "currentDate",
"path": "",
"value": "2025-02-05",
"operator": "dateValue:greaterThan"
}
]
}
And it works as expected with:
'lessThan', 'greaterThan'
But for most practical purposes, 'equal', 'notEqual', 'lessThanInclusive', 'greaterThanInclusive' are not going to be useful then, as you're essentially never going to be comparing exact...
Replies: 2 comments 6 replies
-
I tried adding the current date as a fact:
currentDate: new Date().toISOString().split('T')[0]
And then reference that in my rule as so:
{
"all": [
{
"fact": "currentDate",
"path": "",
"value": "2025-01-29",
"operator": "lessThan"
}
]
}
And it seemed to work at first, but doesn't work fully.
Beta Was this translation helpful? Give feedback.
All reactions
-
Are you saying then that I'd need to pass the numeric value (seconds since Unix epoch) in the rule AND as the fact?
For the "fact" it doesn't matter, but in the rule, I like them to be human readable if possible.
Or is there a way to make the value in the rule get processed as a date at runtime? (e.g. new Date(rule.value).getTime() - only if the "fact" in the rule === "currentDate")
Beta Was this translation helpful? Give feedback.
All reactions
-
The only "built-in" way to do this is by having the number in the value field let's assume you don't want to do that here are some other options. Sorted from "worst" to "best"
Custom Fact
A Fact that parses the human readable version from it's params
You need to call engine.addFact before running the rules.
Downside - it's super ugly
const dateFact = new Fact("date", ({ value }) => new Date(value).getTime()); ... { "all": [ { "fact": "currentDate", "path": "", "value": { "fact": "date", "params": { "value": "2025-01-29" } }, "operator": "greaterThan" } ] }
Custom Operators
Operators that expect a string as one input and turn them into date objects and then do the comparison
You need to call engine.addOperator before running the rules
Downside - you need to build all the comparisons
const dateLessThan = new Operator('dateGreaterThan', (factValue, value) => factValue > new Date(value).getTime()); ... { "all": [ { "fact": "currentDate", "path": "", "value": "2025-01-29", "operator": "dateGreaterThan" } ] }
Operator Decorator
An operator decorator that transforms the value into a timestamp
You need to call engine.addOperatorDecorator before running the rules
Downside - a little ugly
const dateValue = new OperatorDecorator('dateValue', (factValue, value, next) => next(factValue, new Date(value).getTime())); ... { "all": [ { "fact": "currentDate", "path": "", "value": "2025-01-29", "operator": "dateValue:greaterThan" } ] }
Beta Was this translation helpful? Give feedback.
All reactions
-
Thanks for the suggestions; I'll have to decide the route I should take...
Beta Was this translation helpful? Give feedback.
All reactions
-
Just to clarify the reason I'd prefer the Operator Decorator is that it make all the other operators "just work" so lessThan, greaterThan, lessThanEqual, equal, notEqual
It's also composable so you could have a list of dates and do something like this:
{
"all": [
{
"fact": "currentDate",
"path": "",
"value": [
"2025年01月29日",
"2024年01月30日"
],
"operator": "someValue:dateValue:equals"
}
]
}Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
Adding a separate answer.
Beta Was this translation helpful? Give feedback.
All reactions
-
So I tried using it like:
engine.addOperatorDecorator('dateValue', (factValue, value, next) => next(factValue, new Date(value).getTime()));
And I'm setting a fact like:
currentDate = new Date().getTime();
If you log that, you get the ms since Unix epoch:
currentDate 1738883049146
And then a rule like:
{
"all": [
{
"fact": "currentDate",
"path": "",
"value": "2025-02-05",
"operator": "dateValue:greaterThan"
}
]
}
And it works as expected with:
'lessThan', 'greaterThan'
But for most practical purposes, 'equal', 'notEqual', 'lessThanInclusive', 'greaterThanInclusive' are not going to be useful then, as you're essentially never going to be comparing exactly that ms of time. I only want to look at the actual days involved, not the time. And I want the same results whether it's 5am or 10pm local time - not based on UTC. If it's being used to set something based on the date, it needs to be the date where I live.
So I've setup a function like this (yeah, vanilla JavaScript sucks with handling dates, and I could shorten this, but like this for clarity):
function getStartOfCentralTimeDayInMs(dateOnlyString) {
let localDateTimeString;
if(dateOnlyString){
// console.log("dateOnlyString",dateOnlyString);
const localDate = new Date(dateOnlyString+'T00:00:00.000Z'); // Fake UTC for comparison
localDateTimeString = localDate.toLocaleString("en-US", {
timeZone: "UTC",
});
// console.log("localDateTimeString fact",localDateTimeString);
} else { // Get the date and time locally / current
localDateTimeString = new Date().toLocaleString("en-US", {
timeZone: "America/Chicago",
});
// console.log("localDateTimeString current",localDateTimeString);
}
// Create a new Date object from the local time string
const localDate = new Date(localDateTimeString);
// Set the time to the beginning of the day (00:00:00.000)
localDate.setHours(0, 0, 0, 0);
// Get the date in milliseconds
const startOfLocalDayInMs = localDate.getTime();
return startOfLocalDayInMs;
}
And then set the fact as this:
currentDate: getStartOfCentralTimeDayInMs(null)
And then setup the addOperatorDecorator like this:
engine.addOperatorDecorator('dateValue', (factValue, value, next) => {
const ruleValue = getStartOfCentralTimeDayInMs(value);
return next(factValue, ruleValue);
});
(not a one-liner so that I can log values within the function for testing)
And the rule can be the same as above.
Then my date numeric values are for the start of the day in the local timezone, and look something like: 1738908000000
This lets me use any of these for date logic:
'lessThan', 'greaterThan', 'equal', 'notEqual', 'lessThanInclusive', 'greaterThanInclusive'
Hopefully this helps someone else.
@chris-pardy Thanks for your help on this - would it be worthwhile adding this to the documentation for handling dates?
Beta Was this translation helpful? Give feedback.