Compare two TimeDate types

items file

DateTime    lastDoorOpen
DateTime    lastMotion
Switch doorTimer    { expire="60s,command=OFF" }
Contact contactDoor  "Door Sensor"    <door>      (ghome) {channel="zwave:device:ffe82412:node4:sensor_door"}
Switch  sensorMotion "Motion sensor"    <alarm> (ghome) {channel="zwave:device:ffe82412:node5:alarm_motion"}
Switch  andyIsHome

rules

rule "doorOpened"
when
	Item contactDoor changed from CLOSED to OPEN
then
	var timeX = new DateTimeType(now.toString)
	lastDoorOpen.sendCommand(timeX)
	doorTimer.sendCommand(ON)
end

rule "motioninhouse"
when
	Item sensorMotion changed from OFF to ON
then
	var timeY = new DateTimeType(now.toString)
	lastMotion.sendCommand(timeY)
end

rule "doorTimerExpired"
when
	Item doorTimer changed from ON to OFF
then
	var Number epoc1 = (lastDoorOpen.state as DateTimeType).calendar.timeInMillis
	var Number epoc2 = (lastMotion.state as DateTimeType).calendar.timeInMillis
	if (epoc1 < epoc2){
		andyIsHome.sendCommand(ON)
	}
	if (epoc1 > epoc2){
		andyIsHome.sendCommand(OFF)
	}
end

OK… this works but I feel like there should be a more elegant way to do this.
When the rules file loads, it also throws a warning about .calendar.timeInMillis being depreciated (I understand why and don’t care, it works) but loads the file anyhow

See Design Pattern: Time of Day

Looks like:
date1.isAfter(date2)

Warnings are just that, warnings. The Rule will still work.

However, a deprecation warning means that while it may work today, there may be some point in the future when it won’t work because that method or data member will be removed.

namraccr points to the more elegant way to do what you are after.

To get the epoch without the warning you would use:

(MyDateTimeItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli

which is documented here.

1 Like

ok, because learning by trying causes problems in openHAB (repeated file loads et.) let us walk thru this

orig rule

rule "doorTimerExpired"
when
	Item doorTimer changed from ON to OFF
then
	var Number epoc1 = (lastDoorOpen.state as DateTimeType).calendar.timeInMillis
	var Number epoc2 = (lastMotion.state as DateTimeType).calendar.timeInMillis
	if (epoc1 < epoc2){
		andyIsHome.sendCommand(ON)
	}
	if (epoc1 > epoc2){
		andyIsHome.sendCommand(OFF)
	}
end

new version from Rich’s doc
longer but doesn’t throw deprecation warning (untested)

	var Number epoc1 = (lastDoorOpen.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli
	var Number epoc2 = (lastMotion.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli

The cool thing here is we can use simple math operations to compare times. Time is expressed in milliseconds since epoch (1st Jan 1970). So if an event occurred earlier in time, the number will be lower. If the number is greater, the event occurred later (after) in time. hence:

	if (epoc1 < epoc2){
		andyIsHome.sendCommand(ON)
	}
	if (epoc1 > epoc2){
		andyIsHome.sendCommand(OFF)
	}

on to the next method.

I’m guessing (sorry openHAB dumb) this is only going to work with joda time so I must covert TimeDate type to joda time type. Link to Rich’s older (8/17) but still awesome type conversion thread points us to joda time conversion methods.

// Convert DateTimeType to Joda DateTime
val joda = new DateTime((MyDateTimeItem.state as DateTimeType).calendar.timeInMillis)

hmm… smells like millis with depreciated calender warning (untested)
newer and even more awesome time conversion thread by @anfaenger found here suggest 2 methods

val MyJodaFromDateTimeType_VariantA = new DateTime((MyDateTimeItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)

hmmm… uses zonedDateTime (avoids warning) but still smells like millis
second method

so then I’m hoping this will work

	var Number epoc1 = new DateTime(lastDoorOpen.state.toString)
	var Number epoc2 = new DateTime(lastMotion.state.toString)

then

	if (epoc1.isBefore(epoc2)){
		andyIsHome.sendCommand(ON)
	}
	if (epoc1.isAfter(epoc2)){
		andyIsHome.sendCommand(OFF)
	}

will work?

edit: additional question… is the

	var Number

needed… or even correct, is it a joda time not a number?
how about

	var epoc1 = new DateTime(lastDoorOpen.state.toString)
	var epoc2 = new DateTime(lastMotion.state.toString)

Regarding to section 4b of the DateTime Conversion post it is a joda time

val MyJodaFromDateTimeType_VariantB = new DateTime(MyDateTimeItem.state.toString)

I think, your code could also look like this

rule "doorOpened"
when
	Item contactDoor changed from CLOSED to OPEN
then
	lastDoorOpen.postUpdate(new DateTimeType)
	doorTimer.sendCommand(ON)
end

rule "motioninhouse"
when
	Item sensorMotion changed from OFF to ON
then
	lastMotion.postUpdate(new DateTimeType)
end

rule "doorTimerExpired"
when
	Item doorTimer changed from ON to OFF
then

    val JodaLastDoorOpen = new DateTime(lastDoorOpen.state.toString)
    val JodaLastMotion = new DateTime(lastMotion.state.toString)

    if ( JodaLastDoorOpen.isBefore(JodaLastMotion) ) {
		andyIsHome.sendCommand(ON)
    }


    if ( JodaLastDoorOpen.isAfter(JodaLastMotion) ) {
		andyIsHome.sendCommand(OFF)
    }
end
1 Like

The repeated file loads should not stop you from learning by trying.

That will work except you need to remove the Number.

Joda DateTime is not a Number. It’s a very complicated class that encapsulates most anything one would want to do with date times. Epoch, on-the-other-hand, is a Number because all it is is the number of milliseconds since midnight Jan 1 1970.