time.Duration.between() errors out on persisted timestamp (Javascript)

Need some help from our Javascript specialists. I’m converting my scripts from PY to JS and stepped upon strange issue. Basically I want to know how much time passed between now and the last time item was persisted. The code I had in PY which worked fine was translated to JS:

var lastTATime = items.getItem("itemName").persistence.lastUpdate();
var duration = time.Duration.between(time.ZonedDateTime.now(), lastTATime).seconds();

The second line crash with the strange error:

2025-02-02 22:00:46.205 [ERROR] [b.automation.script.file.mytestjs.js] - Failed to execute rule LivingRoomMotion01_test: RangeError: Maximum call stack size exceeded: RangeError: Maximum call stack size exceeded
	at from (/node_modules/openhab.js:2)
	at until (/node_modules/openhab.js:2)
	at between (@openhab-globals.js:2)
	at until (/node_modules/openhab.js:2)
	at between (@openhab-globals.js:2)
	at until (/node_modules/openhab.js:2)
	at between (@openhab-globals.js:2)
	at until (/node_modules/openhab.js:2)
	at between (@openhab-globals.js:2)

Basically the error means that something stuck in a loop and exceeded the number of executions which doesn’t make sense in my case.

Similar code:

var test = time.Duration.between(time.ZonedDateTime.now(), time.ZonedDateTime.now().plusHours(20)).seconds()

works fine returning correct number of seconds, so something with the ZonedDateTime coming from persistent.
Would also appreciate suggestions on how to achieve the same the way but still want to understand why Joda time is not working with persistent.

Thanks a lot,
K.

Which version of openHAB?

Please be aware that ZonedDateTime without a Timezone is deprecated since 4.2.2(?), so better use ZonedDateTime(ZoneId.systemDefault()) instead.

Can you please log the lastTATime? I would suspect it is null …

The DateTimeType change is not related to this issue here.

To expand on @florian-h05’s statement, if your Item’s current state students from the most recent state stored in persistence, OH cannot know when the last update was so it returnsnull.

This could be what’s happening here and a reason why the lastTATime would benull.

No it is not null, that’s the first thing I’ve checked:

2025-02-02 22:00:46.130 [DEBUG] [nhab.automation.openhab-js.mytest_js] - lastTATime: 2025-02-02T22:00:00.626-05:00[America/Toronto]

Latest 4.3.2, same error / behavior was in 4.2.0

No it is not null, this was check right away, I know about this.

The problem is that even this would generate the same error:

time.Duration.between(lastTATime, lastTATime)

Where it should return 0, somehow whatever returned from persistence is crashing “Duration”.

This however works fine:

var zdt = time.ZonedDateTime.now(time.ZoneId.of("Europe/Paris"));
var zdt_a = time.ZonedDateTime.now(time.ZoneId.of("America/Toronto"));
helper.log_debug("Paris: " + zdt);
helper.log_debug("Toronto: " + zdt_a);
helper.log_debug("Difference between Paris and Toronto: " + time.Duration.between(zdt, zdt_a).toHours().toString());
2025-02-03 14:47:19.891 [DEBUG] [nhab.automation.openhab-js.mytest_js] - Paris: 2025-02-03T20:47:19.889+01:00[Europe/Paris]
2025-02-03 14:47:19.891 [DEBUG] [nhab.automation.openhab-js.mytest_js] - Toronto: 2025-02-03T14:47:19.890-05:00[America/Toronto]
2025-02-03 14:47:19.895 [DEBUG] [nhab.automation.openhab-js.mytest_js] - Difference between Paris and Toronto: 0

It even calculated different Time Zones.

I’m at lost, missing something.

Thanks,
K.

Might be helpful

@florian-h05 , that looks like a Java ZDT. Joda-js’s toString shows the timezone as +/- hours from GMT, right?

Maybe the issue is with all the changes that have happened recently with lastUpdate, it broke the conversion to a joda-js’s ZTD.

@Konstantin_Panchenko , if I’m correct, passing it to time.toZDT() will convert that to a joda-js ZDT which Duration should be and to handle.

Rich, thank you for all your responses!!
So I’ve tried this:

helper.log_debug("Last Time ZDT + hours is: " + time.toZDT(lastTATime).plusHours(10));
helper.log_debug("Duration to ZDT: " + time.Duration.between(time.toZDT(lastTATime), time.toZDT(lastTATime).plusHours(10)).toHours().toString());

(helper is my own library of often used functions)

The result is:

2025-02-03 16:51:47.992 [DEBUG] [g.openhab.automation.openhab-js.time] - toZDT: Passing trough 2025-02-03T16:50:00.602-05:00[America/Toronto]
2025-02-03 16:51:47.993 [DEBUG] [nhab.automation.openhab-js.mytest_js] - Last Time ZDT + hours is: 2025-02-04T02:50:00.602-05:00[America/Toronto]
2025-02-03 16:51:47.999 [DEBUG] [g.openhab.automation.openhab-js.time] - toZDT: Passing trough 2025-02-03T16:50:00.602-05:00[America/Toronto]
2025-02-03 16:51:47.999 [DEBUG] [g.openhab.automation.openhab-js.time] - toZDT: Passing trough 2025-02-03T16:50:00.602-05:00[America/Toronto]
2025-02-03 16:51:48.033 [ERROR] [b.automation.script.file.mytestjs.js] - Failed to execute rule LivingRoomMotion01_test: RangeError: Maximum call stack size exceeded: RangeError: Maximum call stack size exceeded

I was thinking about Java to Joda conversion but not sure how to do one, toZDT just passing it through (yes, misspelled:)). Also error is not about type or type conversion but about calling function in the loop and eventually filling the stack.

Anything else I can test?
K.

I think this issue was there before that update happened, I was away and my OH was not updated I think since summer time, I’ve updated it yesterday to the latest and error is still the same.

Java ZDT and Joda ZDT are very similar but not identical. I could see Joda Duration becoming confused or otherwise failing in interesting ways if you pass it a Java ZDT. It wouldn’t necessarily just fail on it if they are similar enough.

But unless time.toZDT is also failing to recognize the Java Object and convert it, which I’m pretty sure worked at some point, my guess above isn’t correct and it is a Joda ZDT after all.

To see if time.toZDT is also broken, you can pass the toString to it instead of the Object. That for sure will guarantee you get a Joda zdt

var lastTATime = items.getItem("iComfortS30System_AwayModeStatus").persistence.lastUpdate().toString();
helper.log_debug(time.Duration.between(time.ZonedDateTime.now(), time.ZonedDateTime.now().plusHours(20)).seconds().toString());
helper.log_debug("Duration to ZDT: " + time.Duration.between(time.toZDT(lastTATime), time.toZDT(lastTATime).plusHours(10)).toHours().toString());

This worked fine, reparsing instead of passing object through:

2025-02-03 18:31:09.410 [DEBUG] [g.openhab.automation.openhab-js.time] - toZDT: Parsing string 2025-02-03T18:30:00.613-05:00[America/Toronto]
2025-02-03 18:31:09.415 [DEBUG] [g.openhab.automation.openhab-js.time] - toZDT: Parsing string 2025-02-03T18:30:00.613-05:00[America/Toronto]
2025-02-03 18:31:09.421 [DEBUG] [nhab.automation.openhab-js.mytest_js] - Duration to ZDT: 10

So is this a bug in Joda implementation of Duration? It looks like an obvious bug but I don’t think it is in our OH code.
Anyway this would work for my implementation, it is just very strange and error is strange too.

Thank you for your help Rich,
K.

Yes, this is a known thing but not in my case, it seems that Joda Duration got confused with ZonedDateTime from persistence for unknown to me reason, reparsing it from string works fine. Seems like a bug but I don’t know where.

Thanks,
K.

Assuming that the Java Object is not being converted to a joda-ja ZDT by the add-on or time.toZDT() the bug is in OH, not joda-js. By parsing the String, you have ensured that a joda-js ZDT Object is passed to Duration.

I think start by filing an issue on openhab-js to fix time.toZDT() and to check the persistence methods to make sure they convert the Java to a joda-js.

1 Like