Need to Store, Do Math and Trigger from Date Variables

I changed the title of the post before anyone responded because I think it better describes what what is needed. I also added the 3rd to last paragraph about possibly extending the Time Variable functionality to Dates.

I am thinking about how to best migrate over from ST where I have a number of date related actions being run by WebCore today.

In Rule Machine 4, can I have a rule that dynamically calculates and stores dates, in a variable, and can rule machine be triggered from a date stored in a variable?

For example, I would trigger a rule on the first day of each year that then calculates, for example, the 3rd Sunday of June, and store that date in a global variable. I see that triggers have the concept of triggering on a specific dynamically determined date, such as the 3rd Sunday of June, so this type of logic exists in Rule Machine, but I don't currently see any way to use this logic in an action to store such a dynamically calculated date into a variable, or to trigger form a variable containing a date.

Then I would like to use that stored date variable for any number of purposes.

For example, trigger a rule when the date is reached.

Or for example, calculate a countdown of the days to that stored date. I believe there is the concept of simple variable math, so maybe something like [%stored-date% - Now()] could determine the number of days until the stored date. Can this countdown be calculated in an action, and the result stored in a global variable?

I would then like to, for example, display the countdown on a dashboard.

I would also like to, for example, trigger another rule when the countdown is within X days.

I realize that I could just determine all the X day away trigger dates by hand and manually set individually triggered rules based on these manual calcs. For example if I wanted to do something 7 days prior, I could set a trigger for the 2nd Sunday of June. But then I wouldn't have the automatic countdown, and will have many, many rules that would have to be adjusted each time I changed the desired stored date, instead of just changing the rule that calculates the stored value. This seems all very manual, when the purpose of the platform is to automate things.

It looks like all these capabilities were added for Time variables a few releases ago. Right now I think the only explicit variable Date logic is to set a string variable to the current date with %date%, or to set a variable to Unix Time with Now(), however. I don't think we can do Math on a string variable, and it doesn't look like there is enough Math capability to calculate dates from a stored Unix Time variable, but maybe I just haven't pondered this enough. But the fundamental logic should already be in Rule Machine to use the Date portion of the Unix Time variable as it is likely used int eh Time variables. If the new Time variable functionality were extended to Dates, then I think we would just need to borrow the logic from periodic trigger determinations as one of the action options to set a date. I believe all the elements would then be there to do what I'm searching for, but I'll keep searching and thinking about it.

Is this possible in the way I am asking without extending the Time functionality to Dates, or is there a clever workaround that doesn't require manually calculating and entering all the dates etc?

Thanks in advance for the advice!

No not any way I can think of. RM is only triggered by events, and variables only make events when changed.

But maybe someone else knows a way I do not, and will chime in.

Edit: you might be able to do a rule that simply repeats periodically forever, and check the date/time variable in the conditions though. If not go time do nothing, other wise do something.

1 Like

So you can use variable changes as I trigger. I used to....

Yes, but you can't use a variable for a future trigger directly. A change to a variable makes an event at that time.

Correct.

I corrected the wording in my original post. Thanks! The nuances matter...

1 Like

I am moving over from ST myself. So far I have only moved my shop but I am going to start on the house soon.

You say you currently run the apps you need in Webcore, so why not just use Webcore in Hubitat.

It works pretty well.

1 Like

Good point! A perfectly viable option. Just don't go crazy - there aren't "infinite" resources in Hubitat webcore like there is when running it in the cloud... You need to consider how you make your pistons and load on hub much more carefully in Hubitat webcore.

The primary reason to move over to Hubitat is to get local, and I really want to find a way to make this work locally, with supported (or at least non frowned upon) apps. So I thought about loading WebCore, but it is frowned upon, and still uses a server. I also thought about HubConnect in conjunction with my ST hub, but that seems to defeat the purpose of moving over to Hubitat.

It would be nice if RM implemented the date functions to allow this, but in the mean time, I have an idea in my mind of how I might chink away at this goal. For example by triggering rules to store Unix Time via now() and then doing a bunch of incremental math on the variables, but it's not fully developed. I'll try to post some ideas later on this, and see what the community thinks about it.

Well, the Hubitat webcore pistons all run locally - no extra server/internet required for that part.

But as with anything, there are multiple ways do do anything - everything from webcore, RM, write your own groovy app (you could definitely do what you want in a custom app), do the logic in Node-red, etc. You will need to decide the path that is best for you.

Perhaps triggering based on time would suffice? You store the variable, trigger based on time and then use the variable in the condition?

1 Like

I was thinking something along those lines too. That would be a little more elegant than just running an RM rule 1x/min (or at whatever frequency) and checking the variable over and over I suppose.

1 Like

What I have in my mind for a trigger is something like indicated below by storing Unix timestamps at specific times, calculating differences, and triggering a rule when the differences equal some other desired number that can also be set with a variable:

  1. Store a Variable with the desired month, day and week of month (using tokens), or in 3 variables
  2. On the first day of the year after midnight, store the Now() Unix timestamp in a number variable.
  3. Determine the future Unix timestamp by manipulating the beginning of year timestamp to add amounts for the number of months, days and weeks to the target date using simple math (hopefully)
  4. Store the future calculated Unix timestamp in a variable as "target"
  5. Trigger a rule each night after midnight to store a now() Unix Time stamp as "current" in a number variable.
  6. Trigger another rule on the change in the "current" variable to calculate the difference in the "target" and "current" timestamps
  7. divide this result by 86,400 (seconds in a day) and save this as a "countdown days" in a number variable.
  8. When a "countdown days" variable changes, trigger a rule
  9. The rule has a condition if "countdown days" = some other number specified in a variable
  10. If true, complete actions, else nothing

With this logic, I should also be able to store target dates, calculate countdowns, and effectively trigger rules on dates, by using the countdown as a proxy for the date.

What I need to determine is the math to work with Unix timestamps and see if RM has enough math functions to calculate the future date timestamps.

Another "simpler" way of doing this, since triggers already have all the settings necessary to calculate a dynamic date in the Periodic Schedule selection, and what I am really looking for is a way to determine how far we are from dynamically created dates, would be for a rule to store that calculated trigger date as a Unix timestamp in a variable that could accessed by other rules (maybe an automatically created global variable). I could then set a rule trigger for the date to be managed around, which replaces steps 1-4 above, and the rest of the steps are the same to get to the desired result. @bravenel, could the capability be added to store the next calculated trigger Unix timestamp from a Periodic Schedule trigger of a rule into a Global Variable (maybe an automatically created one for every rule)?

I will look at this. Not sure at this point...

So after much tinkering, I've been able to implement a rule that now does steps 1-4 with a normally specified date MM-DD-YYYY in the Global Variable GV_TD-G_1, and returns a Unix timestamp into the global variable named GV_TD-U_1 in seconds. This was done the hard way with straight math calcs, and no nice Year(), Month(), Day() type functions available.

If the YYYY = 0000, then the the Unix timestamp is for the current year (at the time the calculation rule is triggered), for the MM-DD-0000 specified. This is so a date can be specified to calculate for a very specific date in if YYYY is included, and therefore it only needs to be calculated once, or every year on MM-DD if YYYY = 0000. For YYYY=0000, then you would just trigger the rule to calculate at the beginning of each year, and it should run perpetually. The date must be in the form MM-DD-YYYY, and include each section or the token calc in the rule errors out of range, and nothing else calculates

Now that a target date is stored in a variable, steps 5-10 should be possible, at least for completely specified dates.

The next step is to try to calculate Unix timestamps for the Nth day of the week in a month.

I have to say implementing this in RM was a bit painful with the interface mechanics, but the calcs use fairly simple operators, so it was possible to pull off. It would definitely be easier if the RM logic stored this variable for a set trigger event on its own. The logic for the math came from this website: chrono-Compatible Low-Level Date Algorithms plus some adjustment to get to work well first in Excel, then in RM.

I tested this for a long range of dates from 1-1-1901 to 12-31-2100+ in Excel, and the math seems to hold. So this should be a durable formula to implement, but there will be a Unix timestamp issue in 2038 if there is no switch to 64 bit...

For example, if I enter:
05-20-2025
The Unix timestamp returns:
1747699200
Which validates as the Unix timestamp for:
Tue, 20 May 2025 00:00:00
As verified by this site: https://www.textmagic.com/free-tools/timestamp-converter

Right now I have the calculation rule set to trigger on the change in the GV_TD-G_1 variable, which means, I can see the Unix timestamp returned in the Global Variables List as soon as I change the Target Date.

Global Variables:

Local Variables:

Rule:

Would be happy to hear is anyone has a simpler way to do this without altering RM.

I was able to also create a rule to display a Gregorian date in the format MM-DD-YYYY from a Unix timestamp. This works to convert a Unix Timestamp back to the date produced by the Rule to calculate a Unix timestamp from a Gregorian date, or any Unix timestamp, including ones produced by the Time variable Now(). However, when I retrieve the Now() variable from the system, it appears to be pulling GMT time even though my Hubitat is set to US/Eastern.

The actual math driving these calculations works for a very large range of dates, however, it appears as if Hubitat may be a 32 bit platform, or the Number variables are limited to 32 bit, as entering any Unix timestamp for a date beyond January 19, 2038, produces a date that circulates back counting up from December 14, 1901 where it left off at January 19, 2038, which is known as the "Year 2038 problem" for Unix type systems. Note that the Rule to create the Unix timestamp from a Gregorian date just stops incrementing the Unix timestamp beyond January 19, 2038. Also note the same is true for dates prior to December 14, 1901 for the same reason, not that anyone is likely interested in a Unix timestamp with that early of an underlying date.

To show how it works, I triggered the U->G rule to trigger from a change in the variable that outputs the G->U conversion. The result is that a change in the Gregorian target date in the Global Variables screen, almost immediately displays the Unix timestamp, and a decoded Unix timestamp confirming the original date entered.

Global Variables:


Note the GV_Decimal_1 variable that is displaying a now() Unix timestamp, and that converts to ~45minutes after midnight on 5-10-2020.

Local Variables:

Rule:

Entering a date of 02-29-2040, produces Unix timestamp stopped at January 19, 2038 :

A really simple way to do this problem:
Please look at "scheduled switch" from the Cobra Apps.
It handles your situation perfectly with ease!