OH 3 Time converting issue

Hi, i migrated from OH2 to 3 and since then im strugling to get some of my rules back working.
i know it’s an Time converting issue but after reading and changing the rules i made it only worse.

hope someone could bring some light in this.

thanks a lot in advance

Matt

rule "Tage bis Abfall berechnen"
when 
	Time cron "0 5 * 1/1 * ? *" or
	Item Restmuell changed or
	Item Biomuell changed or
	Item Papiermuell changed or
	Item GelberSack changed or
	Item DelayedStartup changed from OFF to ON
then
	logInfo("all.rules", "Tage bis Abfall berechnen")
	//Restmuell
	val DateTime varRestmuellDatum = new DateTime((Restmuell.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli).withTimeAtStartOfDay
	logInfo("all.rules", varRestmuellDatum)
	val Long varRestmuellDiff = Math::round((varRestmuellDatum.millis - now.withTimeAtStartOfDay.millis) / (86400000.0))
	if ( varRestmuellDiff == 0 ) {
		RestmuellTage.postUpdate("heute")
	} else if ( varRestmuellDiff == 1 ) {
		RestmuellTage.postUpdate("morgen")
	} else {
		RestmuellTage.postUpdate(String::format("(%1$d T.)", varRestmuellDiff))
	}
	//Biomuell
	val DateTime varBiomuellDatum = new DateTime((Biomuell.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli).withTimeAtStartOfDay
	val Long varBiomuellDiff = Math::round((varBiomuellDatum.millis - now.withTimeAtStartOfDay.millis) / (86400000.0))
	if ( varBiomuellDiff == 0 ) {
		BiomuellTage.postUpdate("heute")
	} else if ( varBiomuellDiff == 1 ) {
		BiomuellTage.postUpdate("morgen")
	} else {
		BiomuellTage.postUpdate(String::format("(%1$d T.)", varBiomuellDiff))
	}
	//Papiermuell
	val DateTime varPapiermuellDatum = new DateTime((Papiermuell.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli).withTimeAtStartOfDay
	val Long varPapiermuellDiff = Math::round((varPapiermuellDatum.millis - now.withTimeAtStartOfDay.millis) / (86400000.0))
	if ( varPapiermuellDiff == 0 ) {
		PapiermuellTage.postUpdate("heute")
	} else if ( varPapiermuellDiff == 1 ) {
		PapiermuellTage.postUpdate("morgen")
	} else {
		PapiermuellTage.postUpdate(String::format("(%1$d T.)", varPapiermuellDiff))
	}
	//GelberSack
	val DateTime varGelberSackDatum = new DateTime((GelberSack.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli).withTimeAtStartOfDay
	val Long varGelberSackDiff = Math::round((varGelberSackDatum.millis - now.withTimeAtStartOfDay.millis) / (86400000.0))
	if ( varGelberSackDiff == 0 ) {
		GelberSackTage.postUpdate("heute")
	} else if ( varGelberSackDiff == 1 ) {
		GelberSackTage.postUpdate("morgen")
	} else {
		GelberSackTage.postUpdate(String::format("(%1$d T.)", varGelberSackDiff))
	}	
end

rule "Abfalltermine sortieren"
when
	Item Restmuell received update or
	Item Biomuell received update or
	Item Papiermuell received update or
	Item GelberSack received update or
	Item RestmuellTage changed or
	Item PapiermuellTage changed or
	Item BiomuellTage changed or
	Item GelberSack changed or
	//Time cron "0 8 0 1/1 * ? *" or
	Item DelayedStartup changed from OFF to ON
then
	Thread::sleep(3000)
	var DateTime varTempDatum
	var DateTime varAbfall1Datum = new DateTime((Restmuell.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
	var DateTime varAbfall2Datum = new DateTime((Biomuell.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
	var DateTime varAbfall3Datum = new DateTime((Papiermuell.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
	var DateTime varAbfall4Datum = new DateTime((GelberSack.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
	var String varTempArt
	var String varAbfall1Art = "Restmuell"
	var String varAbfall2Art = "Biomuell"
	var String varAbfall3Art = "Papiermuell"
	var String varAbfall4Art = "GelberSack"
	var String varTempTage
	var String varAbfall1Tage = RestmuellTage.state.toString
	var String varAbfall2Tage = BiomuellTage.state.toString
	var String varAbfall3Tage = PapiermuellTage.state.toString
	var String varAbfall4Tage = GelberSackTage.state.toString
	var i = 1
	while ((i=i+1) < 6) {
		if (varAbfall1Datum > varAbfall2Datum) {
			varTempDatum = varAbfall1Datum
			varAbfall1Datum = varAbfall2Datum
			varAbfall2Datum = varTempDatum
			varTempArt = varAbfall1Art
			varAbfall1Art = varAbfall2Art
			varAbfall2Art = varTempArt
			varTempTage = varAbfall1Tage
			varAbfall1Tage = varAbfall2Tage
			varAbfall2Tage = varTempTage
			if (varAbfall2Datum > varAbfall3Datum) {
				varTempDatum = varAbfall2Datum
				varAbfall2Datum = varAbfall3Datum
				varAbfall3Datum = varTempDatum
				varTempArt = varAbfall2Art
				varAbfall2Art = varAbfall3Art
				varAbfall3Art = varTempArt
				varTempTage = varAbfall2Tage
				varAbfall2Tage = varAbfall3Tage
				varAbfall3Tage = varTempTage
				if (varAbfall3Datum > varAbfall4Datum) {
					varTempDatum = varAbfall3Datum
					varAbfall3Datum = varAbfall4Datum
					varAbfall4Datum = varTempDatum
					varTempArt = varAbfall3Art
					varAbfall3Art = varAbfall4Art
					varAbfall4Art = varTempArt
					varTempTage = varAbfall3Tage
					varAbfall3Tage = varAbfall4Tage
					varAbfall4Tage = varTempTage
				}
			}
		}
		if (varAbfall2Datum > varAbfall3Datum) {
				varTempDatum = varAbfall2Datum
				varAbfall2Datum = varAbfall3Datum
				varAbfall3Datum = varTempDatum
				varTempArt = varAbfall2Art
				varAbfall2Art = varAbfall3Art
				varAbfall3Art = varTempArt
				varTempTage = varAbfall2Tage
				varAbfall2Tage = varAbfall3Tage
				varAbfall3Tage = varTempTage
				if (varAbfall3Datum > varAbfall4Datum) {
					varTempDatum = varAbfall3Datum
					varAbfall3Datum = varAbfall4Datum
					varAbfall4Datum = varTempDatum
					varTempArt = varAbfall3Art
					varAbfall3Art = varAbfall4Art
					varAbfall4Art = varTempArt
					varTempTage = varAbfall3Tage
					varAbfall3Tage = varAbfall4Tage
					varAbfall4Tage = varTempTage
				}
			}
		if (varAbfall3Datum > varAbfall4Datum) {
					varTempDatum = varAbfall3Datum
					varAbfall3Datum = varAbfall4Datum
					varAbfall4Datum = varTempDatum
					varTempArt = varAbfall3Art
					varAbfall3Art = varAbfall4Art
					varAbfall4Art = varTempArt
					varTempTage = varAbfall3Tage
					varAbfall3Tage = varAbfall4Tage
					varAbfall4Tage = varTempTage
				}
	}
	Abfall_1_Datum.postUpdate(varAbfall1Datum.toString)
	Abfall_2_Datum.postUpdate(varAbfall2Datum.toString)
	Abfall_3_Datum.postUpdate(varAbfall3Datum.toString)
	Abfall_4_Datum.postUpdate(varAbfall4Datum.toString)
	Abfall_1_Art.postUpdate(varAbfall1Art)
	Abfall_2_Art.postUpdate(varAbfall2Art)
	Abfall_3_Art.postUpdate(varAbfall3Art)
	Abfall_4_Art.postUpdate(varAbfall4Art)
	Abfall_1_Tage.postUpdate(varAbfall1Tage)
	Abfall_2_Tage.postUpdate(varAbfall2Tage)
	Abfall_3_Tage.postUpdate(varAbfall3Tage)
	Abfall_4_Tage.postUpdate(varAbfall4Tage)
end

rule "Abfall steht an - Blinkeding"
when
	Time cron "0 00 17 1/1 * ? *"
then
	if (Abfall_1_Tage.state.toString == "morgen") {
	Abfall_Steht_An.postUpdate(ON)
	}
end

One of the breaking changes in OH3 is how datetime is handled. See:

Or search recent forum posts for ZonedDateTime.

1 Like

Hi JustinG,

thanks for your reply. i looked in some of this Post but could not get it to work.
should the lines below look something like this?

Sorry but im not the initial author of the rule and trying to port it to OH3, so my question may seems to be strange.

val DateTime varRestmuellDatum = new ZonedDateTime((Restmuell.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli).now.withHour(0).withMinute(0).withSecond(0)
	logInfo("all.rules", varRestmuellDatum)
	val Long varRestmuellDiff = Math::round((varRestmuellDatum.millis - now.withTimeAtStartOfDay.millis) / (86400000.0))

This is a pretty awkward way of getting the difference in days between two dates. I’m not 100% sure of the details here, but the necessary conversions between ZonedDateTime and LocalDate because of the use of withTimeAtStartOfDay (which I believe would be atStartOfDay here instead) will get long and complicated. You probably don’t need that part however and will still get the correct value (because of the math::round) with:

val varRestmuellDatum = Restmuell.getStateAs(DateTimeType).zonedDateTime.withHour(0).withMinute(0).withSecond(0)
logInfo("all.rules", varRestmuellDatum.toString())
val Long varRestmuellDiff = Math::round((varRestmuellDatum.toInstant().toEpochMilli() - ZonedDateTime.now().toInstant().toEpochMilli()) / (86400000.0))

However there are much more elegant ways of getting the difference between two timepoints. This post list some examples:

Note the import and use of the ChronoUnit to avoid the messy conversions to millis and back and rounding and the .until method to directly get the amount of time between two datetimes.

Thanks a lot to point me in that direction.
The ChronoUnit looks indeed much better.
I’ll look into it to implement that.

I played around with the chronounit and noticed if i compare now with a date of tomorrow it shows my a 0 as the difference. is that intended to be like that?
also for all outher dates it shows my 1 day less then i would expect.

like comparing now with a date of day after tomorrow would give me a 1

You have to be a bit careful.here.
If you have -
Some calculation X involving ‘now’ + 1 day
Some calculation Y involving ‘now’
remember that the 'now’s are different - the one used for Y is a couple of milliseconds later than X
The difference between X and Y will be 0 days … 23 hours 59 minutes etc.

ok, the date i compare now with has all time stamps set to 0 like this: 2021-04-30T00:00:00.000+0200
could i do that then with ‘now’ as well, to have the right datetime and the correct difference?

I don’t know what your calculations are, which is why I was suggesting possible causes, assuming you’d already thought about timezones. Calculate what you like; if you get a result you don’t expect, please share it and your calculation method.

you were right the problem was that my first date had the time set all to ‘0’ and ‘now’ had the time still in it.

i solved it now like this to calculate the difference between ‘now’ and the corresponding Trash collection Date. Seams to work correctly now.:

import java.time.temporal.ChronoUnit

rule "Tage bis Abfall berechnen"
when 
	Time cron "0 5 * 1/1 * ? *" or
	Item Restmuell changed or
	Item Biomuell changed or
	Item Papiermuell changed or
	Item GelberSack changed or
	Item DelayedStartup changed from OFF to ON
then
	
	// Global Settings
	logInfo("all.rules", "Tage bis Abfall berechnen")

	val varHeuteDatum = now.withHour(0).withMinute(0).withSecond(0).withNano(0)
    logInfo("all.rules", "varHeuteDatum: " + varHeuteDatum )


	//Restmuell
    val varRestmuellDatum = Restmuell.getStateAs(DateTimeType).zonedDateTime
	logInfo("all.rules", "varRestmuellDatum: " + varRestmuellDatum )

	val varRestmuellDiff = ChronoUnit.DAYS.between(now.withHour(0).withMinute(0).withSecond(0).withNano(0), (Restmuell.state as DateTimeType).getZonedDateTime)
    logInfo("all.rules", "Days between Restmuell und Heute : " + varRestmuellDiff)

	// val varRestmuellDatum = Restmuell.getStateAs(DateTimeType).zonedDateTime.withHour(0).withMinute(0).withSecond(0)
	// logInfo("all.rules", varRestmuellDatum.toString())
	// val Long varRestmuellDiff = Math::round((varRestmuellDatum.toInstant().toEpochMilli() - ZonedDateTime.now().toInstant().toEpochMilli()) / (86400000.0))	
	// logInfo("all.rules", "Days between Restmuell und Heute : " + varRestmuellDiff)


	if ( varRestmuellDiff == 0 ) {
		RestmuellTage.postUpdate("heute")
	} else if ( varRestmuellDiff == 1 ) {
		RestmuellTage.postUpdate("morgen")
	} else {
		RestmuellTage.postUpdate(String::format("(%1$d T.)", varRestmuellDiff))
	}

// 	//Biomuell
	val varBiomuellDatum = Biomuell.getStateAs(DateTimeType).zonedDateTime
	logInfo("all.rules", "varBiomuellDatum: " + varBiomuellDatum )

	val varBiomuellDiff = ChronoUnit.DAYS.between(now.withHour(0).withMinute(0).withSecond(0).withNano(0), (Biomuell.state as DateTimeType).getZonedDateTime)
    logInfo("all.rules", "Days between Biomuell und Heute : " + varBiomuellDiff)
	
	// val varBiomuellDatum = Biomuell.getStateAs(DateTimeType).zonedDateTime.withHour(0).withMinute(0).withSecond(0)
	// logInfo("all.rules", varBiomuellDatum.toString())
	// val Long varBiomuellDiff = Math::round((varBiomuellDatum.toInstant().toEpochMilli() - ZonedDateTime.now().toInstant().toEpochMilli()) / (86400000.0))	
	// logInfo("all.rules", "Days between Biomuell und Heute : " + varBiomuellDiff)

	if ( varBiomuellDiff == 0 ) {
		BiomuellTage.postUpdate("heute")
	} else if ( varBiomuellDiff == 1 ) {
		BiomuellTage.postUpdate("morgen")
	} else {
		BiomuellTage.postUpdate(String::format("(%1$d T.)", varBiomuellDiff))
	}

// 	//Papiermuell
	val varPapiermuellDatum = Papiermuell.getStateAs(DateTimeType).zonedDateTime
	logInfo("all.rules", "varPapiermuellDatum: " + varPapiermuellDatum )

	val varPapiermuellDiff = ChronoUnit.DAYS.between(now.withHour(0).withMinute(0).withSecond(0).withNano(0), (Papiermuell.state as DateTimeType).getZonedDateTime) + 1
    logInfo("all.rules", "Days between Papiermuell und Heute : " + varPapiermuellDiff)
	
	// val varPapiermuellDatum = Papiermuell.getStateAs(DateTimeType).zonedDateTime.withHour(0).withMinute(0).withSecond(0)
	// logInfo("all.rules", varPapiermuellDatum.toString())
	// val Long varPapiermuellDiff = Math::round((varPapiermuellDatum.toInstant().toEpochMilli() - ZonedDateTime.now().toInstant().toEpochMilli()) / (86400000.0))	
	// logInfo("all.rules", "Days between Papiermuell und Heute : " + varPapiermuellDiff)


	if ( varPapiermuellDiff == 0 ) {
		PapiermuellTage.postUpdate("heute")
	} else if ( varPapiermuellDiff == 1 ) {
		PapiermuellTage.postUpdate("morgen")
	} else {
		PapiermuellTage.postUpdate(String::format("(%1$d T.)", varPapiermuellDiff))
	}

// 	//GelberSack
	val varGelberSackDatum = GelberSack.getStateAs(DateTimeType).zonedDateTime
	logInfo("all.rules", "varGelberSackDatum: " + varGelberSackDatum )

	val varGelberSackDiff = ChronoUnit.DAYS.between(now.withHour(0).withMinute(0).withSecond(0).withNano(0), (GelberSack.state as DateTimeType).getZonedDateTime) + 1
    logInfo("all.rules", "Days between GelberSack und Heute : " + varGelberSackDiff)

	// val varGelberSackDatum = GelberSack.getStateAs(DateTimeType).zonedDateTime.withHour(0).withMinute(0).withSecond(0)
	// logInfo("all.rules", varGelberSackDatum.toString())
	// val Long varGelberSackDiff = Math::round((varGelberSackDatum.toInstant().toEpochMilli() - ZonedDateTime.now().toInstant().toEpochMilli()) / (86400000.0))	
	// logInfo("all.rules", "Days between GelberSack und Heute : " + varGelberSackDiff)


	if ( varGelberSackDiff == 0 ) {
		GelberSackTage.postUpdate("heute")
	} else if ( varGelberSackDiff == 1 ) {
		GelberSackTage.postUpdate("morgen")
	} else {
		GelberSackTage.postUpdate(String::format("(%1$d T.)", varGelberSackDiff))
	}	
end