DateTime calculations/formatting in a rule

Hey chaps & chapettes,
I’m having a hard time transforming DateTime into something I can work with.
What I’m trying to do:
Send a push notification at 6pm in case the garbage will be picked up the next day. I have “garbage collection” as a datetime object in OH (MuellTime0 ). So I’ve started to code the following rule:

import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import org.joda.time.*
import java.util.Date
import java.lang.Double
import org.openhab.core.types.Command
import java.lang.Math
import java.util.Date
import java.util.Calendar
import java.util.GregorianCalendar

rule “Bring garbage outside”
when
Time cron “00 00 18 ? * *”
then
var DateTime alarm = now.plusHours(7) as DateTime
logInfo(“Muell Erinnnerung”, "MuellTime0 : " + MuellCalTime0.state)
logInfo(“Muell Erinnnerung”, "Now : " + now)
logInfo(“Muell Erinnnerung”, "Now +7h : " + alarm)
if (alarm.isAfter(MuellCalTime0.state)) {
logInfo(“Muell Erinnnerung”, “Muelltonne muss raus”)
} else {
logInfo(“Muell Erinnnerung”, “Muelltonne kann stehen bleiben”)
}

Logfile sais:

2016-04-28 13:52:00.267 [INFO ] [model.script.Muell Erinnnerung] - MuellTime0 : 2016-04-28T00:00:00
2016-04-28 13:52:00.327 [INFO ] [model.script.Muell Erinnnerung] - Now : 2016-04-28T13:52:00.327+02:00
2016-04-28 13:52:00.380 [INFO ] [model.script.Muell Erinnnerung] - Now +7h : 2016-04-28T20:52:00.114+02:00
2016-04-28 13:52:00.439 [ERROR] [.o.m.r.i.engine.ExecuteRuleJob] - Error during the execution of rule Bring garbage outside
java.lang.IllegalStateException: Could not invoke method: org.joda.time.base.AbstractInstant.isAfter(long) on instance: 2016-04-28T20:52:00.114+02:00
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:738) ~[na:na]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._featureCallOperation(XbaseInterpreter.java:713) ~[na:na]
at sun.reflect.GeneratedMethodAccessor50.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_77]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_77]
at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
at org.openhab.model.script.interpreter.ScriptInterpreter.internalFeatureCallDispatch(ScriptInterpreter.java:69) ~[na:na]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateMemberFeatureCall(XbaseInterpreter.java:549) ~[na:na]
at sun.reflect.GeneratedMethodAccessor51.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_77]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_77]
at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateIfExpression(XbaseInterpreter.java:327) ~[na:na]
at sun.reflect.GeneratedMethodAccessor91.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_77]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_77]
at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateBlockExpression(XbaseInterpreter.java:321) ~[na:na]
at sun.reflect.GeneratedMethodAccessor59.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_77]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_77]
at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) ~[na:na]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) ~[na:na]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:204) ~[na:na]
at org.openhab.model.script.internal.engine.ScriptImpl.execute(ScriptImpl.java:59) ~[na:na]
at org.openhab.model.rule.internal.engine.ExecuteRuleJob.execute(ExecuteRuleJob.java:55) ~[na:na]
at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [quartz-all-2.1.7.jar:na]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:557) [quartz-all-2.1.7.jar:na]
Caused by: java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_77]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_77]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_77]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_77]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:729) ~[na:na]
… 29 common frames omitted

As you can see my variables/timestamps are filled ok but the if comparrison does not work. Can anyone give me a hint how to make it work?
Thanks much!

Have a look at my rule for determining time of day:

rule "Get time period for right now"
when
System started
then
val morning = now.withTimeAtStartOfDay.plusHours(MorningStart).millis
val night = now.withTimeAtStartOfDay.plusHours(NightStart).millis
val sunrise = new DateTime(getAstroSunriseStart(now.toDate, lat, lon))
val evening = new DateTime(getAstroSunsetStart(now.toDate, lat, lon))
val twilight= evening.minusMinutes(TwilightStart)

// Set the time variables (once), and their events OFF
var DateTime morningtime=now.withTimeAtStartOfDay.plusHours(MorningStart)
if(now.getHourOfDay>MorningStart) {
	// morning has passed add 24 hours
	morningtime=morningtime.plusHours(24)
}
var DateTime nighttime=now.withTimeAtStartOfDay.plusHours(NightStart)
postUpdate(Morning_Time, new DateTimeType(morningtime.toCalendar(null)))
postUpdate(Night_Time, new DateTimeType(nighttime.toCalendar(null)))
Morning_Event.sendCommand(OFF)
Night_Event.sendCommand(OFF)

// Determine TimeOfDay state
if(now.isAfter(morning) && now.isBefore(sunrise)) {     
	TimeOfDay.sendCommand("morning")
	callScript("morning")	
} else if(now.isAfter(sunrise) && now.isBefore(twilight)) {
	TimeOfDay.sendCommand("day")
	callScript("day")
} else if(now.isAfter(twilight) && now.isBefore(evening)) { 
	TimeOfDay.sendCommand("twilight")
	callScript("twilight")
} else if(now.isAfter(evening) && now.isBefore(night))  {  
	TimeOfDay.sendCommand("evening")
	callScript("evening")
} else if(now.isAfter(night)) {                                                 
	TimeOfDay.sendCommand("night")
	callScript("night")
}

// Create the necessary timers
if(TimeOfDay.state=="night") {
	// Create timer for in the morning
	if(tMorning!=null) {
		logInfo("TOD","Cancelling morning timer")
		tMorning.cancel()
	}
	tMorning = createTimer(morningtime) [|
		logInfo("TOD","Timer tMorning executed")
		Morning_Event.sendCommand(ON)
		]
	logInfo("TOD","Morning timer created")
} else {
	// Create timer for the night
	if(tNight!=null) {
		logInfo("TOD","Cancelling night timer")
		tNight.cancel()
	}
	tNight = createTimer(nighttime) [|
		logInfo("TOD","Timer tNight executed")
		Night_Event.sendCommand(ON)
		]
	logInfo("TOD","Night timer created")
}

end

This works like a charm.

Hey Gerhard,
thanks for your reply. Not sure if I got your example correctly. You’re suggesting to convert both DateTime’s into millis to do proper “greater than” comparrison?
For my alarm var thats easy, but how do I put the DateTime object into such millis?

Or did I get your idea completly wrong?
Thanks for your help.

As you can see from your log MuellCalTime0.state is casted to 2016-04-28T00:00:00 which is a formatted datetime and you cant do calculations with that. Am not that experienced with java and jodatime, but maybe you can give the following a go:
add var Muell = new DateTime(MuellCalTime0.state) and then change your alarm.isafter to this variable. This is similar to my sunrise and evening variables.

One small point: A DateTime item in openHAB is not at all the same thing as a DateTime object from org.joda.time. They unfortunately share the same name, but they aren’t actually related.

A DateTime item in openHAB usually has a state of class DateTimeType. When you have a DateTimeType, you can reference its java.util.Calendar member using .calendar in a rule. Then you can access this calendar’s properties, such as .timeInMillis. Then you can use that returned long to create or modify org.joda.time.base.AbstractInstant instances. For example:

import org.joda.time.*
import org.openhab.core.library.types.DateTimeType

rule MyRule
when
  Item BinMenArrive changed
then
  val DateTime binMenArrive = new DateTime((BinMenArrive.state as DateTimeType).calendar.currentTimeMills)
  if (now.isBefore(binMenArrive)) {
    logInfo("MyRule", "put the bins out!")
  } else {
    logWarn("MyRule", "too late; have to wait a week.")
  }
end

Hey guys,
many thanks for your help. I have the following rule in place now which works ok.

rule “Bring garbage outside”
when
Time cron “00 00 18 ? * *”
//Time cron “00 * * ? * *”
then
//The time from NOW until alarm
val alarm = now.plusHours(7).millis
//The time from NOW until garbage event happens
var MuellTime0Millis = (MuellCalTime0.state as DateTimeType).calendar.timeInMillis
//logInfo(“Muell Termin”, "MuellTime0 : " + MuellCalTime0.state)
//logInfo(“Muell Termin”, "MuellTime0.Millis : " + MuellTime0Millis)
//logInfo(“Muell Termin”, "Now +7h : " + now.plusHours(7))
//logInfo(“Muell Termin”, “Now +7h.Millis : " + alarm)
if (alarm > MuellTime0Millis) {
logInfo(“Muell Termin”, “Muelltonne muss raus: " + MuellCalName0.state.toString )
sendBroadcastNotification(“Morgen (”+MuellCalTime0.state+”) wird die "” + MuellCalName0.state.toString + “" Tonne geleert”)
} else {
logInfo(“Muell Termin”, “Muelltonne kann stehen bleiben”)
}
end

Only thing I have to fiddle now is that my events are 24h events and I get a message in case the bins have been collected the same morning already. But that should be fairly easy with now.getDayofweek.millis I guess.
Again, thanks for your help!