Update 4.2: JSScripting date comparisons

running OH 4.2.0-release on openHABian since upgrade from 4.1.2.

I’m trying to compare an JSON delivered date with the current time.
the item TIB_Prices is a JSON with the current value:

[
  {
    "startsAt": "2024-07-19T00:00:00.000+02:00",
    "total": 0.2934
  },
  {
    "startsAt": "2024-07-19T01:00:00.000+02:00",
    "total": 0.2819
  }#, ... all the other hours
]

The following comparison worked within OH 4.1.2:

var jetzt = time.LocalDateTime.now().minusMinutes(59);
var checkPrice = JSON.parse(items.getItem("TIB_Prices").state);

if (jetzt > checkPrice[0].startsAt) {
  // do something
}

which now throws an error:

2024-07-18 16:30:00.626 [ERROR] [n.script.javascript.checkItemChanges] - Failed to execute script: TypeError: A conversion from Temporal to a number is not allowed. To compare use the methods .equals(), .compareTo(), .isBefore() or one that is more suitable to your use case.
        at <js>.:anonymous(@openhab-globals.js:2)
        at <js>.letztesUpdateIstPassiert(<eval>:20)
        at <js>.:program(<eval>:34)
        at org.graalvm.polyglot.Context.eval(Context.java:399)
        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:458)
        ... 23 more
2024-07-18 16:30:00.627 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'checkItemChanges' failed: org.graalvm.polyglot.PolyglotException: TypeError: A conversion from Temporal to a number is not allowed. To compare use the methods .equals(), .compareTo(), .isBefore() or one that is more suitable to your use case.

So I have to rewrite all of that? but how? (I’m at a complete loss with the various date formats and dat comparisons). I tried to lookup “.isBefore”, which lead me to this monster (which supposedly works? :wink: )

var jetzt = time.toZDT(time.LocalDateTime.now().minusMinutes(59).toString());
var checkPrice = JSON.parse(items.getItem("TIB_Prices").state);

if (jetzt.isAfterDateTime(time.toZDT(checkPrice[0].startsAt)) {
  // do something
}

is it really THAT complex? or is there some better solution (I hope!)?
I did not find a way to “minusMinutes” or “plusHours” with time.toZDT… and do I have to convert into a zonedDateTime? I found an documentation for JavaScript, in which the “isBefore”-part was mentioned as “can also be string”?

No, that’s way overdone. time.toZDT() is your friend. See JavaScript Scripting - Automation | openHAB

var jetzt = time.toZDT('PT-59M`);

That’s an ISO8601 duration string. It will be converted to now plus that duration. Since it’s negative it will subtract from now. You can also use a plain old number which will be interpreted as milliseconds or a Quantity time.

var jetzt = time.toZDT(1000*60*59*-1);
var jetzt = time.toZDT(Quantity('-59 min'));

But I like the ISO8601 approach best. The Quantity approach works great though when you have a Number:Time Item and want to add it’s state to now. Though in that case you can just pass the Item and time.toZDT() will know what to do.

However, unfortunately you have to use isBefore and isAfter for simple comparisons. A js-joda ZonedDateTime isn’t a number so you can’t use the reguar math operators for comparisons.

But openhab-js has expanded on those operations by adding isBeforeTime and isAfterTime which only compares the time part and ignores the date, isBetweenTimes, isBetweenDateTimes which checks to see if the ZDRT is between the two passed in Times or DateTimes (the first one ignores the date).

These additions also support being passed anything supported by time.toZDT() too so you can do something like (note time.toZDT() returns now):

if(time.toZDT().isBetweenTimes('11:30 pm', '8:00 am')){

if(time.toZDT().isBetweenDateTimes(items.StartTimeItem, items.EndTimeItem)){

and so on.

So the rule in full becomes:

var jetzt = time.toZDT('PT-59M');
var checkPrice = JSON.parse(items.TIB_Prices.state);

if(jetzt.isAfter(time.toZDT(checkPrice[0].startsAt))){
  // do something
}

The table at JavaScript Scripting - Automation | openHAB is supposed to make it clear that passing any of the following will result in the value being passed being added to now (if the value passed is negative it’s a subtract):

  • number, bigint, Java Number, DecimalType (all these are assumed to be milliseconds)
  • Quantity or QuantityType with a Time unit
  • JavaScript or Java Item Object (if it’s a Number or Number:Time Item it’s state is added to now, if it’s a DateTime Item it’s state is converted to a ZDT)
  • ISO8601 duration string.

I welcome anything that can make that more clear in those docs.

Since OH timers require a ZonedDateTime and Rules DSL standardized on using a ZonedDateTime, it was natural for JS Scripting to also standardize on a ZonedDateTime. However, unlike Rules DSL, JS Scripting uses a JavaScript library that is close but not exactly the same. See Manual | js-joda (which is linked to in the JS Scripting docs as well).

But that’s why time.toZDT() was created. If it can be converted to a ZonedDateTime, time.toZDT() will convert it by parsing it or adding that value to now. The table I linked to above documents the assumptions and rules applied when different things are passed to time.toZDT() (e.g. how plain numbers are treated as milliseconds, if a date time string is passed without a timezone, the SYSTEM timezone is applied, etc.).

That’s a good question. It will definitely work with the new functions that were monkey patched into ZonedDateTime (see the isBetween et al functions mentioned above) but it doesn’t appear to work with the functions that come from js-joda itself like isBefore and isAfter.

For now you’ll have to convert it to a ZDT using time.toZDT() first as shown above or use the monkey patched functions which can take anything that time.toZDT() can handle.

var jetzt = time.toZDT('PT-59M');
var checkPrice = JSON.parse(items.TIB_Prices.state);

if(jetzt.isAfterDateTime(checkPrice[0].startsAt)){
  // do something
}

Or the even more terse

var checkPrice = JSON.parse(items.TIB_Prices.state);

if(time.toZDT('PT-59M').isAfterDateTime(checkPrice[0].startsAt)){
  // do something
}

Note: if the original rule was JS Scripting, I don’t see how it ever worked. Nothing discussed above is new for OH 4 and is how it’s worked since the early releases of JS Scripting in OH 3.x. I know because I actually wrote the first version of time.toZDT() as part of my OHRT and submitted it to openhab-js early on.

1 Like

Thanks. as usual you rock my lack of knowledge and organisational skills, Rich! :heart: