[gelöst] Differenz zwischen zwei Daten in Tagen

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

Benutzeravatar
Florian.Reinartz
Beiträge: 103
Registriert: 11. Apr 2022 08:47
Wohnort: bei Schwerin

Re: Differenz zwischen zwei Daten in Tagen

Beitrag von Florian.Reinartz »

Moin,
habe es nun am laufen. :D
Vielen Dank für die Unterstützung.

Habe allerdings noch die Umrechnung der Milisekunden in Tage, Stunden und Sekunden angepasst.
Eine Minute sind 60000 Millisekunden und nicht 1000.

Code: Alles auswählen

rule "Beginn der Messung Tasmota 042"
    when
        Time cron "0 * * * * ?" or
        Item Tasmota042_Dunstabzug_Messbeginn received update or
        Item Tasmota042_Dunstabzug_Messbeginn changed
    then
        var dtStart  = new DateTime((Tasmota042_Dunstabzug_Messbeginn.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
        val Diff     = now.millis - dtStart.millis
        val iDays    = (Diff / 60000 / 60 / 24).intValue                                      // volle Tage
        val iHours   = (Diff / 60000 / 60).intValue - iDays * 24                          // RestStunden
        val iMinutes = (Diff / 60000).intValue - iHours * 60 - iDays * 24 * 60     // RestMinuten
        Tasmota042_Dunstabzug_Messdauer.postUpdate(iDays)
end
Was ist denn erforderlich damit iDays als Dezimalzahl ausgegeben werden?
openHAB 4.1.0 (64 bit) auf einem Raspberry Pi 5 Model B Rev 1.0 mit 8GB

Benutzeravatar
udo1toni
Beiträge: 13860
Registriert: 11. Apr 2018 18:05
Answers: 222
Wohnort: Darmstadt

Re: Differenz zwischen zwei Daten in Tagen

Beitrag von udo1toni »

Ja, ich hatte Millisekunden / 1000 / 60 / 60 / 24 gerechnet. Beim Hinschreiben habe ich dann einmal das /60 vergessen... (Hab's oben angepasst)
iHours und iMinutes habe ich nur angefügt, damit Du siehst, wie Du an diese Werte ran kommst. In der Rule werden die beiden Konstanten momentan nicht genutzt.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

Benutzeravatar
Florian.Reinartz
Beiträge: 103
Registriert: 11. Apr 2022 08:47
Wohnort: bei Schwerin

Re: Differenz zwischen zwei Daten in Tagen

Beitrag von Florian.Reinartz »

hat prima funktioniert.
Wenn es erst mal läuft ist es eigentlich ganz einfach ;-)

Kann man dann auch iDays als Dezimalzahl ausgegeben?
Also z.B. 9,8 Std. ?
openHAB 4.1.0 (64 bit) auf einem Raspberry Pi 5 Model B Rev 1.0 mit 8GB

Benutzeravatar
udo1toni
Beiträge: 13860
Registriert: 11. Apr 2018 18:05
Answers: 222
Wohnort: Darmstadt

Re: Differenz zwischen zwei Daten in Tagen

Beitrag von udo1toni »

Na ja, Es reicht ja, die Millisekunden durch 1000 / 60 / 60 / 24 zu teilen, aber als Float oder Double zu speichern. Natürlich sind Tagesbruchteile nicht sonderlich intuitiv. Du kannst den Zahlenwert auch gemischt angeben, dazu muss das Item natürlich vom Typ String sein.
Ich habe so eine Funktion als JS für Minuten und Sekunden geschrieben und hier mal für Tage, Stunden, Minuten und Sekunden angepasst (ungetestet, müsste aber so hinkommen). Input wäre die Dauer in Millisekunden. Man könnte das Script in einer Datei /etc/openhab/transform/ms2dhms.js abspeichern (für "Millisekunden Zu Tage Stunden Minuten Sekunden")

Code: Alles auswählen

(function(seconds){
    seconds = Math.floor(seconds / 1000);
    var days = Math.floor(seconds / 60 / 60 / 24);
    seconds = seconds % (60 * 60 * 24);
    var hours = Math.floor(seconds / 60 / 60);
    seconds = seconds % (60 * 60);
    var minutes = Math.floor(seconds / 60);
    seconds = seconds % 60;
    var retval  = days + "t";
    if (hours < 10)
        retval = retval + "0";
    retval = retval + hours + "h";
    if (minutes < 10)
        retval = retval + "0";
    retval = retval + minutes + "'";
    if (seconds < 10)
        retval = retval + "0";
    retval = retval + seconds + "\"";
    return retval;
})(input)
Der Eingangswert wird in eine Variable seconds übernommen (zu dem Zeitpunkt sind es allerdings noch Millisekunden).
Erste Amtshandlung ist also, aus den Millisekunden Sekunden zu machen. Math.floor() schneidet die Nachkommastellen ab.
Die Variable days wird mit dem Ganzzahlanteil der Sekunden durch die Anzahl Sekunden pro Tag belegt. Anschließend wird eine Modulo Operation ausgeführt, die nur den Rest übrig lässt.
Das gleiche passiert für den Stundenanteil und Minutenanteil. Übrig bleiben zum Schluss die Sekunden.
Danach wird der String zusammengesetzt, wobei das Trennzeichen t Tage von Stunden trennt, h trennt Stunden von Minuten, das ' trennt Minuten von Sekunden und " schließt die Sekunden ab. (analog zu den Winkelrgaden). Falls Stunden, Minuten und/oder Sekunden einstellig sind, wird jeweils eine führende 0 ergänzt.
Die Rule sähe dann so aus:

Code: Alles auswählen

rule "Beginn der Messung Tasmota 042"
when
    Time cron "0 * * * * ?" or
    Item Tasmota042_Dunstabzug_Messbeginn changed
then
    var dtStart  = new DateTime((Tasmota042_Dunstabzug_Messbeginn.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
    val Diff     = now.millis - dtStart.millis
    val strDur = transform("JS","ms2dhms.js",Diff)
    Tasmota042_Dunstabzug_Messdauer.postUpdate(strDur)
end
Es wird also lediglich die Anzahl Millisekunden berechnet und dieser Wert anschließend dem JavaScript Script übergeben. Als Rückgabewert wird der String zum Schluss in das String Item geschrieben.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

Benutzeravatar
Florian.Reinartz
Beiträge: 103
Registriert: 11. Apr 2022 08:47
Wohnort: bei Schwerin

Re: Differenz zwischen zwei Daten in Tagen

Beitrag von Florian.Reinartz »

Moin Zusammen,

die Lösung von Udo1Tone hat super funktioniert bis ich auf OH3 gewechselt bin.
Kann mir bitte noch mal jemand auf die Sprünge helfen...

Code: Alles auswählen

rule "Sauna-Ofen Heizphase an"
    when
        //Time cron "*/30 * * * * ?" or
        Item Schmiede_SaunaOfen changed to ON
    then
        SaunaOfen_Time_An.postUpdate(new DateTimeType().zonedDateTime.toInstant.toEpochMilli)
end
-> AUSGABE: 2022-08-11T11:29:58.352+0000 - also OK

Bei der Weiterverarbeitung hänt es dann...

Code: Alles auswählen

rule "Sauna-Ofen Heizphase dauer"
    when
        Time cron "*/10 * * * * ?"
    then
        if(Schmiede_SaunaOfen.state == ON)
        {
            // Heizphase
            var dtStart  = new DateTime((SaunaOfen_Time_An.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
            val Diff     = now.millis - dtStart.millis
            val iMinutes = (Diff / 1000 / 60).intValue
            SaunaOfen_Laufzeit_Aktuell.postUpdate(iMinutes)
            SaunaOfen_Laufzeit_Gesamt.postUpdate((SaunaOfen_Laufzeit_Heizphase.state as DecimalType) + (SaunaOfen_Laufzeit_Aktuell.state as DecimalType))
        }
end
new DateTime und now.millis sind das Problem.
Ich weiß, dass new DateTime in OH3 so nicht mehr funktioniert aber alle anderen Varianten, von denen ich dachte so läuft es auf OH3 funtkionierten auch nicht ...


Danke und Gruß
openHAB 4.1.0 (64 bit) auf einem Raspberry Pi 5 Model B Rev 1.0 mit 8GB

Benutzeravatar
udo1toni
Beiträge: 13860
Registriert: 11. Apr 2018 18:05
Answers: 222
Wohnort: Darmstadt

Re: Differenz zwischen zwei Daten in Tagen

Beitrag von udo1toni »

Genau. openHAB2 hat noch Joda Time verwendet, openHAB3 nutzt JavaTime. Die Methoden, die Zeit betreffend haben leicht andere Namen und sind teilweise leicht anders anzusteuern.

Du hast ein DateTime Item, welches in die lokale Variable (es reicht auch eine lokale Konstante) überführt werden soll.

Code: Alles auswählen

val dtStart = (SaunaOfen_Time_An.state as DateTimeType).getZonedDateTime()
Du möchtest allerdings die Millisekunden.

Code: Alles auswählen

val lStart = (SaunaOfen_Time_An.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli()
Die aktuelle Zeit (ebenfalls in Milllisekunden)

Code: Alles auswählen

val lNow = now.toInstant.toEpochMilli()
Und die Differenz wäre dann lNow - lStart

Der Trigger der Rule ist vermutlich der Tatsache geschuldet, dass Du auf der einen Seite eine Aktualisierung der Anzeige möchtest, aber auf der anderen Seite eine ständig triggernde Rule vermeiden willst.

Besser:

Code: Alles auswählen

var Timer tSauna = null                                       // globale Variable für Timer
var Long lSauna = 0                                           // globale Variable für Zeitstempel in Millisekunden

rule "Sauna-Ofen Heizphase"
when
    Item Schmiede_SaunaOfen changed
then
    tSauna?.cancel                                            // Falls Timer existiert: abbrechen

    if(newState == ON) {                                      // Ofen gestartet

        lSauna = now.toInstant.toEpochMilli                   // Zeitstempel merken

        tSauna = createTimer(now.plusMinutes(1), [            // Timer starten, nach einer Minute
            val lNow     = now.toInstant.toEpochMilli         // Zeitstempel merken
            val iMinutes = ((lNow - lSauna)/60000).intValue   // Differenz in vollen Minuten
            SaunaOfen_Laufzeit_Aktuell.postUpdate(iMinutes)   // ins Item
            SaunaOfen_Laufzeit_Gesamt.postUpdate((SaunaOfen_Laufzeit_Heizphase.state as Number) + iMinutes) // und auf das andere Item addieren
            tSauna.reschedule(now.plusMinutes(1))             // Timer erneut starten
        ])

    } else {                                                  // Ofen gestoppt

        val lNow     = now.toInstant.toEpochMilli             // gleiches Spiel wie im Timer Code...
        val iMinutes = ((lNow - lSauna)/60000).intValue       // nur ohne erneute Ausführung
        SaunaOfen_Laufzeit_Aktuell.postUpdate(iMinutes)
        SaunaOfen_Laufzeit_Gesamt.postUpdate((SaunaOfen_Laufzeit_Heizphase.state as Number) + iMinutes)
    }
end
Nur eine Rule. Leider lässt sich die Codedoppelung nicht elegant lösen und ist der Tatsache geschuldet, dass sonst die letzte Minute (obwohl vielleicht schon mehr als die Hälfte rum ist) nicht gezählt wird. Je nachdem, wie SaunaOfen_Laufzeit_Gesamt und SaunaOfen_Laufzeit_Heizphase definiert sind, könnte man auch auf die Heizphase in der Rule verzichten, und einfach eins aufsummieren:

Code: Alles auswählen

        SaunaOfen_Laufzeit_Gesamt.postUpdate((SaunaOfen_Laufzeit_Gesamt.state as Number) + 1)
weil die Rule ja minütlich aufgerufen wird.

Im Grunde könnte man sogar komplett auf die ganze Kalkulation verzichten und einfach immer nur 1 zum aktuellen Wert addieren, nur beim Stoppen des Ofens müsste man berechnen, ob zur vollen Betriebsminute mehr oder weniger als 30 Sekunden fehlen.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

Benutzeravatar
Florian.Reinartz
Beiträge: 103
Registriert: 11. Apr 2022 08:47
Wohnort: bei Schwerin

Re: Differenz zwischen zwei Daten in Tagen

Beitrag von Florian.Reinartz »

Wie immer, muss ich mich für die ausfürliche Hilfe bedanken.
Ich habe jetzt versucht mit meinem - erneut erweiterten - Wissen folgendes Problem nach dem Wechsel von OH2.5 auf OH3 zu lösen.
Auch unter Verwendung der Suchbegriffe JavaTine...
Ich weiß nicht wo man sich mal ausführlich belesen kann...

Es geht um die Urlaubssteuerung meiner Heizung:

Code: Alles auswählen

        // ------------------- Start-Datum hm1 --------------------------
        // Time-String von Buderus YYYY-MM-DD/YYY-MM-DD aufsplitten in Start YYYY-MM-DD
        var String hm1TimeSplitStart       = Buderus_hm1_StartStop.state.toString.split('/').get(0)
        // Time-String von Start YYYY-MM-DD umwandeln in DD.MM.YYYY
        var String Buderus_hm1_Start_Day   = hm1TimeSplitStart.toString.split('-').get(2)
        var String Buderus_hm1_Start_Month = hm1TimeSplitStart.toString.split('-').get(1)
        var String Buderus_hm1_Start_Year  = hm1TimeSplitStart.toString.split('-').get(0)
        Buderus_hm1_Start_Datum.postUpdate(Buderus_hm1_Start_Day.toString + "." + Buderus_hm1_Start_Month.toString + "." + Buderus_hm1_Start_Year.toString)

        var DateTime JDT_time_hm1_Start = parse(hm1TimeSplitStart.toString + "T00:00:00+00:00")     // Parse JDT object from timestring
        var DateTimeType DTT_time_hm1_Start = new DateTimeType(JDT_time_hm1_Start.toString)         // Convert JodaDateTime to DateTimeType 
        Buderus_hm1_Start_DT.postUpdate(DTT_time_hm1_Start)                                         // CarTripStart item changed to TestTime for the purpose of this test


        // ------------------- Stop-Datum hm1 --------------------------
        // Time-String von Buderus YYYY-MM-DD/YYY-MM-DD aufsplitten in Stop YYYY-MM-DD
        val hm1TimeSplitStop       = Buderus_hm1_StartStop.state.toString.split('/').get(1)
        logInfo("Buderus01aufsplitten", "Buderus hm1TimeSplitStart {}", hm1TimeSplitStart)
        // Time-String von Start YYYY-MM-DD umwandeln in DD.MM.YYYY
        var Buderus_hm1_Stop_Day   = hm1TimeSplitStop.toString.split('-').get(2)
        logInfo("Buderus01Tag",   "Buderus Buderus_hm1_Start_Day {}",   Buderus_hm1_Start_Day)
        var Buderus_hm1_Stop_Month = hm1TimeSplitStop.toString.split('-').get(1)
        logInfo("Buderus01Monat", "Buderus Buderus_hm1_Start_Month {}", Buderus_hm1_Start_Month)
        var Buderus_hm1_Stop_Year  = hm1TimeSplitStop.toString.split('-').get(0)
        logInfo("Buderus01Year",  "Buderus Buderus_hm1_Start_Year {}",  Buderus_hm1_Start_Year)
        Buderus_hm1_Stop_Datum.postUpdate(Buderus_hm1_Stop_Day + "." + Buderus_hm1_Stop_Month + "." + Buderus_hm1_Stop_Year)
        var DateTime JDT_time_hm1_Stop = parse(hm1TimeSplitStop.toString + "T00:00:00+00:00")       // Parse JDT object from timestring
        var DateTimeType DTT_time_hm1_Stop = new DateTimeType(JDT_time_hm1_Stop.toString)           // Convert JodaDateTime to DateTimeType 
        Buderus_hm1_Stop_DT.postUpdate(DTT_time_hm1_Stop)                                           // CarTripStart item changed to TestTime for the purpose of this test

       
        // ------------------- Abfrage ob Urlaub jetzt --------------------------
        if (new Interval(new DateTime(Buderus_hm1_Start_DT.state.toString),new DateTime(Buderus_hm1_Stop_DT.state.toString)).contains(now))
            {
                sendCommand(Buderus_hm1_Urlaub_aktiv,ON)
                sendCommand(Heizung_Schmiede_Arbeit_Urlaub_Verreist,2.0)
            }
        else
            {
                sendCommand(Buderus_hm1_Urlaub_aktiv,OFF)
            }
Bemängelt werden von VS Code (explizit: rot markiert):
var DateTime JDT_time_hm1_Start = parse(hm1TimeSplitStart.toString + "T00:00:00+00:00")
demnach natürlich auch:
var DateTime JDT_time_hm1_Stop = parse(hm1TimeSplitStop.toString + "T00:00:00+00:00")
und
new Interval(new DateTime(Buderus_hm1_Start_DT.state.toString),new DateTime(Buderus_hm1_Stop_DT.state.toString)).contains(now)

Ich habe das Gefühlt, alles was ich mir in den letzten Jahen aufgebaut habe funktioniert nicht mher...
Kann mir bitte noch mal Jemand eine Hilfestellung geben.
Schön wäre auch eine Informationsquelle...
openHAB 4.1.0 (64 bit) auf einem Raspberry Pi 5 Model B Rev 1.0 mit 8GB

Benutzeravatar
udo1toni
Beiträge: 13860
Registriert: 11. Apr 2018 18:05
Answers: 222
Wohnort: Darmstadt

Re: Differenz zwischen zwei Daten in Tagen

Beitrag von udo1toni »

Also für mich sieht das alles (schon seit immer) unnötig kompliziert aus.
Was sind die Ausgangsdaten (und in welchem Format werden sie von wo angeliefert)?
Was soll mit den Daten passieren (und warum)?

Hast Du mehrere gleichartige Daten (hm1, hm2, hm3...)?
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

Benutzeravatar
Florian.Reinartz
Beiträge: 103
Registriert: 11. Apr 2022 08:47
Wohnort: bei Schwerin

Re: Differenz zwischen zwei Daten in Tagen

Beitrag von Florian.Reinartz »

Moin Zusammen,
also...
Es geht um die Urlaubszeiten in denen die Therme automatisch aus ist bzw. abgesenkt arbeitet.
Es gibt 5 Urlaubszeiträume die definiert werden können.
hm1, hm2 ... hm5
Ist ein Timer angelegt so liefert mir die Therme:

Buderus_hm1_StartStop           2022-07-15/2022-07-28

Num möchte ich Start und Ende getrennt voneinander in dieses Format bringen:

Buderus_hm1_Start_DT            2022-07-15T00:00:00+0000
Buderus_hm1_Stop_DT             2022-07-28T00:00:00+0000
openHAB 4.1.0 (64 bit) auf einem Raspberry Pi 5 Model B Rev 1.0 mit 8GB

Benutzeravatar
udo1toni
Beiträge: 13860
Registriert: 11. Apr 2018 18:05
Answers: 222
Wohnort: Darmstadt

Re: Differenz zwischen zwei Daten in Tagen

Beitrag von udo1toni »

Dann sollte das ausreichen:

Code: Alles auswählen

val hm1TimeSplitStart           = Buderus_hm1_StartStop.state.toString.split('/').get(0)+"T00:00:00.000Z"
val hm1TimeSplitStop            = Buderus_hm1_StartStop.state.toString.split('/').get(1)+"T00:00:00.000Z"
val DateTimeType DTT_hm1_Start  = DateTimeType.valueOf(hm1TimeSplitStart)
val DateTimeType DTT_hm1_Stop   = DateTimeType.valueOf(hm1TimeSplitStop)
Buderus_hm1_Start_DT.postUpdate(DTT_hm1_Start)
Buderus_hm1_Stop_DT.postUpdate(DTT_hm1_Stop)
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

Antworten