Per openhab-addons PR#21160 and the AI analysis in the comments:
Specifically GenericItem.applyState.
Core PR#4351 added lastStateUpdate and lastStateChange to Items. However it looks like the assumption in applyState was that the notify and send event calls were simple calls and that changing lastState{Update,Change} afterwards was sufficient to let listeners see the old values (and presumably use "now" as the new although that assumes now is constant through the call chain - where is Mr Scott when you need him?). Since the listeners are queued/asynchronous callbacks that never worked - the chances of any of them getting scheduled, executed and making significant progress before applyState did the after-the-fact time change is probably slim but there's no guarantee which time they see. Nor that they all see the same time...
Core PR#4606 then explicitly adds the old lastState{Update,Change} values to StateChangedEvents but misses the race in late setting of the values.
I posit that the new values should be set before invoking listeners and events. They should see both the new time and new state on the Item. (See openhab-addons PR#21160.)
It's worth noting that setState immediately above applyState in GenericItem does update the times before invoking listeners and events but setState is only used when restoring state from persistence or (recently) rolling forward state for a forecast time series. Although in the time series case it looks wrong because it gives the new times to the State{Update,Changed}Events rather than the old as applyState does. Is that intended?
Finally, what happens if an Item receives two state updates at the same time? Perhaps because it is linked to two Channels? Or because restore from persistence happens at the same time as the binding sends an update? Should applyState and setState use synchronization?
Per openhab-addons PR#21160 and the AI analysis in the comments:
Specifically GenericItem.applyState.
Core PR#4351 added lastStateUpdate and lastStateChange to Items. However it looks like the assumption in applyState was that the notify and send event calls were simple calls and that changing lastState{Update,Change} afterwards was sufficient to let listeners see the old values (and presumably use "now" as the new although that assumes now is constant through the call chain - where is Mr Scott when you need him?). Since the listeners are queued/asynchronous callbacks that never worked - the chances of any of them getting scheduled, executed and making significant progress before applyState did the after-the-fact time change is probably slim but there's no guarantee which time they see. Nor that they all see the same time...
Core PR#4606 then explicitly adds the old lastState{Update,Change} values to StateChangedEvents but misses the race in late setting of the values.
I posit that the new values should be set before invoking listeners and events. They should see both the new time and new state on the Item. (See openhab-addons PR#21160.)
It's worth noting that setState immediately above applyState in GenericItem does update the times before invoking listeners and events but setState is only used when restoring state from persistence or (recently) rolling forward state for a forecast time series. Although in the time series case it looks wrong because it gives the new times to the State{Update,Changed}Events rather than the old as applyState does. Is that intended?
Finally, what happens if an Item receives two state updates at the same time? Perhaps because it is linked to two Channels? Or because restore from persistence happens at the same time as the binding sends an update? Should applyState and setState use synchronization?